type
Post
status
Published
date
Jan 30, 2026 12:22 PM
slug
summary
记录一些常见的计算机网络相关的知识
tags
基础
category
八股盛宴
icon
password
概论
网络模型
- 七层网络模型
- 四层网络模型
- 应用层(Application Layer)message 应用层专注于为用户提供应用功能 HTTP、FTP、Telnet、DNS、SMTP ···
- 传输层(Transport Layer)segment 实现端到端的可靠或高效数据传输 TCP/UDP
- 网络层(Internet Layer)packet 负责数据包的路由与转发,跨网络寻址 IP
- 网络接口层(Link Layer)frame 物理传输数据,管理硬件通信 网络接口层主要为网络层提供「链路级别」传输的服务,负责在以太网、WiFi 这样的底层网络上发送原始数据包,工作在网卡这个层次,使用 MAC 地址来标识网络上的设备。
从键入网址到网页显示
- 浏览器解析 URL(访问协议+Web服务器+文件路径),
- 浏览器从本地 DNS 服务器逐级向下(根→顶级→权威)查询 Web 服务器对应 IP 地址 在此之前,浏览器会先查询浏览器缓存,有则返回,否则查询操作系统缓存,再否则查看 hosts 文件。
- 浏览器构造 HTTP 消息
- 委托操作系统发送请求并等待响应后渲染展示。 浏览器通过 Socket 接口发送数据包,数据包会被网络协议栈从上到下逐层处理,然后送入网卡队列经由交换机和路由器组成的互联网网络发送到目标服务器。
HTTP 超文本传输协议
HTTP 是一个在计算机世界里专门在两点之间传输 [ 文字、图片、音频、视频 ] 等「超文本」数据的「约定和规范」。
请求行:请求方法(GET、POST)、URL、HTTP
请求头:键值对(请求元数据,如客户端类型、身份验证等等)
请求体:需要发送的额外数据,只在某些请求方法中使用
GET & POST
根据 RFC 规范:
GET 的语义是从服务器获取指定的资源.
POST 的语义是根据请求负荷对指定的资源做出处理
HTTP 状态码
1xx(提示信息)
- 100 Continue:服务器已收到请求头,等待客户端发送请求体。
- 101 Switching Protocols:服务器将切换协议。例如从HTTP切换到WebSocket。
2xx(成功状态码)
- 200 OK:请求成功,返回资源。
- 201 Created:请求成功,服务器创建了新资源。
- 204 No Content:请求成功,但响应的没有内容。
3xx(重定向状态码)
- 301 Moved Permanently:资源永久移动到新位置
- 302 Found:资源临时移动到新位置
- 304 Not Modified:资源未修改,客户端可直接使用缓存。
4xx(客户端错误)
- 400 Bad Request:请求的格式有问题
- 401 Unauthorized:请求需要身份验证。
- 403 Forbidden:无权访问。
- 404 Not Found:请求的资源不存在。
5xx(服务器错误)
- 500 Internal Server Error:服务器内部错误
- 502 Bad Gateway:服务器作为网关,从上游的服务器,收到无效响应。
- 503 Service Unavailable:服务器暂时不可用,可能维在维护。
- 504 Gateway Timeout:服务器作为网关,没有及时从上游服务器到响应。
HTTP 常见字段
- Host 客户端发送请求时,用来指定服务器的域名,可以将请求发往「同一台」服务器上的不同网站。
- Content-Length 服务器在返回数据时表明本次回应的数据长度。 目的是解决使用 TCP 作为下层协议带来的粘包问题。
- Connection 客户端要求服务器使用「HTTP 长连接」机制,以便其他请求复用。
- Content-Type & Accept 服务器回应时,告诉客户端,本次数据是什么格式
- Content-Encoding & Accept-Encoding 说明数据的压缩方法。表示服务器返回或者客户端需要的数据使用了什么压缩格式
HTTP/1.1
HTTP 基于 TCP 进行传输。
HTTP 基本的报文格式就是 header + body,头部信息也是 key-value 简单文本的形式。
HTTP 无状态连接并且明文传输,基于此 HTTP 1.1 作如下优化:
- 持久连接 HTTP/1.1 提出了长连接的通信方式,也叫持久连接。 这种方式的好处在于减少了 TCP 连接的重复建立和断开所造成的额外开销,减轻了服务器端的负载。持久连接的特点是,只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。
管道传输(默认不开启)HTTP/1.1 采用了长连接的方式,这使得管道(pipeline)网络传输成为了可能。 即可在同一个 TCP 连接里面,客户端可以发起多个请求,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。 服务器必须按照接收请求的顺序发送对这些管道化请求的响应。HTTP/1.1 管道解决了请求的队头阻塞,但是没有解决响应的队头阻塞。
HTTPS
HTTPS 在 HTTP 与 TCP 层之间加入了 SSL/TLS 协议
使用混合加密方案使得报文能够加密传输,
使用摘要算法保证数据完整性,
引入数字证书避免三方冒充。
SSL 是早期版本,现代 HTTPS 基本使用 TLS 协议进行加密。
TLS Handshake Protocol

