小谈TCP协议中的ACK和SEQ号

小谈TCP协议中的ACK和SEQ号

问题背景

根据维基百科的定义

传输控制协议(英语:TransmissionControlProtocol,缩写:TCP)是一种面向连接的、可靠的、基于字节流传输层通信协议,由IETFRFC793定义。

很多人日常称呼TCP为传输层协议,这个名称不能说不对,但是会给人一种误解,认为TCP负责网络包的传输。但实际上呢,TCP其实是对于传输的控制,保证可靠,所以称之为控制层协议更恰当一些。

正如维基百科所讲的一样,TCP具有可靠性,而这种可靠性是通过超时重传、快速重传、SACK等方法实现不丢包的设计(如果你对这些名词感到陌生,其实也不妨碍对本文的阅读,只是还是需要补充这些基础知识)。而这些方法的实现就是依靠协议头里面的ackseqlen等字段的设置。

TCP协议头展示

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          Source Port          |       Destination Port        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        Sequence Number                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Acknowledgment Number                      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Data |           |U|A|P|R|S|F|                               |
   | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
   |       |           |G|K|H|T|N|N|                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           Checksum            |         Urgent Pointer        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                             data                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

回归本文,我们只关注Sequence NumberAcknowledgment Number其他字段在后面的文章中再做阐释。

seq(Sequence Number):32bits,表示这个tcp包的序列号。tcp协议拼凑接收到的数据包时,根据seq来确定顺序,并且能够确定是否有数据包丢失。

ack(Acknowledgment Number):32bits,表示这个包的确认号。首先意味着已经收到对方了多少字节数据,其次告诉对方接下来的包的seq要从ack确定的数值继续接力。

如果当前还没有看懂 上面这个图,那就请继续往下看哈。

tcp就是根据ack号来告知对方是否收到了消息,下文的实际抓包分析便是证明这一点。

我们接下来采用Wireshark进行抓包,并设置初始序列号为0,以便清晰的阅读。

还有一个len:表示tcp携带的数据长度,不包括tcp头部信息的长度。

Wireshark抓包

本地请求61.135.185.32这个ip,这个过程的抓包如下。

三次握手

(客户端)1号包:我能和你建立连接吗?

  • seq=0,表示这是一个新的开始
  • 没有ack,因为还没有建立连接,也就不存在我收到了对方多少的数据的说法
  • Len=0,表示我没有传输数据,就是一个想要建立连接的tcp包而已。

(服务端)2号包:我收到了,我们能进行连接,快来玩吧。

  • seq=0
  • ack=1暗示了两点,第一表示我收到了你刚才的那个seq=0的连接请求,另外告诉对方接下来请从seq=1开始给我传输数据
  • Len=0,表示同样没有传输数据。

(客户端)3号包:好的,那我们就连接吧。

  • seq=1,响应上面的包,我真的从seq=1开始传输哦
  • ack=1,表示我收到了你的seq=0同意连接,下面你也请从seq=1给我传输数据吧
  • Len=0

好了,三次握手愉快的结束,建立起来了连接。


总结一下三次握手的过程:

  • 起始包的seq都等于0
  • 三次握手中的ack=对方上一个的seq+1
  • seq等于对方上次的ack

数据传输过程

(客户端)4号包:我要你的首页信息

客户端发送http请求,http请求需要tcp进行控制,然后交给ip层,然后由网卡发出...

注意4号帧tcp包的内容

  • seq=1,因为上次没有传输数据,seq号不变,也就是3号包的seq=1,len=0
  • ack=1,告诉服务端你要是发送数据,得从seq=1开始哈
  • len=77,表示我这次传输的数据字节数

(服务端)5号包:好的,我收到你的请求了。

  • seq=1,如4号包的ack所要求的
  • ack=78ack=4号包的seq+4号包的len = 1+77=78表示客户端啊,你要是再发就从seq=78开始发送哈
  • len=0

(服务端)`6`号包:诺,给你的数据

5、6号均为服务端发送的包,在这期间没有接收到包,理所应当的,5、6号包的seq、ack是一样的。

  • seq=1
  • ack=78
  • len=1440,数据的长度

(客户端)7号包:收到啦

  • seq=78,你让我从78发,我就从78
  • ack=14411441=6号包的seq+6号包的len=1+1440=1441,表示我收到啦
  • len=0

抓包总结

发送方的包包括seqlen,接收方如何告知对方数据已经收到呢?

答案就在于接收方的ack=发送方的seq+发送方的len

整体来讲,就是这样。

特殊情况在于三次握手时,客户端、服务端握手时,len=0,此时对方就不是ack=seq+0,而是ack=seq+1

再次呈现下图

结合状态图

总结

  • 当我们说TCP是可靠的时候,凭借的是什么?

正如这篇文章所说,TCP连接过程中,双方的SEQ、ACK号就是确认数据传输是否到达对端的手段。发送端发送数据,需要对端的ACK让发送端放心。

下面我们稍微深入讨论一下哈

本文展示的都是正常情况,但是如果没有按照预期收到对端的ACK呢?

比如发送端发送数据之后,对端没有回复相应的ACK,发送端怎么办?

最简的,也是首先想到的办法。我发送端就当我刚才发的包丢了,等待一段时间后,我重新发呗!

上面这种手法,就是『超时重传』

这种情况,等待时间的设定就比较讲究了,太长太短都不行。一般是200ms,这也不算短了。难道就没有别的办法了吗?

有,需要对端配合与提示,就是所谓的『快速重传』。后来发现,快速重传的效率不够高,TCP还要开大招,SACK就来了。

  • 为什么三次握手与数据传输过程中,ACK号的确定具有不一致性?

三次握手的时候:对端ACK=发送端的SEQ+1,此时Len=0

传输数据的时候:对端ACK=发送端的SEQ+Len,此时Len!=0

确实,经过我们仔细的观察,我们确实发现了这个现象。但是,你想不想问问为什么?

本质上来说,就是三次握手的时候,虽然Len=0,也就是应用层的载荷为0。但是SYN标志本身,就是最重要的载荷了。因为SYN标志太重要了,所以在三次我手中,就把带有SYN包的载荷等价1,而不是0哈。那有人可能会说,四次挥手的时候,FIN标志也是很重要的,难不成四次挥手的时候,也会把FIN包的载荷等价为1吗?你说对了,就是这么回事!

  • TCP一定是可靠的吗?

肯定不是啊,所有说可靠的地方,其实都是表达自己会尽力而为。

为什么这么说?TCPIP网络层之后,更在物理层以上,你说在数据传输的过程中,网口或者网线突然烧了。你说TCP这个时候还保证个毛线的可靠性啊

展望

通过以上的讨论,明白了TCP是如何通过seqack来确定包的接收与否。

下面我会继续写文,方向大概为:

  • TCP头部其他字段的含义以及作用
  • TCP重传机制
  • TCPUDP的区别,对应的socket编程

希望大家多多留言交流~

引用

1、<TCP/IP网络编程> -尹圣雨

2、<Wireshark网络分析的艺术> - 林沛满

编辑于 2022-11-03 15:59・IP 属地北京