huanzhiyazi / articles

我的原创文章,包括且不限于技术 blog,历史,文学写作,日常心得等
103 stars 32 forks source link

技术 / 网络 / 理解TCP三次握手和四次挥手 #26

Open huanzhiyazi opened 4 years ago

huanzhiyazi commented 4 years ago

目录



1 三次握手建立连接[Top]

关于 TCP 为什么需要三次握手建立连接、四次挥手断开连接,网上有很多标准的答案,很多讲的都非常不错。这里总结一下三次握手四次挥手中我认为最能帮助理解的原因分析。

先看一下三次握手建立连接的图示:

TCP connect

为什么需要三次握手呢?关键的原因是 通信双方为了相互确认对方收发信息的能力都是正常的。这也是 TCP 建立连接的目的。具体而言:

  1. 客户端先向服务器发送 SYN 报文,如果服务器收到之后,便能确认客户端发送信息能力正常,但此时还不确定客户端接收信息能力是否正常。
  2. 然后,服务器返回给客户端 SYN+ACK 报文,SYN 使得客户端确认服务器发送信息能力正常,ACK 使得客户端确认服务器接收信息能力正常,因为这个 ACK 是服务器对上一步客户端发送 SYN 的一个确认。到这里,客户端已经完全确认了服务器收发信息都是正常的。
  3. 客户端最后再回复一个 ACK 报文给服务器,作为上一步服务器发送给客户端的 SYN 的一个确认。服务器收到该 ACK 之后便终于确认客户端接收信息的能力也是正常的。到这里,服务器已经完全确认了客户端收发信息都是正常的。

所以,经过以上三步,客户端和服务器都完全确认对方收发信息的能力都是正常的,连接建立成功,可以正常传输数据了。



2 四次挥手断开连接[Top]

先看一下四次挥手断开连接的图示:

TCP disconnect

需要四次挥手断开连接的关键原因是,通信双方为了相互确认对方不再发送数据给自己了。为什么只需要确认不再发送信息而不需要确认接收信息呢?因为接收信息是对方自己可以完全处理的事情,不需要自己关心,而对方如果还需要发送信息,那必须等待对方发完,否则最终自己接收到的信息可能就是不完整的。

具体而言:

  1. 客户端首先执行系统调用 close() 关闭连接,向服务器发送 FIN 报文。这时客户端不再发送数据,只读取未读完的缓存数据。服务器收到 FIN 报文,便确认客户端已经不再发送数据过来了。客户端接下来需要确认服务器不再发送数据了,等待服务器发送 FIN 报文过来,进入 <FIN_WAIT1> 状态。
  2. 服务器向客户端发送 ACK 报文,告诉客户端我已经知道你不再发送数据过来了,但服务器可能还有数据没有发送完,所以这一步只向客户端发送 ACK 确认报文,而不捎带 FIN。客户端知道服务器已经知道自己不再发送数据了,但是服务器仍然需要时间处理完未发完的数据,所以仍然需要等待服务器发送 FIN 过来,进入 <FIN_WAIT2> 状态,即等待服务器 FIN 报文过来的第二个阶段。
  3. 服务器发送完所有数据后,执行系统调用 close(),并向客户端发送 FIN 报文,告诉客户端,我也不再发送数据了。但是服务器仍然需要确认客户端已经知道自己不再发送数据,所以进入 <LAST_ACK> 状态,即等待最后确认状态。
  4. 客户端收到来自服务器的 FIN 报文,知道服务器也不再发送数据了,于是进入 <TIME_WAIT> 状态,在这个状态下,需要等待 2MSL 时间(2个最大报文生存时间)后最终进入 <CLOSE> 状态。同时向服务器发送最后一个 ACK 确认报文。

为什么客户端最后需要在 <TIME_WAIT> 状态等待 2MSL 的时间呢?因为网络是不可靠的,最后一个 ACK 报文可能丢失到不了服务器。服务器在发送完最后一个 FIN 之后,如果在超时时间内没有收到客户端的 ACK,会重发一次 FIN 给客户端,客户端会再重发一个 ACK 报文并重新计时。如果客户端不等待 2MSL 直接进入关闭状态,那么服务器可能永远收不到最后的 ACK 报文了,也就无法进入关闭状态了。