客户端与服务器进入加密通信,使用普通的 HTTP 协议,用「会话秘钥」加密内容
- ClientHello
- TLS version、Client Random、Cipher Suites(支持的套件)
- SeverHello 确认 TLS 协议版本,如果浏览器不支持,则关闭加密通信。 服务端生成 Server Random,从客户端发送的加密列表中选择加密方式,然后将服务器公钥等其他信息使用 CA 私钥加密生成 Certificate Signature;
- Server Random
- Cipher Suites(秘钥交换算法 + 签名算法 + 对称加密算法 RSA + 摘要算法)
- 数字证书 (Certificate Signature + 服务器公钥)
- Client Key Exchange 客户端首先使用 CA 公钥解密 Certificate Signature 得到原始信息哈希值,在计算已知消息哈希值,比较是否相同,相同则认为证书可信,否则不可信。(目的是确认消息的身份) 客户端生成新的随机数,并使用服务器公钥加密随机数发送到服务端,服务端解密获得 pre-master。至此客户端和服务端双方共享三个随机数,双方使用随机数生成会话秘钥 Master Secret。
- pre-master
- Change Cipher Spec 服务端通知客户端开始使用会话秘钥加密信息进行通信。
- Encrypted Handshake Message(Finishd) 客户端将之前所有数据做摘要并使用会话秘钥加密发送。 服务器获取信息并校验加密通信是否可以和握手信息是否被中途篡改过。
TSL Record Protocol
TLS 记录协议主要负责消息(HTTP 数据)的压缩,加密及数据的认证。
- 数据分片(Fragmentation) 记录协议将数据分片为 不超过16KB 的明文块,再逐块加密传输。这避免了因TCP分段导致的加密边界模糊(例如解密时无法对齐数据块)。
- 完整性保护(Integrity)
- 传统模式:为每个分片附加 HMAC(基于哈希的消息认证码),接收方验证MAC值是否匹配。 现代模式(TLS 1.3) :使用 AEAD 加密模式(如AES-GCM),在加密时自动生成认证标签(类似MAC),确保数据未被篡改。
- 防重放攻击(Replay Attack Prevention) 攻击者可能截获并重复发送合法密文(如支付请求),导致重复操作。记录协议隐式为每条记录分配序列号,并参与MAC计算或AEAD附加数据。接收方会拒绝重复或乱序的序列号。
- 标准化封装(Standardized Format) 使用对称秘钥加密处理过的数据片的到加密后数据,同时记录协议为每个加密块添加 报头,这样做是为了让接收方能够正确解析数据。
HTTP/2
- 头部压缩 HTTP/2 会压缩头(Header)如果你同时发出多个请求,他们的头是一样的或是相似的,那么协议会帮你消除重复的部分。 HACK 算法:在客户端和服务器同时维护头信息表,所有字段都会存入后生成索引号,以后教化使用索引号而非完整字段名。
- 二进制格式 HTTP/2 不再像 HTTP/1.1 里的纯文本形式的报文,而是全面采用了二进制格式,头信息和数据体都是二进制,并且统称为帧(frame):头信息帧(Headers Frame)和数据帧(Data Frame)。
- 并发传输(解决 HTTP 层面队头阻塞问题,TCP 层面仍然存在) 引入 Stream 概念,多个 Stream 复用在一条 TCP 连接。 对于不同的 HTTP 请求使用唯一的 Stream ID 区分,不同 Stream Frame 可以乱序发送,即并发不同的 Stream。
- 服务推送 基于 Stream 概念,允许双方都可以建立 Stream,规定服务器 Stream ID 为偶数、客户端 Stream ID 为奇数。
队头阻塞
HTTP/2 通过 Stream 的并发能力,解决了 HTTP/1 队头阻塞的问题,看似很完美了,但是 HTTP/2 还是存在“队头阻塞”的问题,只不过问题不是在 HTTP 这一层面,而是在 TCP 这一层。
HTTP/2 是基于 TCP 协议来传输数据的,TCP 是字节流协议,TCP 层必须保证收到的字节数据是完整且连续的,这样内核才会将缓冲区里的数据返回给 HTTP 应用,那么当「前 1 个字节数据」没有到达时,后收到的字节数据只能存放在内核缓冲区里,只有等到这 1 个字节数据到达时,HTTP/2 应用层才能从内核中拿到数据,这就是 HTTP/2 队头阻塞问题。
HTTP/3
HTTP 3 基于 QUIC 进行传输。
Quick UDP Internet Connections(Google)
基于UDP的传输层协议,整合了TCP的可靠性、TLS的安全性、HTTP/2的多路复用等特性,旨在替代“TCP+TLS+HTTP/2”的传统组合。
- 无队头阻塞
- 更快的连接建立
- 连接迁移 QUIC 通过连接ID标记通信两端,客户端和服务器可以自定义标记,即使移动设备网络变化导致IP变化,只要仍保有上下文信息就可以复用连接。
TCP 传输控制协议
TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。
协议格式

