1. 核心特性
面向连接:通过三次握手建立连接,四次挥手终止连接,确保通信双方状态同步。
TCP连接建立的3次握手
抓包:
client发出连接请求;
server回应client请求,并且同步发送syn连接;
client回应连接,至此3步握手完成。
TCP连接释放的4次握手
Client:192.168.2.100 -- server:192.168.2.101之间的TCP 4次挥手(释放连接)
client主动断开连接
server确认断开,server主动断开,client确认断开。
可靠性传输:通过序列号、确认应答(ACK)、超时重传、重复ACK检测等机制保证数据完整有序。
序列号、确认应答(ACK):
超时重传
重复ACK
全双工通信:双方可同时发送和接收数据。
字节流服务:数据被拆分为TCP段传输,接收方按序重组为连续字节流。
流量控制:通过滑动窗口机制动态调整发送速率,避免接收方缓冲区溢出。
什么是TCP窗口
TCP 采用流量控制(Flow Control)机制,使用滑动窗口(Sliding Window)来管理数据的发送和接收。
TCP 头部中的 窗口大小字段(Window Size) 指定了接收方当前可以接受的最大数据量,用于控制数据流,以防止接收方被发送方淹没。
窗口大小字段(16 位)最大能表示的值是 65,535(2¹⁶ - 1)字节,即约 64 KB,这个值在现代高速网络环境下严重不足,因此 TCP 提供了 Window Scale 选项来扩展窗口大小。
Window Scale选项的作用
Window Scale(窗口缩放)选项用于扩展 TCP 窗口的大小,使其支持最大 1GB(2³⁰ 字节)的窗口,而不是 64KB。
在 TCP 头部,窗口大小字段 只有 16 位,最大值为 65,535 字节(64KB)。
但是,高带宽-时延(BDP,Bandwidth Delay Product)网络环境下,64KB 可能会严重限制吞吐量。
Window Scale 选项通过左移窗口大小字段,扩展了窗口的范围,最大可达 1GB。
Window Scale 选项的工作原理
TCP 窗口扩展通过一个"缩放因子"(Scaling Factor)来扩大窗口的大小:
Window Scale 选项定义了一个因子 n(0 ≤ n ≤ 14),窗口大小的计算方式变为:
实际窗口大小=TCP 头部窗口大小字段×2^n(2的n次方)
其中:
TCP 头部的窗口字段(Window Size) 仍然存储 16 位的值(最大 65,535)。
缩放因子 n 在 Window Scale 选项中存储,范围是 0 到 14。
最终的实际窗口大小最大可达 65,535 × 2¹⁴ = 1,073,725,440 字节(即 1GB)。
Window Scale 选项的格式
Kind Length Shift Count
+------+--------+-------------+
| 3 | 3 | n |
+------+--------+-------------+
字段解释:
Kind = 3:表示这是 Window Scale 选项。
Length = 3:整个字段占用 3 字节。
Shift Count:即缩放因子 n,接收方必须用 2^n 进行窗口扩展计算。
Window Scale 选项的协商(在 TCP 三次握手时)
Window Scale 选项只能在 TCP 三次握手期间协商,后续不能更改。
双方都必须在 SYN 报文中发送 Window Scale 选项,否则不会启用。
每一方独立协商自己的窗口缩放因子。
Tcp连接第一次握手协商win-mss-ws
Tcp连接第二次握手协商win-mss-ws
Tcp连接第三次握手协商win
TCP发送窗口和接收窗口
拥塞控制:通过慢启动、拥塞避免、快速重传/恢复等算法避免网络过载。
慢启动(Slow Start):
初始拥塞窗口(cwnd)较小,每收到一个ACK,cwnd指数增长(如1, 2, 4, 8...)。
达到阈值(ssthresh)后进入拥塞避免阶段。
拥塞避免:
cwnd线性增长(如每RTT增加1),避免激进发送。
快速重传与快速恢复:
检测到3个重复ACK时,立即重传丢失包,并设置ssthresh = cwnd/2, cwnd = ssthresh + 3,进入快速恢复阶段。
超时重传(拥塞处理):
超时表明严重拥塞,重置ssthresh = cwnd/2, cwnd = 1,重新慢启动。
1、慢启动;慢启动的算法:当发送方每收到一个 ACK,拥塞窗口 cwnd 的大小就会加 1。(慢启动呈指数增加);
这里假定拥塞窗口 cwnd 和发送窗口 swnd 相等,下面举个栗子:
连接建立完成后,一开始初始化 cwnd = 1,表示可以传一个 MSS 大小的数据。
当收到一个 ACK 确认应答后,cwnd 增加 1,于是一次能够发送 2 个
当收到 2 个的 ACK 确认应答后, cwnd 增加 2,于是就可以比之前多发2 个,所以这一次能够发送 4 个
当这 4 个的 ACK 确认到来的时候,每个确认 cwnd 增加 1, 4 个确认 cwnd 增加 4,于是就可以比之前多发 4 个,所以这一次能够发送 8 个。可以看出慢启动算法,发包的个数是指数性的增长。
那慢启动涨到什么时候是个头呢?
有一个叫慢启动门限 ssthresh (slow start threshold)状态变量。
当 cwnd(拥塞窗口) < ssthresh 时,使用慢启动算法。
当 cwnd(拥塞窗口) >= ssthresh 时,就会使用「拥塞避免算法」。
2、拥塞避免;进入拥塞避免算法后,它的规则是:每当收到一个 ACK 时,cwnd 增加 1/cwnd。(拥塞避免线性增长),当触发了重传机制,也就进入了「拥塞发生算法」。
前面说道,当拥塞窗口 cwnd 「超过」慢启动门限 ssthresh 就会进入拥塞避免算法。
一般来说 ssthresh 的大小是 65535 字节。
那么进入拥塞避免算法后,它的规则是:每当收到一个 ACK 时,cwnd 增加 1/cwnd。
接上前面的慢启动的例子,现假定 ssthresh 为 8:当 8 个 ACK 应答确认到来时,每个确认增加 1/8,8 个 ACK 确认 cwnd 一共增加 1,于是这一次能够发送 9 个 MSS 大小的数据,变成了线性增长。
就这么一直增长着后,网络就会慢慢进入了拥塞的状况了,于是就会出现丢包现象,这时就需要对丢失的数据包进行重传。
当触发了重传机制,也就进入了「拥塞发生算法」。
3、拥塞发生
当网络出现拥塞,也就是会发生数据包重传,重传机制主要有两种:超时重传、快速重传
当发生了「超时重传」,就会使用拥塞发生算法。
这个时候,Ssthresh(慢启动门限) 和 cwnd(拥塞窗口) 的值会发生变化:
ssthresh 设为 cwnd/2(慢启动门限设置为当前拥塞窗口的1/2),
cwnd 重置为 1 (是恢复为 cwnd 初始化值,我这里假定 cwnd 初始化值 1),
接着,就重新开始慢启动,慢启动是会突然减少数据流的。这真是一旦「超时重传」,马上回到解放前。但是这种方式太激进了,反应也很强烈,会造成网络卡顿。
发生快速重传的拥塞发生算法:
快速重传算法:当接收方发现丢了一个中间包的时候,发送三次前一个包的 ACK,于是发送端就会快速地重传,不必等待超时再重传。
TCP 认为这种情况不严重,因为大部分没丢,只丢了一小部分,则 ssthresh 和 cwnd 变化如下:
cwnd(拥塞窗口) = cwnd/2 ,也就是设置为原来的一半;
ssthresh(慢启动门限) = cwnd;
进入快速恢复算法。
4、快速恢复
快速恢复算法是认为,你还能收到 3 个重复 ACK 说明网络也不那么糟糕,所以没有必要像 RTO 超时那么强烈。
进入快速恢复之前,cwnd 和 ssthresh 已被更新了:
cwnd = cwnd/2 ,也就是设置为原来的一半;
ssthresh = cwnd;
进入快速恢复算法如下:
1.拥塞窗口 cwnd = ssthresh + 3 ( 3 的意思是确认有 3 个数据包被收到了);
2.重传丢失的数据包;
3.如果再收到重复的 ACK,那么 cwnd 增加 1;
4.如果收到新数据的 ACK 后,把 cwnd 设置为第一步中的 ssthresh 的值,原因是该 ACK 确认了新的数据,说明从 duplicated ACK 时的数据都已收到,该恢复过程已经结束,可以回到恢复之前的状态了,也即再次进入拥塞避免状态;
2. 抓包分析技巧
Wireshark过滤:
tcp.analysis.retransmission:定位重传包。
tcp.analysis.window_update:观察窗口变化。
tcp.options.sack:分析SACK块范围。
关键指标:
重传率 = 重传包数 / 总包数(>1%需告警)。
乱序深度:连续Out-Of-Order包数量。
RTT波动:标准差反映网络稳定性。