本文积累了作者在准备面试时学习的计算机网络的知识与问题,作为科班出身,408 这些科目的重要性不必多说,能直接检验出你作为科班选手的水准。

1. 键入网址后依次发生了什么?

参考链接:https://mp.weixin.qq.com/s/I6BLwbIpfGEJnxjDcPXc1A

  • 浏览器解析 URL 生成 Http 请求。
  • 发送 Http 请求前,会将域名解析为 IP 地址,会先查询浏览器缓存、系统缓存、本机 hosts 文件,如果没有,就会发送请求到本地域名服务器,通过本地域名服务器分别访问根域名服务器、顶级域名服务器、权威域名服务器,最终拿到域名映射的 IP 地址,并且缓存起来。
  • 建立 TCP 连接,三次握手。三次握手是为了双方确定对方具有发送和接收数据的能力,所以两次握手不行
  • 增加 TCP 头部。如果 Http 数据太长,超过了 MSS(网络层数据大小),就需要将其切分再每个加上 TCP 头部。TCP 头部包含了源端口,目的端口,校验和,序号等信息,最后交给 IP 模块处理。
  • 加上 IP 头部。IP 头部包含了源地址和目的地址,如果主机有多个网卡,会根据路由表规则来选择网卡,其实就是目的地址与网卡的掩码做与运算从而来选择,如果都不匹配,就会走默认网卡 0.0.0.0。
  • 加上 MAC 头部。通过 ARP 协议来获取目的地址的 MAC 地址,如果 ARP 缓存有,就直接用,如果没有,就在以太网中广播目的地址从而得到响应拿到对应的 MAC 地址。
  • 将给网卡,将包转为电信号,通过网线发送出去
  • 交换机拿到包,通过 MAC 表将数据从对应端口发送出去,如果表中没有对应映射,就对局域网所有主机发送。
  • 路由器拿到包并根据 IP 地址进行转发。MAC 头部作用就是将包送到路由器,然后 MAC 头部就会被丢弃。路由器根据路由表决定下一跳(下一跳的 IP)。通过 ARP 协议拿到下一跳的 MAC 地址,重新发送。整体传输过程只有 MAC 地址在不断变化,因为包需要不断在以太网中传输。
  • 服务器收到请求,回复 ACK 和 Http 响应。浏览器得到响应,再请求 html 中的 js,css 资源。浏览器再解析渲染,呈现网页。

sessioncookie 的区别

  • Cookie 是保存在客户端的一小块文本串的数据。客户端向服务器发起请求时,服务端会向客户端发送一个 Cookie,客户端就把 Cookie 保存起来。在客户端下次向同一服务器再发起请求时,Cookie 被携带发送到服务器。服务器就是根据这个 Cookie 来确认身份的。
  • session 指的就是服务器和客户端一次会话的过程。Session 利用 Cookie 进行信息处理的,当用户首先进行了请求后,服务端就在用户浏览器上创建了一个 Cookie,当这个 Session 结束时,其实就是意味着这个 Cookie 就过期了。Session 对象存储着特定用户会话所需的属性及配置信息。

需要注意的是,浏览器第一次请求服务器时,服务器创建了 session,会给浏览器返回 sessionId,这也是放在 cookie 里面的,cookie 记录此 SessionId 是属于哪个域名,之后浏览器的请求会自动判断此域名下是否存在 Cookie 信息,如果存在,则自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到,说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。

# Http 协议

1. 谈谈 http 协议

参考链接:https://mp.weixin.qq.com/s/AK1Pb9rx0q5Hf8dq6HNOhw

http 协议是超文本传输协议,一开始用于传输 html、css、js 等资源文件,后来也可以传输图片、视频、音频等。http1.0 是无状态的,每个 http 请求都必须重新建立 TCP 连接,这导致开销较大。http1.1 通过 Cookie 来管理状态,同时实现了持久化连接。

http 请求由:请求行、消息头、数据组成。请求行包括请求方法、URL、协议版本。

请求方法包括 GET(表单)、POST(实体)、DELETE(删除文件)、PUT(文件)、TRACE 等组成。

状态码含义分别是:

  • 1xx:表示请求正在处理,是信息性状态码
  • 2xx:表示请求成功处理
  • 3xx:表示重定向
  • 4xx:表示客户端错误,请求不合法
  • 5xx:表示服务器错误,不能处理合法请求