- 源端口号和目的端口号
- 序列号 在建立连接时由计算机生成的随机数作为其初始值,通过 SYN 包传给接收端主机,每发送一次数据,就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。
- 确认号 指下一次「期望」收到的数据的序列号,发送端收到这个确认应答以后可以认为在这个序号以前的数据都已经被正常接收。用来解决丢包的问题。
- 控制位
- ACK,该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建立连接时的 SYN 包之外该位必须设置为 1
- RST,该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。
- SYN,该位为 1 时,表示希望建立连接,并在其「序列号」的字段进行序列号初始值的设定。
- FIN,该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换 FIN 位为 1 的 TCP 段。
- 校验和 checksum
- 首部长度 header length
- 窗口大小 rwnd
- 选项 options
- 数据 DATA
协议缺陷
- 建立连接延迟 基于 TCP 实现的应用协议,都是需要先建立三次握手才能进行数据传输,比如 HTTP 1.0/1.1、HTTP/2、HTTPS。现在大多数网站都是使用 HTTPS 的,这意味着在 TCP 三次握手之后,还需要经过 TLS 四次握手后,才能进行 HTTP 数据的传输,这在一定程序上增加了数据传输的延迟。
- 队头阻塞 TCP 是字节流协议,TCP 层必须保证收到的字节数据是完整且有序的,如果序列号较低的 TCP 段在网络传输中丢失了,即使序列号较高的 TCP 段已经被接收了,应用层也无法从内核中读取到这部分数据。
- 网络迁移 当移动设备的网络从 4G 切换到 WIFI 时,意味着 IP 地址变化了,那么就必须要断开连接,然后重新建立 TCP 连接。
面向连接
TCP 四元组可以唯一的确定一个连接:源地址、源端口、目标地址、目标端口,
但是不同时间发起的连接是不同的实例。
建立连接-三次握手

