TCP三次握手/四次挥手的文章看过太多,但有些不太正确。还是决定自己写一篇,也算是对自己知识和碰到的问题的总结。
tcp的主要特点
- 面向连接
- 点到点
- 可靠交付
- 全双工通信
- 面向字节流
其中核心的特点:tcp是可以可靠传输协议,它的其他所有特点都为这个可靠传输服务。
如何保证可靠传输
tcp在传输过程中都有一个ack,接收方通过ack告诉发送方收到那些包了。这样发送方能知道有没有丢包,进而确定重传。
tcp三次握手
第一步:client发送连接请求报文段,将SYN位置为1,Seq(Sequence Number)为X(由操作系统动态随机选取一个32位长的序列号)。然后,客户端进入SYN_SEND状态,等待服务器的确认。
第二步:server收到client的SYN报文段。需要对这个SYN报文段进行确认,设置Ack(Acknowledgment Number)设置为X(第一次握手中的Seq的值)+1。同时,自己还要发送SYN请求信息,将SYN位置为1,Seq(Sequence Number)为Y(由操作系统动态随机选取一个32位长的序列号)。服务器端将上述所有信息一并发送给客户端,此时服务器进入SYN_RECV状态。
第三步:client收到sever的SYN+ACK后,回复server一个ACK。将Ack(Acknowledgment Number)设置为Y(第二次握手中的Seq的值)+1,Seq(Sequence Number)设置为X+1(第二次握手中的Ack(Acknowledgment Number)值),向服务器发送ACK报文段,这个报文段发送完毕以后,客户端和服务器端都进入ESTABLISHED状态,完成TCP三次握手。
tcp首部简介
这里先介绍下TCP首部的相关知识。三次握手相关的是主要是ACK和SYN这两个标志比特位。
(1)源端口和目的端口:各占2字节。
(2)序号(Seq, Sequence Number):占4个字节。用来标识从TCP发端向TCP收端发送的数据字节流,它表示在这个报文段中的的第一个数据字节在数据流中的序号。主要用来解决网络报乱序的问题。序号范围是[0, 2^32 - 1],共2^32个序号。
(3)确认号(Ack, Acknowledgment Number): 占4个字节。期望收到对方下一个报文段的第一个数据字节的序号,因此,确认序号应当是上次已成功收到数据字节序号加1。不过,只有当标志位中的ACK标志(下面介绍)为1时该确认序列号的字段才有效。主要用来解决不丢包的问题。
(4)数据偏移(Offset): 占4位。TCP报文段的数据起始处距离TCP报文段的起始处有多远。实际上指出TCP报文段的首部长度。但注意,“数据偏移”的单位是32位字(即以4字节长度为单位)。最多能表示15个32bit的的字,即4*15=60个字节的首部长度。因此TCP最多有60字节的首部。然而,没有选项字段,正常的长度是20字节。
(5)保留: 占6位。保留为今后使用,目前置为0。
(6)标志比特(TCP Flags): TCP首部中有6个标志比特,它们中的多个可同时被设置为1,主要是用于操控TCP的状态机的,依次为URG,ACK,PSH,RST,SYN,FIN。每个标志位的意思如下:
- URG(URGent): 此标志表示TCP包的紧急指针域(后面马上就要说到)有效,用来保证TCP连接不被中断,并且督促中间层设备要尽快处理这些数据。
- ACK(ACKnowlegment): 仅当ACK=1时Ack字段有效。在连接建立后所有传送的报文段都必须把ACK置1。
- PSH(PuSH): 这个标志位表示Push操作。所谓Push操作就是指在数据包到达接收端以后,立即传送给应用程序,而不是在缓冲区中排队。
- RST(ReSeT): 这个标志表示连接复位请求。用来复位那些产生错误的连接,也被用来拒绝错误和非法的数据包。
- SYN(SYNchoronization): 表示同步序号,用来建立连接。SYN标志位和ACK标志位搭配使用,当连接请求的时候,SYN=1,ACK=0。连接被响应的时候,SYN=1,ACK=1。
- FIN(FINis): 表示发送端已经达到数据末尾,也就是说双方的数据传送完成,没有数据可以传送了,发送FIN标志位的TCP数据包后,连接将被断开。
(7)窗口: 占2个字节,[0, 2^16 - 1]。指的是发送本报文段的一方的接收窗口。明确指出了现在允许对方发送的数据量。窗口值是经常动态变化的。
(8)校验和: 占2字节。该字段检验的范围包括首部和数据这两部分。由发端计算和存储,并由收端进行验证。
(9)紧急指针: 占2个字节,紧急指针仅在URG=1时才有意义,它指出本报文段中的紧急数据的字节数。当所有紧急数据处理完毕时,TCP就告诉应用程序恢复到正常操作。值得注意的是,即使窗口为0时也可发送紧急数据。
(10)选项: 长度可变,最长可达40字节,当没有选项时,TCP的首部长度是20字节。最初只规定了一种选项,即最大报文段长度MSS(Maximum Segment Size),MSS是指每一个TCP报文段中的数据字段的最大长度。
抓包实践
来看一个pb请求的三次握手过程
client: 172.17.1.217 server: 172.27.247.168
握手的核心目的是告知对方seq(client的初始seq,server的初始seq),对方回复ack(收到的seq+包的大小),这样发送端就知道有没有丢包了。
握手的次要目的是告知和协商一些信息。
- MSS–最大传输包
- SACK_PERM–是否支持Selective ack(用户优化重传效率)
- WS–窗口计算指数(有点复杂的话先不用管)
这就是tcp为什么要握手建立连接,就是为了解决tcp的可靠传输。
查看第一次握手的详情
查看第二次握手的详情
查看第三次握手的详情
为什么要三次握手?
如果只有一次握手,Client不能确定与Server的单向连接,更加不能确定Server与Client的单向连接;
如果只有两次握手,Client确定与Server的单向连接,但是Server不能确定与Client的单向连接;
只有三次握手,Client与Server才能相互确认双向连接,实现双工数据传输。
谢希仁版《计算机网络》中的例子是这样的,“已失效的连接请求报文段”的产生在这样一种情况下:client发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达server。本来这是一个早已失效的报文段。但server收到此失效的连接请求报文段后,就误认为是client再次发出的一个新的连接请求。于是就向client发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要server发出确认,新的连接就建立了。由于现在client并没有发出建立连接的请求,因此不会理睬server的确认,也不会向server发送数据。但server却以为新的运输连接已经建立,并一直等待client发来数据。这样,server的很多资源就白白浪费掉了。采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。
握手中断
- 第一次握手中断: A发送给B的SYN中断,A会周期性超时重传,直到A收到B的确认响应。
- 第二次握手中断: B发送给A的SYN、ACK中断,B会周期性超时重传,直到B收到A的确认响应。
- 第三次握手中断: A发送给B的ACK中断,A不会重传。超时后,B会重传SYN信号(即回到第二次握手),直到B收到A的确认响应。