首先有下面几个问题:
TCP序列号是两个方向的,每个方向有自己的序列号,这个序列号是随机产生的。
服务端序列号和确认号跟客户端一样。
这里说的客户端,服务器端都是发数据的一方。后面会通过抓包验证。
在网络分析中,读懂TCP序列号和确认号在的变化趋势,可以帮助我们学习TCP协议以及排查通讯故障,如通过查看序列号和确认号可以确定数据传输是否乱序。
TCP协议工作在OSI的传输层,是一种可靠的面向连接的数据流协议,TCP之所以可靠,是因为它保证了传送数据包的顺序。顺序是用一个序列号来保证的。响应包内也包括一个序列号,表示接收方准备好这个序列号的包。在TCP传送一个数据包时,它会把这个数据包放入重发队列中,同时启动计时器,如果收到了关于这个包的确认信息,便将此数据包从队列中删除,如果在计时器超时的时候仍然没有收到确认信息,则需要重新发送该数据包。另外,TCP通过数据分段中的序列号来保证所有传输的数据可以按照正常的顺序进行重组,从而保障数据传输的完整。
-----百度百科
TCP 协议为了实现可靠传输, 通信双方需要判断自己已经发送的数据包是否都被接收方收到, 如果没收到, 就需要重发。 为了实现这个需求, 很自然地就会引出序号(sequence number) 和 确认号(acknowledgement number) 的使用。
这里来举个栗子:
客户端在发送数据包,假设大小为 时, 假设上一个序列号为,那么服务端收到这个数据包以后, 就会回复一个确认号(参考上面的计算方法:客户端的确认号是上一个服务器端的序列号+负载数据,) ,告诉客户端我已经收到了序列号截止到的数据,期待的下一个数据起点是。
这样发送方就可以知道哪些数据被接收到,哪些数据没被接收到, 需要重发。
防止接受网络上粘滞的TCP包,如果都从0开始的话,极其容易接受之前断开连接发送的粘滞包。虽然可以采用每次TCP会话都使用一个UUID作为标记,但是考虑到每次都要携带UUID,比较浪费流量,所以就采用随机序列号的方法。
防止Hack猜测序列号,然后伪装TCP报文进行攻击。
下表为TCP状态码列表,以S指代服务器,C指代客户端,S&C表示两者,S/C表示两者之一:
还要知道TCP标志位的含义:
注意:标志位的ACK和确认号ack是完全不一样的。
上图是三次握手的过程, 为了方便计算seq(序列号)和ack(确认号),我们把上面的公式先搬下来:
结合上图和公式来理解下面的三次握手的过程:
第一次握手:客户端将标志位 SYN 置为 1,并随机产生一个值 seq = x,然后将该数据包发送给服务端,客户端进入 SYN_SENT 状态,等待服务端确认。
第二次握手:服务端收到客户端发来的数据包,看到标志位 SYN = 1,从而知道客户端想要建立连接,于是服务端将标志位 ACK 置为 1(标志位 SYN 此时还是 1),设置 ack = x + 1随机产生一个值 seq = y,然后将该数据包发送给客户端以确认连接请求,服务端进入 SYN_RECVD 状态,客户端进入 ESTABLISHED 状态。
第三次握手:客户端收到确认数据包后,检查 ack 是否为 x + 1,标志位 ACK 是否为 1,如果都满足,则将标志位 ACK 置为 1,设置seq = x + 1, ack = y + 1,然后将该数据包发送给服务端,服务端检查 ack 是否为 y + 1,标志位 ACK 是否为 1,如果都满足,则连接建立成功。服务端进入 ESTABLISHED 状态 。
对于上面三次握手的过程,你可能会有疑问:负载数据1是咋来的?
TCP规定,SYN报文段(即SYN = 1的报文段)不能携带数据,但要消耗掉一个序号。
这样是不是就理解那个“1”是咋来的了,接下来我们通过实际的数据包来验证序列号和确认号在TCP连接建立过程中的变化。
使用的抓包工具是科来网络分析系统技术交流版,有兴趣的可以下载分析一下,点我下载。
图一
可以看到上图红框是三次握手的过程,下面我们具体看一下是怎么建立连接的 。
图二
第一次握手:客户端将标志位 SYN 置为 1,并随机产生一个值 seq = 3066323072,此时ack = 0,然后将该数据包发送给服务端,客户端进入 SYN_SENT 状态,等待服务端确认。
图三
第二次握手:服务端收到客户端发来的数据包,看到标志位 SYN = 1,从而知道客户端想要建立连接,于是服务端将标志位 ACK 置为 1(标志位 SYN 此时还是 1),设置 ack = 3066323072 + 1(客户端的确认号,是上一个服务器端的序列号+负载数据),此时ack = 3066323073,随机产生一个值 seq = 1997685163,然后将该数据包发送给客户端以确认连接请求,服务端进入 SYN_RECVD 状态,客户端进入 ESTABLISHED 状态。
图四
第三次握手:客户端收到确认数据包后,检查服务器发来的 ack 是否为 3066323073,标志位 ACK 是否为 1,如果都满足,则将标志位 ACK 置为 1,设置seq = 3066323072 + 1(客户端的序列号为上一个服务器端的确认号,同时也是上一个客户端的序列号+负载数据), ack = 1997685163 + 1,此时seq = 3066323073, ack = 1997685164然后将该数据包发送给服务端,服务端检查 ack 是否为 1997685164,标志位 ACK 是否为 1,如果都满足,则连接建立成功。服务端进入 ESTABLISHED 状态 。
注意:“下一个序列号”(图片中序列号右边)是科来网络分析系统为了方便用户查找下一个连续数据包,而根据数据包序列号和确认号自动计算得出,该字段在实际数据包中是不存在的。这个数据(下一个序列号)在TCP三次握手(建立连接)和四次挥手(断开连接)中是不正确的。
这个数据(下一个序列号)在内部实现上没有考虑到SYN报文段和FIN报文段,虽然不能携带数据,但要消耗掉一个序号,前两次握手的负载数据应该是1,而不是图一的0。
后续待补。。。