SYN
客户端会随机初始化序号(client_isn ),将此序号置于 TCP 首部的「序号」字段中,同时把 SYN 标志位置为 1,表示 SYN 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT 状态。
如果客户端迟迟收不到服务端的 SYN-ACK 报文,就会触发「超时重传」机制,重传 SYN 报文,重传的 SYN 报文的序列号不变。
随机初始
客户端和服务端建立一个 TCP 连接,在客户端发送数据包被网络阻塞了,然后超时重传了这个数据包,而此时服务端设备断电重启了,之前与客户端建立的连接就消失了,于是在收到客户端的数据包的时候就会发送 RST 报文。紧接着,客户端又与服务端建立了与上一个连接相同四元组的连接;在新连接建立完成后,上一个连接中被网络阻塞的数据包正好抵达了服务端,刚好该数据包的序列号正好是在服务端的接收窗口内,所以该数据包会被服务端正常接收,就会造成数据错乱。
如果每次建立连接,客户端和服务端的初始化序列号都是一样的话,很容易出现历史报文被下一个相同四元组的连接接收的问题。
随机化的初始序列号意味着每一次连接都是独立的,即使连接四要素完全相同。
起始 ISN 是基于时钟的,每 4 微秒 + 1,转一圈要 4.55 个小时。RFC793 提到初始化序列号 ISN 随机生成算法: ISN = M + F (localhost, localport, remotehost, remoteport)。
- M 是一个计时器,这个计时器每隔 4 微秒加 1。
- F 是一个 Hash 算法,根据源 IP、目的 IP、源端口、目的端口生成一个随机数值。要保证 Hash 算法不能被外部轻易推算得出,用 MD5 算法是一个比较好的选择。
随机数基于时钟计时器递增的,基本不可能会随机成一样的初始化序列号
超时重传
如果客户端迟迟收不到服务端的 SYN-ACK 报文,就会触发「超时重传」机制,重传 SYN 报文,重传的 SYN 报文的序列号不变。
不同版本的操作系统可能超时时间不同,这个超时时间是写死在内核里的。
在 Linux 里,客户端的 SYN 报文最大重传次数由 tcp_syn_retries内核参数控制,这个参数是可以自定义的,默认值一般是 5。通常,第一次超时重传是在 1 秒后,第二次超时重传是在 2 秒,第三次超时重传是在 4 秒后,第四次超时重传是在 8 秒后,第五次是在超时重传 16 秒后。每次超时的时间是上一次的 2 倍
半连接队列与全连接队列
当服务端接收到客户端的 SYN 报文时,会创建一个半连接的对象,然后将其加入到内核的「 SYN 队列」,接着发送 SYN + ACK 给客户端,等待客户端回应 ACK 报文;服务端接收到 ACK 报文后,从「 SYN 队列」取出一个半连接对象,然后创建一个新的连接对象放入到「 Accept 队列」,应用通过调用 accpet() socket 接口,从「 Accept 队列」取出连接对象。
当半连接队伍被打满,一般情况下后续报文会被丢弃,无法建立连接。
增大半连接队列
- 增大 net.ipv4.tcp_max_syn_backlog
- 增大 listen() 函数中的 backlog
- 增大 net.core.somaxconn
开启 net.ipv4.tcp_syncookies
当 「 SYN 队列」满之后,后续服务端收到 SYN 包,不会丢弃,而是根据算法,计算出一个 cookie 值;将 cookie 值放到第二次握手报文的「序列号」里,然后服务端回第二次握手给客户端;
服务端接收到客户端的应答报文时,服务端会检查这个 ACK 包的合法性。
如果合法,将该连接对象放入到「 Accept 队列」。最后应用程序通过调用 accpet() 接口,从「 Accept 队列」取出的连接。
SYN + ACK
服务端收到客户端的 SYN 报文后,首先服务端也随机初始化自己的序号(server_isn),将此序号填入 TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填入 client_isn + 1, 接着把 SYN 和 ACK 标志位置为 1。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD 状态。
ACK + Data
客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部 ACK 标志位置为 1 ,其次「确认应答号」字段填入 server_isn + 1 ,最后把报文发送给服务端,这次报文可以携带客户到服务端的数据,之后客户端处于 ESTABLISHED 状态。
“三次握手”的主要目的是减少资源浪费,设想客户端发送的 SYN 在网络中阻塞,于是客户端触发超时重传,实际上发送若干 SYN,如果服务端收到 SYN 则尝试建立连接,那么建立若干无效连接,进行第三次握手的目的实质上是确认两台设备特定端口之间的连接唯一性,同时确认双方都具有接受和发送信息的能力。
如此可以推出,如果已经确认过双方的能力,那么完全可以跳过第三次握手的验证。
TCP Fast Open

