TCP 序列号和确认号是如何变化的?( 二 )

为什么第二次和第三次握手报文中的确认号是将对方的序列号 + 1 后作为确认号呢?
SYN 报文是特殊的 TCP 报文,用于建立连接时使用,虽然 SYN 报文不携带用户数据,但是 TCP 将 SYN 报文视为 1 字节的数据,当对方收到了 SYN 报文后,在回复 ACK 报文时,就需要将 ACK 报文中的确认号设置为 SYN 的序列号 + 1,这样做是有两个目的:
  • 告诉对方,我方已经收到 SYN 报文 。
  • 告诉对方,我方下一次「期望」收到的报文的序列号为此确认号,比如客户端与服务端完成三次握手之后,服务端接下来期望收到的是序列号为 client_isn + 1 的 TCP 数据报文 。
数据传输阶段的变化完成了,三次握手后,客户端就可以发送第一个 ** **TCP 数据报文了,假设客户端即将要发送 10 字节的数据,流程图下:
TCP 序列号和确认号是如何变化的?

文章插图
在这里插入图片描述
客户端发送 10 字节的数据,通常 TCP 数据报文的控制位是 [PSH, ACK],此时该 TCP 数据报文的序列号和确认号分别设置为:
  • 序列号设置为 client_isn + 1 。客户端上一次发送报文是 ACK 报文(第三次握手),该报文的 seq = client_isn + 1,由于是一个单纯的 ACK 报文,没有携带用户数据,所以 len = 0 。根据公式 1(_序列号 = 上一次发送的序列号 + len_),可以得出当前的序列号为 client_isn + 1 + 0,即 client_isn + 1 。
  • 确认号设置为 server_isn + 1 。没错,还是和第三次握手的 ACK 报文的确认号一样,这是因为客户端三次握手之后,发送 TCP 数据报文 之前,如果没有收到服务端的 TCP 数据报文,确认号还是延用上一次的,其实根据公式 2 你也能得到这个结论 。
可以看到,客户端与服务端完成 TCP 三次握手后,发送的第一个 「TCP 数据报文的序列号和确认号」都是和「第三次握手的 ACK 报文中序列号和确认号」一样的 。
接着,当服务端收到客户端 10 字节的 TCP 数据报文后,就需要回复一个 ACK 报文,此时该报文的序列号和确认号分别设置为:
  • 序列号设置为 server_isn + 1 。服务端上一次发送报文是 SYN-ACK 报文,序列号为 server_isn,根据公式 1(_序列号 = 上一次发送的序列号 + len 。特殊情况,如果上一次发送的报文是 SYN 报文或者 FIN 报文,则改为 + 1_),所以当前的序列号为 server_isn + 1 。
  • 确认号设置为 client_isn + 11。服务端上一次收到的报文是客户端发来的 10 字节 TCP 数据报文,该报文的 seq = client_isn + 1,len = 10 。根据公式 2(_确认号 = 上一次收到的报文中的序列号 + len_),也就是将「收到的 TCP 数据报文中的序列号 client_isn + 1,再加上 10(len = 10) 」的值作为了确认号,表示自己收到了该 10 字节的数据报文 。
之前有读者问,如果客户端发送的第三次握手 ACK 报文丢失了,处于 SYN_RCVD 状态服务端收到了客户端第一个 TCP 数据报文会发生什么?
刚才前面我也说了,发送的第一个 「TCP 数据报文的序列号和确认号」都是和「第三次握手的 ACK 报文中序列号和确认号」一样的,并且该 TCP 数据报文也有将 ACK 标记位置为 1 。如下图:
TCP 序列号和确认号是如何变化的?

文章插图
在这里插入图片描述
所以,服务端收到这个数据报文,是可以正常完成连接的建立,然后就可以正常接收这个数据包了 。
四次挥手阶段的变化最后,我们来看看四次挥手阶段中,序列号和确认号的变化 。
数据传输阶段结束后,客户端发起了 FIN 报文,请求服务端端开该 TCP 连接,此时就进入了 TCP 四次挥手阶段,如下图 。

经验总结扩展阅读