2. GETPOST 区别

  • GET 的语义是从服务器获取指定的资源,请求的参数写在 URL 中,浏览器一般对 URL 的长度有一定的限制。 GET 方法是 ** 安全、幂等(多次请求结果不变)** 的。请求的结果会被缓存。
  • POST 的语义是根据报文 body 对指定的资源做出修改POST 请求携带的数据一般是写在报文 body 中。 POST 方法不是安全、幂等的。请求的结果不会被主动缓存。

但是实际开发中,很多人并没有遵循 RFC 语义来实现 GETPOST

长连接与短连接

http1.0 默认是短链接,但是可以设置头部的 connection 字段为 keep-alive 强制开启长连接。到了 http1.1 就是默认长连接了。 keep-alive 也有三个参数: tcp_keepalive_timetcp_keepalive_intvltcp_keepalive_probes :当 TCP 连接之后,闲置了 tcp_keepalive_time ,则会发生侦测包,如果没有收到对方的 ACK,那么会每隔 tcp_keepalive_intvl 再发一次,直到发送了 tcp_keepalive_probes ,就会丢弃该连接。

3. Http 缓存技术

http 的缓存技术分为强制缓存协商缓存

  • 强制缓存:浏览器判断缓存没有过期,就直接使用缓存,是通过响应头部的两个字段实现的。 Cache_Controller 是相对时间; Expire 是一个绝对时间,如果两个字段同时存在于头部,那么 Cache_Controller 的优先级更高。以 Cache_Controller 为例,浏览器第一次访问资源,服务器返回资源的同时在头部加上 Cache_Contorller
  • 协商缓存:有时响应返回的状态码为 304 ,其实就是服务端告知客户端是否可以使用缓存,这就是协商缓存。

4. Http 各版本之间的特性

  • http1.0 :短连接, TCP 连接只处理一个请求,处理完就关闭连接。引入了 POSTHEAD
  • http1.1 :通过 Cookie 维护长连接,同时引入了 PUTDELETEPATCH 等动词。使用管道可以同时发送多个请求,减少整体响应时间(管道解决了请求的队头阻塞,但是没有解决响应的队头阻塞,而且管道这个功能默认其实是不开启的,而且浏览器基本没有支持)。
  • http2.0 :针对 http 报文做出优化,如压缩首部,采用二进制报文格式,对计算机更友好。因为 http1.1 默认不开启管道,浏览器也不支持, http2.0 引入了 Stream 概念,多个 Stream 可以复用一个 TCP 连接。不同的 http 请求可以使用唯一的 Stream ID 来区分,接收端通过 Stream ID 组装 http 消息。
  • http3.0 :使用了基于 UDP 实现的 QUIC 协议, http2.0 的缺点在于如果某一个 Streamtcp 层有数据丢失,那么所有排在该 Stream 后面的数据都要等待重传,应用层无法获取。 http3.0 的每一个 Stream 并不互相依赖,某一个 Stream 数据丢失之后阻塞自己这个 Stream 。其他特性还有更快的建立连接, QUIC 包含了 TLS ,只需要三次握手即可建立连接与密钥协商,而 TCP 三次握手之后还需要额外的 TLS 握手。由于 QUIC 只是维护连接 ID ,所以哪怕连接的主机 IP 变了(wifi 变流量),只要连接 ID 和上下文信息还在就不需要重新建立连接( TCP 的唯一标识是四元组,这种情况需要断开原来的连接,建立新的连接)。

5. 说说如何优化 http1.1 协议?

优化 http 协议的思路:

  • 减少 http 请求次数:
    • 使用缓存,将请求和响应的数据保存到本地磁盘,并设置过期时间。
    • 减少重定向次数,一般客户端和浏览器之间还会设置代理服务器,如果有资源的位置变了需要重定向,重定向的工作交给代理服务器做就可以减少 http 请求。
    • 合并请求和懒加载。
  • 减少 http 响应数据的大小:对数据使用无损压缩(谷歌的 br 压缩)或者有损压缩(谷歌的 WebP 格式)。

3. 谈谈 https 协议

参考链接:https://mp.weixin.qq.com/s/21JaXwdfSjItj5SgOwhapg

数据传输使用的仍然是 http 协议,只不过使用了 SSL 对数据进行了加密,保证了数据传输的安全。