断开连接-四次挥手

FIN
如果主动关闭方一定时间内未收到被动关闭方ACK 回复,触发超时重传,重新传输 FIN 报文,最大重传次数由 tcp_orphan_retries 参数控制。当主动关闭方重传次数到达上限,就不再发送 FIN 报文,则会在等待一段时间(时间为上一次超时时间的 2 倍),如果还是没能收到 ACK,那么直接进入到 close 状态。
ACK
当服务端(被动关闭方)收到客户端(主动关闭方)的 FIN 报文后,内核会自动回复 ACK,同时连接处于 CLOSE_WAIT 状态,它表示等待应用进程调用 close 函数关闭连接。
FIN
服务端处于 CLOSE_WAIT 状态时,调用了 close 函数,内核就会发出 FIN 报文,
同时连接进入 LAST_ACK 状态,等待客户端返回 ACK 来确认连接关闭。
如果迟迟收不到这个 ACK,服务端就会重发 FIN 报文,重发次数仍然由 tcp_orphan_retries 参数控制,这与客户端重发 FIN 报文的重传次数控制方式是一样的。tcp_fin_timeout 时间内还是没能收到服务端的第三次挥手(FIN 报文),那么客户端就会断开连接。
CLOSE_WAIT
当服务端出现大量 CLOSE_WAIT 状态的连接的时候,说明服务端的程序没有调用 close 函数关闭连接。
当服务端出现大量 CLOSE_WAIT 状态的连接的时候,通常都是代码的问题,这时候我们需要针对具体的代码一步一步的进行排查和定位,主要分析的方向就是服务端为什么没有调用 close。
ACK
当主动关闭方收到被动关闭方的第三次挥手的 FIN 报文后,就会回 ACK 报文,也就是第四次挥手,此时客户端连接进入 TIME_WAIT 状态。
在 Linux 系统,TIME_WAIT 状态会持续 2MSL 后才会进入关闭状态。
然后,被动关闭方没有收到 ACK 报文前,还是处于 LAST_ACK 状态。
如果第四次挥手的 ACK 报文没有到达被动关闭方,被动关闭方就会重发 FIN 报文,重发次数仍然由前面介绍过的 tcp_orphan_retries 参数控制。
TIME_WAIT
等待足够的时间以确保最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭。
如果服务端没有收到 ACK,那么就会触发 TCP 重传机制,服务端会重新发送一个 FIN,这样一去一来刚好两个 MSL 的时间。
2 MSL
MSL 是 Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。 在 Linux 系统里 2MSL 默认是 60 秒,那么一个 MSL 也就是 30 秒。Linux 系统停留在 TIME_WAIT 的时间为固定的 60 秒。
- 网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,所以一来一回需要等待 2 倍的时间。可以看到 2MSL时长 这其实是相当于至少允许报文丢失一次。
- 序列号和初始化序列号并不是无限递增的,会发生回绕为初始值的情况,这意味着无法根据序列号来判断新老数据。为了防止历史连接中的数据,被后面相同四元组的连接错误的接收,这个时间足以让两个方向上的数据包都被丢弃,使得原来连接的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接所产生的。
2MSL 的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将重新计时。
TIME_WAIT 状态连接过多
- 如果 HTTP 没有启用长连接,连接都会由服务端主动关闭,这会造成大量连接处于 TIME_WAIT 状态;
- 即使启用 Keep Alive,如果长连接超时,同样也会造成服务端主动关闭大量 TCP 连接。
- Web 服务端通常会有个参数,来定义一条 HTTP 长连接上最大能处理的请求数量,当超过最大限制时,就会主动关闭连接。
服务器存在大量处于 TIME_WAIT 状态连接造成服务器资源被大量占用。最好的解决方式是使得连接主要由客户端关闭而不是服务端关闭,由分布在各处的客户端去承受。
- 启用 net.ipv4.tcp_tw_reuse 和 tcp_timestamps net.ipv4.tcp_tw_reuse 开启后发起连接时会复用处于 TIME_WAIT 的 socket ,强制启用 tcp_timestamps 的目的是解决序列号回绕问题。需要注意的是这有很大风险。 net.ipv4.tcp_tw_recycle,如果开启该选项的话,允许处于 TIME_WAIT 状态的连接被快速回收,该参数在 NAT 的网络下是不安全的。
- net.ipv4.tcp_max_tw_buckets 这个值默认为 18000,当系统中处于 TIME_WAIT 的连接一旦超过这个值时,系统就会将后面的 TIME_WAIT 连接状态重置。
- SO_LINGER 可以通过设置 socket 选项,来设置调用 close 关闭连接行为。如果l_onoff为非 0, 且l_linger值为 0,那么调用close后,会立该发送一个RST标志给对端,该 TCP 连接将跳过四次挥手,也就跳过了TIME_WAIT状态,直接关闭。
服务端通常需要等待完成数据的发送和处理,所以服务端的 ACK 和 FIN 一般都会分开发送,因此是需要四次挥手。但如果「没有数据要发送」并且「开启了 TCP 延迟确认机制」,那么第二和第三次挥手就会合并传输,这样就出现了三次挥手。
延迟确认机制
当发送没有携带数据的 ACK,它的网络效率也是很低的,因为它也有 40 个字节的 IP 头 和 TCP 头,但却没有携带数据报文。 为了解决 ACK 传输效率低问题,所以就衍生出了 TCP 延迟确认。
- 当有响应数据要发送时,ACK 会随着响应数据一起立刻发送给对方
- 当没有响应数据要发送时,ACK 将会延迟一段时间,以等待是否有响应数据可以一起发送
- 如果在延迟等待发送 ACK 期间,对方的第二个数据报文又到达了,这时就会立刻发送 ACK
连接故障
保活机制 定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间间隔,发送一个探测报文,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。
心跳机制 web 服务软件一般都会提供 keepalive_timeout 参数,用来指定 HTTP 长连接的超时时间。如果设置了 HTTP 长连接的超时时间是 60 秒,web 服务软件就会启动一个定时器,如果客户端在完成一个 HTTP 请求后,在 60 秒内都没有再发起新的请求,定时器的时间一到,就会触发回调函数来释放该连接。
保证可靠
重传机制
- 超时重传 在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的 ACK 确认应答报文,就会重发该数据,每当触发超时重传会将超时时间设置为先前值的两倍。
RTT & RTO
RTT和RTO都是动态变化的
需要 TCP 通过采样 RTT 的时间,然后进行加权平均,算出一个平滑 RTT 的值,而且这个值还是要不断变化的,因为网络状况不断地变化。除了采样 RTT,还要采样 RTT 的波动范围,这样就避免如果 RTT 有一个大的波动的话,很难被发现的情况。

