TCP的三次握手和四次挥手详解

本文最后更新于:January 31, 2022 am

TCP

基本特点

面向连接的可靠字节流服务。

建立连接——三次握手

TCP建立连接时,需要经历下图所示的三次握手过程。

三次握手

每一次握手都可以反映出不同的信息。

第一次握手

SYN标志提示TCP连接的服务端检查序列编号(seq),该序列编号为TCP连接初始端(一般是客户端)的初始序列编号。客户端通过SYN=1来告诉服务端它想要建立连接。SYN=1的报文段不携带数据,但是也要消耗一个序号。

seq是基于时钟生成一个序号,每4微秒加1,到2^32-1时又从0开始。tcp提供全双工服务,客户端和服务端都有各自的序号。编号是为了解决网络包乱序的问题

第一次握手成功后,服务器可以知道客户端的发送能力是正常的,服务器自己的接收能力是正常的

第二次握手

服务端用SYN=1和ACK=1来表示这条数据报是针对之前的连接请求的应答。

ack=m+1表示服务端已经收到了到m为止的报文,期待的下一个报文是m+1

第二次握手成功后,客户端知道了服务端的接收和发送能力正常,客户端自己的接收和发送能力是正常的

第三次握手

客户端发送确认包(ACK=1)进行应答,ack=n+1表示n为止的序号都收到了,下一次希望收到序号是n+1的报文。

seq=m+1表示当前序号时m+1,需要注意的是,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号所以下一个报文的序号还是m+1。

服务端检查ack的值以及ACK=1,接收成功,第三次握手完成。

这时,因为收到了正确的回传,说明第二次握手的信息客户端正确接收了,所以服务端知道了客户端的接收能力是正常的,同时也说明了服务器自己的发送能力是正常的。

至此,客户端和服务端都确定了自己和对方的发送、接收都是正常了,tcp连接建立完成。

问题:建立连接为什么一定要3次握手?两次可以吗

不可以。

  • 三次握手才可以阻止重复历史连接的初始化(主要原因)
  • 三次握手才可以同步双方的初始序列号
  • 三次握手才可以避免资源浪费

阻止历史连接

客户端连续发送多次 SYN 建立连接的报文,在网络拥堵等情况下:

  • 一个「旧 SYN 报文」比「最新的 SYN 」 报文早到达了服务端;
  • 那么此时服务端就会回一个 SYN + ACK 报文给客户端;
  • 客户端收到后可以根据自身的上下文,判断这是一个历史连接序列号过期或超时),那么客户端就会发送 RST 报文给服务端,表示中止这一次连接。

如果是两次握手,服务端在收到第一次握手时,就进入了连接状态,假设这次是历史连接,客户端判断到此次连接为历史连接,那么就会回 RST 报文来断开连接,而服务段在第一次握手的时候就进入 ESTABLISHED 状态,在向客户端发送数据前,并没有阻止掉历史连接,导致服务端建立了一个历史连接,又白白发送了数据,浪费资源。

断开连接——四次挥手

TCP 是全双工 的, 每个方向必须单独地进行关闭。断开连接时需要进行4次握手。

tcp四次挥手

第一次挥手

FIN=1表示要关闭连接,seq=x是当前报文序号。这次挥手表示客户端已经没有数据要发送了,想要断开连接。客户机进入终止等待1

第二次挥手

服务端收到第一次挥手的报文后,通知上层应用程序对方已经请求断开连接,并且回复给客户端一个ACK确认报文。服务器进入关闭等待状态,此时,服务器还可以继续发送那些尚未发送完成的数据,客户端也可以接收。客户端收到确认报文后进入终止等待2

第三次挥手

服务器发送完所有的数据后,发送一个FIN=1的报文表示自己可以断开连接了。因为第二次挥手后,服务器有可能又发送了一些数据,所以此时的seq是一个新的序列号z。服务器进入最终确认阶段。

第四次挥手

客户端收到服务器的FIN报文后,恢复一个ACK确认报文,服务器接到这个确认报文后直接关闭连接。而客户端在发出FIN报文后先是进入时间等待状态,经过**2MSL **(两倍的报文段最大存活时间,常用值有30秒、1分钟和2分钟) 的时间后,客户端也进入关闭状态。

至此,tcp连接关闭。

问题:为什么建立连接时3次握手就可以,而关闭时需要4次?

建立连接时,服务端收到第一次握手SYN请求后可以直接回复一个SYN+ACK报文,一方面确认了自己收到了客户端的连接请求,另一方面也表明自己也同意连接。

然而,关闭连接时,服务端收到客户端的FIN报文时,可能还有数据没有发送完,服务端不能立刻停止连接,它只能先发送一个确认报文给客户端。等到服务端的数据都发完了,才能向客户端发送FIN报文。所以需要4次握手。

问题:为什么要有2MSL的时间等待?

因为网络可能不稳定,导致第4次握手的ACK报文迟迟没有被服务器收到,服务器会一直超时重传FIN报文,如何客户端已经关闭,就无法处理了。所以客户端在送出第4次挥手的报文后,会等待一段时间,这段时间内没收到FIN报文,说明服务器已经收到了确认报文,客户端就可以关闭了。至于为什么时间设置成2MSL,可以这样理解:

1、如果客户机发送的第3次挥手报文不能顺利到达服务器,那么服务器那边等待的时间不可能比MSL更长,所以在一个MSL的时间内,服务器一定会重传一个FIN报文。

2、这个FIN报文最多存活MSL的时间,也就是说客户端一定会在一个MSL的时间内收到这个报文。

上面两个步骤加在一起,客户端最多等2MSL的时间,就可以确定能否关闭连接了。

参考文章

https://segmentfault.com/a/1190000022410446

https://zhuanlan.zhihu.com/p/40013850

https://juejin.cn/post/6844903685563105293

https://www.zhihu.com/question/271701044/answer/1279809269