其过程为:

  • 客户端发送 https 请求,服务端响应 CA 证书和公钥。(需要注意,是证书里面附带了公钥
  • 客户端校验 CA 证书合法性,生成随机密钥 key,并使用公钥对 key 加密,再发送给服务端。
  • 服务端收到后,使用私钥对可 key 解密,拿到真正的 key,双方之后的数据传输就用该 key 进行对称加密传输

上述过程,如果没有 CA 证书,那么中间人攻击可以这样:在第一步将公钥换成自己的公钥 1,这样在第二步对客户端产生的 key 使用自己的私钥 1 解密从而拿到 key。最后再将 key 使用最开始服务端发送的公钥进行加密发送给服务端,这样 MITM(中间人攻击)照样可以完成。

所以需要第三方的公信认证,CA 机构有一对公钥 / 私钥,公钥是对外界公开的,而私钥必须严格保密。当服务端将 CA 证书 + 服务端公钥发送给客户端时, 会先将证书的数据(包括服务端公钥)进行哈希,得到哈希值 H,再用私钥(CA 机构的私钥)将 H 加密,最后客户端(浏览器)拿到证书后,会使用系统浏览器内置的 CA 公钥对 H 进行解密得到 H,再自己通过证书指定的哈希算法对数据进行哈希得到 H',比较 H==H'

私钥和公钥可以互相加密解密,非不是私钥只能解密。上述过程,假设有中间人换了证书中的服务端公钥,也会因为不知道私钥而无法加密哈希值导致客户端会检测出来。所以 CA 机构应该严格保密私钥,如果泄露,就会失去公信力。

如果整个 https 通信全部用非对称加密确实可以,双方各拿一对公钥 / 私钥,然后交换公钥进行通信。至于为什么本质还是要使用对称加密,是因为非对称加密太耗时间了,仅用于传输 key 即可。

# ICMP

4. 用过 ping 吗,说说原理

参考链接:https://mp.weixin.qq.com/s/3KF0IxLum8EOtcF0ZNIiPA

ping 实现原理依托于 ICMP 协议。该协议是互联网控制报文协议,用于报告网络错误、传送报文运输情况等。 ICMP 报文被封装到 IP 数据包里面。ping 使用的两种 ICMP 数据包是回送请求回送响应

假设主机 A 对主机 B 进行了 ping 操作,那么主机 A 会封装 ** ICMP 回送请求 **,此时会记录请求产生的时间,并将其封装到 IP 数据包中,再加上 MAC 头部,最后发送出去。没有缓存目的 MAC 地址,先通过 ARP 协议获取。

主机 B 收到报文后,逐步拆除 MAC 和 IP 头部,经过地址检验后,将有用的信息提取交给 ICMP 协议,再发送 ** ICMP 回送响应 **。主机 A 收到回送响应后,用当前时间减去 ICMP 数据包发送时间,就可以得到 RTT

同时, ICMP 还维护了一个 TTL ,每次数据包经过一个路由器,就会 - 1,直到为 0 被丢弃,TTL 就可以检测出两个主机之间经过多少跳。

tracert 也是借助 ICMP 协议实现的。

# TCP/IP

聊聊 TCP 三次握手和四次挥手

参考链接:https://mp.weixin.qq.com/s/rX3A_FA19n4pI9HicIEsXg

TCP 三次握手

  • 确认双方都有发送和接收数据的能力。

  • 防止旧连接覆盖新连接。客户端知道自己此时应该建立哪个连接,但是网络传输过程复杂,很可能旧的连接 SYN 比新的 SYN 后到,到底建立哪个连接服务端是不知道的,所以必须有第三次握手,让客户端确认到底建立哪个连接。

  • 防止浪费资源。两次握手中,服务端不知道自己的 ACK+SYN 是否被客户端收到,这会导致重复发送 SYN+ACK ,建立很多个无用的连接。

  • 同步初始化序列号。同步序列号能够防止接收端接收的数据乱序。

总的来说,第三次握手是必要的,必须由客户端确认建立连接的各种状态信息的正确性。值得一提的是,第三次握手,发送方可以顺带发送数据。

TCP 四次挥手

重点:主动放弃连接的一方会进入 Time_Wait 状态,在 Linux 中,会等待 2MSL(60 秒)。

四次挥手的过程为(假设客户端主动断开连接):

  1. 客户端发送 Fin 表示自己断开连接,不再发送数据,但是可以接收数据,进入 FIN_WAIT_1 状态。
  2. 服务端收到 Fin ,发送 ACK ,表示自己收到断开请求,需要处理剩下的数据,进入 CLOSED_WAIT
  3. 服务端处理完数据,发送 Fin ,进入 LAST_ACK 状态。
  4. 客户端收到 Fin ,发送 ACK ,进入 TIME_WAIT 状态,等待 2MSL ,最后进入 CLOSED 状态。

正是因为服务端需要处理剩下数据,所以是四次挥手,同样,如果省去最后一次挥手,那么服务端就会一直处于 LAST_ACK 状态,当客户端想建立新的连接,发送 SYN ,服务端就会回复 RST ,建立连接的过程会终止。

为什么四次挥手中要有 TIME_WAIT 状态以及为什么要等 2MSL?

参考链接:https://mp.weixin.qq.com/s/rX3A_FA19n4pI9HicIEsXg

我们这里默认主动断开连接的是客户端。首先,主动断开连接的那一方才会进入 TIME_WAIT ,这个时间是 2MSL,在 Linux 中为 60s,而且这个时间是固定的,也就是在内核代码中写死了,无法修改。

TIME_WAIT 的出现能够保证被动断开连接方(服务端)可以正常的关闭,从 LAST_ACK 进入 CLOSED 状态。

MSL 是数据包在网络传输中存活的最长时间, TIME_WAIT 设置为 2MSL,比较合理的解释为:如果服务端没有收到 ACK ,超时重传 FIN 后再接收 ACK 的时间在 2MSL 之内。当客户端重新接收到 FIN 时,会重置 2MSL 时间。同时网络连接中的旧数据包在 2MSL 中能够被清理干净,如果客户端当前端口重新建立连接,不会有旧的数据传到当前端口,造成数据混乱。

TIME_WAIT 出现的原因为:

  • 保证服务端正常关闭
  • 防止旧的四元组数据包影响下一次连接传输。

正因为主动断开会进入 TIME_WAIT ,此时既会白白占用端口,又会无法传输数据,经历时间还非常长,对于服务端来说是很大的负担,所以这个烂摊子尽量交给对方,尽量让对方断开连接。

解决 TIME_WAIT 方法:

  • 使用 tcp_rw_reuse + tcp_timestamp :这样可以使得处于 TIME_WAIT 套接字复用,因为开启了时间戳,新的连接不会接收时间戳过期的数据。
  • 其他方法不推荐使用。

知道 SYN 攻击吗,说说你知道的防御手段

参考链接:https://info.support.huawei.com/info-finder/encyclopedia/zh/SYN+Flood.html

SYN 攻击是 DDos 攻击的一种,通过程序不断发送 SYN 迅速占满服务端的 SYN 队列,使其崩溃的攻击手段。

防御手段:

  • 首包丢弃:大多数 SYN 攻击都是变源的,这使得在 SYN Flood 攻击中,每个 SYN 都是首包,Anti-DDos 系统可以丢弃收到的 SYN 首包,如果对方客户端是正常的,那么基于 TCP 超时机制,一定会重传,此时 SYN 就不是首包了,可以对其进行源认证。

  • 源认证:Anti-DDos 系统部署在网络入口,先代替服务端发送 SYN+ACK,如果收到了客户端的 ACK,就将其 IP 加入白名单,之后一段时间都不会代替服务端对该 IP 的 SYN 进行拦截。

源认证必须配合首包丢弃使用,不然性能瓶颈也只是从服务器转移到了 Anti-DDos 系统中。

  • 设置 TCP 参数也可以一定程度上防御 SYN 攻击,比如扩大半连接队列,开启 syncookies

TCP 的半连接队列和全连接队列了解吗?如果队列满了怎么办?

参考链接:https://mp.weixin.qq.com/s/tRXlq1hErqKQLMMLcxoXvg

半连接队列是指 SYN 队列,服务端收到 SYN 请求,就会将其加入到 SYN 队列;全连接队列是指 Accept 队列,当服务端收到客户端的 ACK 就会将 SYN 队列对应节点放到 Accept 队列中。当队列满了,Linux 默认的操作是拒绝再接收 ACK。因为队列装不下了,但是有个问题就是,客户端发送了 ACK 就会进入 ESTABLISHED 状态,但是实际上服务端却没有接收。

Linux 中变量 tcp_abort_on_overflow 为 0,就是丢掉客户端发送的数据,为 1 就会发送一个 reset 包给客户端。

所以全连接队列满了,一般解决方法就是扩大队列长度,Accept 队列长度由两个变量决定,结果式为 len = min(backlog, somaxconn)

半连接队列长度 max_qlen_log 取决于全连接队列长度 len 、变量 max_syn_backlogmax_qlen_log = 2 * min(len, max_syn_backlog)

半连接队列一般不会满,当队列中剩余长度达到某个特定值时(和 max_syn_backlog 有关,但是不同 Linux 版本计算方法可能不同),就不会再接收 SYN 了。其实当全连接队列满了,不论半连接队列如何,都不会再接收 SYN 了。

半连接队列满了(假设遇到了 SYN 攻击),策略有三个:

  • 增大半连接队列长度,也就是增大那三个参数。
  • 打开 syncookies ,将该变量设置为 1 即可(0-- 关闭,1-- 队列满了打开 syncookies ,2-- 直接打开 syncookies )。开启该功能后,不会再丢弃 SYN 包,而是服务器根据当前状态计算出一个值,放在 SYN+ACK 中发出,当客户端返回 ACK 报文时,取出该值校验合法性,建立连接。
  • 减少 SYN+ACK 重发次数,使得处于 SYN_REVC 状态的连接尽快断开。

谈谈 TCP 相关的参数

参考链接:https://mp.weixin.qq.com/s/ytV7RZSyFXyvPW_lKhv8hw

这里讲的 TCP 参数与 TCP 三次握手和四次挥手优化有关。

三次握手优化角度:

  • 客户端(发送方):客户端行为有发送 SYNACK ,以及重发 SYNACK
    • tcp_syn_retries 参数:控制重传 SYN 次数,每次超时时间为上次 2 倍,初始为 1s。超过次数就会断开连接
  • 服务端(接收方):服务端行为较复杂,涉及到半连接队列和全连接队列的大小以及拒绝策略
    • 重发 FIN+ACK 次数:由 tcp_synack_retires 决定
    • 半连接队列:大小由 tcp_max_synbacklogsomaxconn 共同决定。可以通过增大这三个参数来增大半连接队列。同时 syncookies 参数控制当半连接队列满了时,生成状态值校验来避免放到半连接队列中。
    • 全连接队列:大小由 backlogsomaxconn 共同决定。拒绝策略由 tcp_abort_on_overflow 决定,0 表示丢弃 ACK ,不让其进入全连接队列,一般用这个,还可以解决短暂的突发网络繁忙。1 表示发送 RST 包使其断开连接。
  • 绕过三次握手:Linux 内核 3.1 版本后,出现了 Fast Open 机制,通过 Cookie 来绕过后面的三次握手。第一次正常三次握手,但是服务端可以在第二次握手时创建 Cookie 并发送给客户端。之后就可以重用该 TCP 连接,而不需要重复建立 TCP 连接。因为后续数据发送可以携带 Cookie ,服务端只需要验证 Cookie 即可。这种的缺点就是,如果重发,还需要重发 Cookie 。该机制使用 tcp_fastopn
    • 0 —— close
    • 1 —— Client 打开
    • 2 —— Server 打开
    • 3 —— 双方都打开

四次挥手优化角度:

  • 主动断开方:会进入 TIME_WAIT 状态,接收发送 FINACK
    • tcp_max_orphan 参数:调用 close 函数后,连接就变成了孤儿连接,该参数限制了最大孤儿连接数量,超过直接发送 RST 包断开连接。
    • FIN_WAIT1 状态优化: tcp_orphan_retries 参数 —— 表示处于 FIN_WAIT1 状态的 FIN 重传次数,超过直接关掉连接。
    • FIN_WAIT2 状态优化: tcp_fin_timeout 参数:表示孤儿连接等待 FIN 的最长时间,默认 60s。
    • TIME_WAIT 状态优化:
      • tcp_max_tw_buckets 参数:如果处于 TIME_WAIT 连接超过该参数,之后的连接不再进入该状态。
      • tcp_tw_reuse 参数:开启后可以复用处于 TIME_WAIT 状态的连接,需要配合时间戳使用。
  • 被动断开方:
    • 还是借助 tcp_orphan_retires 参数限定 FIN 重传次数。

小结:

  • 三次握手参数: tcp_syn_retriessomaxconnbacklogtcp_max_synsyncookiestcp_abort_on_overflowtcp_fastopn
  • 四次挥手参数: tcp_max_orphantcp_orphan_retriestcp_fin_timeouttcp_max_tw_bucketstcp_tw_reuse

聊聊 TCP 的可靠传输机制,比如重传、拥塞、流量控制等

  • 重传机制: 接收方回复 ACK 用于提醒发送方应该发那个数据包,当出现数据包丢失,接收方需要重传,分为超时重传和快速重传

    • 超时重传:接收方拿不到 3 这个数据包,就不发 3 的 ACK,发送方等待 3 这个 ACK 超时,再重传,一种是只重传 3(节省带宽,慢),另一种是 3,4,5(快,浪费带宽)等都重传。

    • 快速重传:发送方连续三次接收到同一个 ACK,则重传对应的数据报。

    其实重传都面临一个选择:只重传这一个还是重传后边所有数据报。这就引出 SACK 机制,接收方回复 SACK,SACK 会汇报收到的数据碎片,这个协议需要两边都支持。但是 SACK 并不能替代 ACK,接收方有权把已经报给发送端 SACK 里的数据给丢了

    SACK 有一个严重的问题,Linux 代码中,使用一个 sk_buff 的数据结构,简称 SKB ,用于存储发送、接收队列等,还有一个结构体为 skb_cb 用于控制缓存,记录各种 TCP packet 的各种信息,如小报文的数量 tcp_gso_segs ,无符号两字节,最多表示 64K,SKB 会将小报文段分片累积成大报文段再发送,但是内部最多维护 17 个分片队列,每个队列最大 32KB,如果有恶意攻击者将 mss 设置为 8,则每个小报文段大小为 8B。SACK 机制会将许多 SKB 合并填满一个 SKB ,那么就可能出现: 17 * 32 * 1024 / 8 > 64K 导致 tcp_gso_segs 溢出,进入 BUG_ON 函数使得服务器拒绝远程连接。

  • 滑动窗口:发送方和接收方都有窗口,接收方的滑动窗口可以使发送方根据接收方的接收能力来发送数据。确认机制为累计确认 / 累计应答,假设收到序列号为 100 的 ACK,说明 100 以前的数据都收到了。

    如果接收方的窗口为 0 了,也会将发送方的窗口设为 0(让发送方知道接收方窗口大小,这是根据 TCP 首部有个字段 win 可以控制窗口大小),此时不再发送数据,直到接收方窗口恢复,此时发送一个通知消息给发送方,并等待数据。如果这个通知消息因为网络拥塞丢失了,就会导致:接收方一直等待数据,发送方一直等待通知的死锁状况。所以一旦发送方窗口被置为 0,就会每隔一段时间发送探测报文,询问接收方窗口大小。

    Silly Window Syndrome 是一种现象,会对小的 window size 做出响应,为了避免对小的 window size 做出响应,直到有足够大的 window size 再响应,如果窗口太小,发送出去的数据甚至没有 MSS 高,就会先累积再发送。

  • 拥塞处理:名词: ssthresh 是慢启动阈值, cwnd 为拥塞窗口大小。

    三个状态,分别是慢启动,拥塞避免和快速恢复。

    • 慢启动cwnd (拥塞窗口)一开始为 1MSS ,每过 1 个 RTT 就二倍上升(本质是每收到一个 ACKcwmd++ ),如果超时ssthresh=cwnd/2 ,并且 cwnd=1 重新慢启动。如果之后 cwnd >= ssthresh 就进入拥塞避免。如果触发快速重传,就进入快速恢复
    • 拥塞避免:每一个 RTTcwnd++ ,如果超时,设置 ssthresh=cwnd/2, cwnd = 1 ,进入慢启动。如果连续三次收到同一个 Ack ,设置参数为 ssthresh=cwnd/2, cwnd = ssthresh + 3 。进入快速恢复
    • 快速恢复:(进入快速恢复说明窗口长度太小了)如果超时,同样操作,进入慢启动;每次收到一个冗余 ACKcwnd++ ,如果收到新 ACK ,进入拥塞避免

    这里的 RTT 是指一个窗口的数据全部发送出去,又全部收到 ACK 的时间,而不是某一个报文的往返时间

TCP 粘包和拆包问题

  • 要发送的数据小于 TCP 发送缓冲区的大小,TCP 将多次写入缓冲区的数据一次发送出去,将会发生粘包;或者是,接收数据端的应用层没有及时读取接收缓冲区的数据,将发生粘包。
  • 要发送的数据大于 TCP 发送缓冲区剩余空间大小或者大于 MSS,将会发生拆包;

解决方案为,发送端将数据包封装为固定长度,在数据尾部增加特殊字符,将数据分为两部分,头部和内容提,头部结构大小固定,且有一个字段声明内容体的大小。

Nagle 算法与延迟确认

如果发送方疯狂地向接收方发送很小的数据包,比如一次就发送 1 个字节,那么显然会有问题。每次都会为这种小数据包加上头部并且发过去,比较浪费网络带宽。

Nagle 算法是一种优化 TCP 协议的算法,它的主要作用是减少网络拥塞和提高网络吞吐量。当我们发送小数据包时,Nagle 算法会将它们缓存起来,直到缓存区中的数据量达到一定大小或者一定时间后再一次性发送出去。这样可以减少网络传输的次数,从而提高网络吞吐量。但是,当我们发送大数据包时,Nagle 算法会立即发送出去,以避免数据包的延迟。

而延迟确认则是接收方收到数据包后,如果暂时没有数据要发给对端,它可以等一小段时间,再确认(Linux 上默认是 40ms)。如果这段时间刚好有数据要传给对端,ACK 就随着数据传输,而不需要单独发送一次 ACK。如果超过时间还没有数据要发送,也发送 ACK,避免对端以为丢包。一般情况下,Nagle 算法和延迟确认不能一起使用,Nagle 算法意味着延迟发,延迟确认意味着延迟接收,会造成更大的延迟,会产生性能问题。

DNS 劫持和 DNS 污染

DNS 劫持:劫持了 DNS 服务器,通过某些手段取得某域名的解析记录控制权,进而修改此域名的解析结果,导致对该域名的访问由原 IP 地址转入到修改后的指定 IP。

DNS 污染:通过对 UDP 端口 53 上的 DNS 查询进行入侵检测,一经发现与关键词相匹配的请求则立即伪装成目标域名的解析服务器(NS,Name Server)给查询者返回虚假结果。很难靠个人设置解决,使用 VPN 是一个方法

聊聊 IP 协议,它和 MAC 地址有什么区别,IPV4 和 IPV6 呢?

IP 协议用于唯一标识网络设备,属于网络层协议,传输层将数据包传到网络层后,会为数据加上 IP 首部。 MAC 属于链路层,用于标识下一跳的网络设备的物理地址,数据从源主机到目的主机的过程中, MAC 首部每经过一个路由器都会变换,而 IP 地址不会变换。

IPv4 地址由 32 位组成,以前会根据前几位将其分为 ABCDE 类地址,但是分类地址的局限性太多,比如 C 类 IP 数量太少,而 A 类 IP 数量有太多,所以采用了无分类 IP 地址,通过子网掩码和 IP 地址做 ** & 运算来确定网络号、子网号 **。在路由控制中,目的地址与路由表中的子网掩码运算并比较网络号,从而进行路由转发。

IP 协议因为不能重组分片数据,所以分片会导致严重的性能损耗,一个分片丢失了,就要重发整个 IP 数据报,所以通过引入 MSS 将分片操作交给 TCP 处理。

IPv6 相对于 IPv4 的改进:

  • 取消了首部校验和字段:因为数据链路层和传输层都会校验
  • 取消分片 / 重组相关字段,这种操作只允许源 / 目标主机。
  • 使用了 128 位,16 进制,极大扩充了 IP 数量

WebSocket 和 Socket 的区别

Socket 其实就是等于 IP 地址 + 端口 + 协议,是一种传输层协议,它提供了一种在网络上进行双向通信的方式。Socket 可以用于不同的网络协议,如 TCP 和 UDP。WebSocket 是一种应用层协议,它建立在 HTTP 协议之上。WebSocket 提供了一种在客户端和服务器之间进行双向通信的方式,而不需要像 HTTP 那样每次请求都要建立一个新的连接。WebSocket 可以在客户端和服务器之间传输任意类型的数据,而不仅仅是文本数据。

WebSocket 和 Socket 的主要区别在于它们的应用场景和通信方式。Socket 通常用于客户端和服务器之间的通信,而 WebSocket 通常用于实时通信,如在线聊天和游戏等。WebSocket 使用 HTTP 协议进行握手,然后建立一个持久连接,而 Socket 需要在每次通信之前都要建立一个新的连接。