TimeoutInterval = EstimatedRTT + 4*DevRTT
初始值为 1s,当出现超时则翻倍,只要收到报文段更新 ERTT 就重新计算 TimeoutInterval 。
SampleRTT
某报文段被发出到对收到该报文段的确认的时间,tcp 会周期性的测量该唯一值。
由于路由器的拥塞和端系统负载变化,该值会随之波动,需要计算 EstimatedRTT。
该值由旧值和新值加权组合而成,最近样本的权重要更高,因为越近的样本越能反映网络当前的拥塞情况。
EWMA,指数加权移动平均
DevRTT 描述 RTT 的变化,是 SampleRTT 与 EstimatedRTT 差值的 EWMA
- 快速重传
冗余 ACK:序号大于下一个所期望的、按序的报文段
一旦收到三个冗余 ACK 则认为该报文段已经丢失,执行快速重传,
即在定时器过期之前就重传。
- 选择性确认 SACK 接收方记录已收到的数据信息并发送给发送方,只重传丢失的数据 Duplicate SACK 使用了 SACK 来告诉「发送方」有哪些数据被重复接收。 可以让「发送方」知道,是发出去的包丢了,还是接收方回应的 ACK 包丢了 可以知道是不是「发送方」的数据包被网络延迟了; 可以知道网络中是不是把「发送方」的数据包给复制了
流量控制
TCP 提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制。 TCP 通过让接收方指明希望从发送方接收的数据大小(窗口大小)来进行流量控制。
接受窗口即无需等待确认应答,而可以继续发送数据的最大值。
接收窗口表示接收方还有多少可用缓存空间,连接两端的发送方都各自维护一个接收窗口。
累计确认

