一、三次握手建立连接
1.1 TCP报文首部
1.1.1 序号SequenceNumber:
占4个字节,TCP是面向字节流的,在一个TCP连接中传送的字节流中的每一个字节都按顺序编号。
1.1.2 确认号Acknowledgment Number:
占4个字节,期望收到对方下一个报文段的第一个数据字节的序号。
若确认号=N,则表示到序号N-1为止的所有数据都已正确收到。
1.1.3 数据偏移:
数据偏移,即首部长度,指出TCP报文段的数据起始处距离TCP报文段的起始处有多远,以32比特(4字节)为计算单位。最多有60字节的首部,若无选项字段,正常为20字节。
1.1.4 控制位Flags:
-
同步序号SYN(SYNchronization):在连接建立时用来同步序号。
当SYN=1而ACK=0时,表明这是一个连接请求报文段;对方若同意建立连接,则在响应的报文段中使SYN=1,ACK=1。
因此SYN=1就表示这是一个连接请求或连接接受的报文。
-
确认ACK(ACKnowledgment):确认序号有效标识。
只有当ACK=1时确认号字段才有效。当ACK=0时,确认号无效。TCP规定,在连接建立后,所有传送的报文段,都必须把ACK置1。
-
推送PSH(PuSH):标识接收方应该尽快将这个报文段交给应用层。
接收到PSH = 1的TCP报文段,应尽快的交付接收应用进程,而不再等待整个缓存都填满了后再向上交付。
-
复位RST(ReSeT):重建连接标识,也可称为重建位或重置位。
当RST=1时,表明TCP连接中出现严重错误(如由于主机崩溃或其他原因),必须释放连接,然后再重新建立连接。
RST=1还用来拒绝一个非法的报文段或拒绝打开一个连接。
-
终止FIN(FINis):用来释放一个连接。
当FIN=1时,表明此报文段的发送方的数据已经发送完毕,并要求释放连接。
Client端要向Server端发送FIN请求,用于关闭Client->Server的连接。
Server端也要向Client发送FIN请求,用于关闭Server->Client的连接。
1.1.5 窗口Window:
窗口明确指出了现在允许对方发送的数据量,窗口会动态变化。
之所以会有这个限制,是因为接收方的数据缓存空间是有限的。
例如:发送了一个报文段,其确认号ACK的值是701,窗口字段是1000。这就是告诉对方:“从701号开始,我的接收缓存空间还可以接收1000个字节数据,你在给我发送数据时,必须考虑到这一点”
1.1.6 选项Options:
【MSS(Maximum Segment Size)最大报文长度】
定义:MSS 是发送方在TCP连接建立时协商的、单个TCP段可以发送的最大数据量(不包括TCP头部和IP头部)。
作用:MSS 值用于限制每个TCP段中负载数据(即应用层数据)的最大大小,以避免IP层的分片。通常,MSS 是根据路径上的最小MTU(Maximum Transmission Unit)来计算的:
MSS=MTU−IP头部大小−TCP头部大小
MTU 是指IP层能够传输的最大数据包大小,包括IP头部、TCP头部和数据部分。
举例:在以太网中,标准的MTU为1500字节,IP头部通常为20字节,TCP头部通常也是20字节,因此典型的MSS为 1460
字节。
【SACK permitted(Selective Acknowledgment)选择性确认】
是否启用SACK是在三次握手阶段,双方确定的,只有双方都在TCP Options中开启SACK permitted,在通信过程中才会使用SACK。
我们上面的例子中,在三次握手阶段,只有一方确认支持SACK,因此整个TCP数据包传输的过程中还是使用的普通的ACK机制;
下图是从网上找到的一个双方开启SACK的示例:
【TCP时间戳选项(Timestamp Option)】
TSval(Timestamp Value):这是时间戳选项中的“发送方时间戳值”字段。
它由发送方填充,发送方在发送报文时,把当前时钟的时间值放入到TSval中。
TSecr(Timestamp Echo Reply):这是时间戳选项中的“时间戳回送回答”字段。
它由接收方填充,接收方在ACK确认收到的报文时,把该数据包中TSval值复制到TSecr中回传给发送方。
TSval 和 TSecr 的用途
- 估算报文段的往返时间RTT(Round-Trip Time):
- 发送方在发送数据时,将当前的时间戳值写入TSval字段。
- 当接收方收到该数据包时,它将TSval的值存储起来,并在回应的ACK中将该值填入TSecr字段。
- 发送方收到ACK后,可以通过当前时间与TSecr的差值,精确地计算出此段数据的往返时间(RTT)。
- 防止序列号回绕TCP PAWS(Protect Against Wrapped Sequence numbers):
- TCP序列号是32位的,因此在长时间的连接中,可能会发生序列号回绕(wraparound)。时间戳选项通过附加时间戳信息,帮助区分新旧数据包,从而避免混淆。
【WS(Window Scale
)窗口缩放因子】
二、TCP数据发送及接收端确认ACK
三、Window动态调整、TCP Window Full 、TCP ZeroWindow
从上图中可以看到,随着接收方的缓冲区的减少,接收方给发送方发送ACK确认报文时,窗口大小Win会不断变小,最后的值为1295。
在Linux中,默认的TCP接收缓冲区大小一般在128 KB到1 MB之间。可以通过sysctl
命令查看和修改这些设置:
sysctl net.ipv4.tcp_rmem
输出通常包括最小、默认和最大接收缓冲区大小。
例如,4096 87380 6291456
表示最小值为4 KB,默认值为87 KB,最大值为6 MB。
缓冲区满:
此时,发送方向接收方发送报文数据的Len值就变成了1295;相应的这条数据在Wireshark中,就被标识出TCP Window Full。
发送TCP ZeroWindow:
接收方在向发送方发送ACK确认报文时,窗口大小Win就变成了0,表示接收方目前无法接收任何数据;相应的这条数据在Wireshark中,就被标识出TCP ZeroWindow。
发送方暂停传输:
当发送方收到这个TCP ZeroWindow通知后,将暂停发送数据,等待接收方恢复接收能力并发送一个窗口更新的报文(Window Update)。
在长时间没有数据传输的情况下,发送方会周期性地发送TCP Keep-Alive包,以确认连接的另一端仍然活跃并响应。
发送TCP Window Update:
一旦接收方的缓冲区中腾出空间,接收方会发送一个窗口更新报文,通知发送方可以重新开始发送数据。
四、数据重传及重复确认
TCP Out-Of-Order
TCP Out-of-Order 是一种常见现象,表示接收方接收到了乱序的TCP数据包。这意味着接收方接收到的某些TCP段的序列号与预期的不匹配,也就是说这些段并不是按顺序到达的。
这种情况常发生在网络条件不稳定、链路质量较差或使用了多条路径(例如通过负载均衡或多路径TCP传输)的场景中。
例如:在上图中,No.为9814的数据包中,客户端已经发送了Seq=1696803的数据包,但是后面又发送了Seq=1661763,Seq=1663223等序号早于1696803的数据包。
在Wireshark中,捕获到的TCP流量如果出现乱序数据包,Wireshark会在“Info”列中标记为“TCP Out-Of-Order”