窗口的实现实际上是操作系统开辟的一个缓存空间,发送方主机在等到确认应答返回之前,必须在缓冲区中保留已发送的数据。如果按期收到确认应答,此时数据就可以从缓存区清除。
rwnd = RcvBuffer - [LastByteRcvd - LastByteRead]
LastByteSend - LastByteAcked <= rwnd
窗口探测
当接收方 rwnd = 0 时,发送方将持续发送只有一个字节数据的报文段,当接收方缓存可用时就会接受并回复 rwnd 以使得流程继续。
如果这个通告窗口的 ACK 报文在网络中丢失了,那双方就会陷入死锁。
为了解决这个问题,TCP 为每个连接设有一个持续定时器,只要 TCP 连接一方收到对方的零窗口通知,就启动持续计时器。如果持续计时器超时,就会发送窗口探测 ( Window probe ) 报文,而对方在确认这个探测报文时,给出自己现在的接收窗口大小。
窗口探测的次数一般为 3 次,每次大约 30-60 秒(不同的实现可能会不一样)。如果 3 次过后接收窗口还是 0 的话,有的 TCP 实现就会发 RST 报文来中断连接
糊涂窗口
如果接收方腾出几个字节并告诉发送方现在有几个字节的窗口,而发送方会义无反顾地发送这几个字节,这就是糊涂窗口综合症。
TCP + IP 头有 40 个字节,为了传输那几个字节的数据,要搭上这么大的开销,这太不经济了。
- 接收方不通告小窗口 当「窗口大小」小于 min( MSS,缓存空间/2 ) ,就会向发送方通告窗口为 0,也就阻止了发送方再发数据过来。 等到接收方处理了一些数据后,窗口大小 >= MSS,或者接收方缓存空间有一半可以使用,就可以把窗口打开让发送方发送数据过来。
- 发送方避免发送小数据 使用 Nagle 算法,该算法的思路是延时处理, 只有满足下面两个条件中的任意一个条件,才可以发送数据: 条件一:要等到窗口大小 >= MSS 并且 数据大小 >= MSS; 条件二:收到之前发送数据的 ack 回包;
拥塞控制
拥塞控制让每一个发送方根据所感知到的网络拥塞程度来限制其能向连接发送流量的速率。控制的目的就是避免「发送方」的数据填满整个网络。
发生了超时重传,就会认为网络出现了拥塞。
通过控制拥塞窗口 cwnd 大小限制发送流量速率
LastByteSent - LastByteAcked <= min (cwnd, rwnd)
该约束限制发送方中未被确认的数据量,即只允许发送方向该链接发送 cwnd 字节数据,在该 RTT 结束时发送方接受对数据的确认报文,因此发送方的发送速率大概是 cwnd / RTT 字节每秒
- 慢启动 初始值设置为 1 MSS,收到确认后发送速率翻倍,呈现指数增长。 如果存在超时指示的丢包事件,tcp 发送方将 cwnd 设置为 1 并重新开始慢启动。 同时将 ssthresh 慢启动阈值设置为 cwnd / 2. 当 cwnd 到达或超过慢启动阈值,则结束慢启动并且转移到拥塞避免模式。 如果检测到并执行快速重传则进入到快速恢复状态。
- 拥塞避免 cwnd += MSS / cwn 如果存在超时指示的丢包事件,和慢启动处理相同。 如果检测到并执行快速重传则将 cwnd 减半,然后进入快速恢复状态。
- 快速恢复 cwnd += MSS 如果存在超时指示的丢包事件,和慢启动处理相同。
- 作者:宗海
- 链接:https://nowave.cloud//article/1fbbeb96-1d72-8049-9b20-d0dbd365608d
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。





