TCP连接三次握手及关闭连接过程边界状况分析

传输控制协议(英语:Transmission Control Protocol,缩写为 TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层传输层所指定的功能,用户数据报协议(UDP)是同一层内另一个重要的传输协议。

TCP通过三次连接(three-way handshake)过程创建一个连接。在连接创建过程中,很多参数要被初始化,例如序号被初始化以保证按序传输和连接的强壮性。连接终止使用四次握手过程(four-way handshake),在这个过程中每个终端的连接都能独立地被终止。

由于多种原因,在实际的使用过程中,TCP连接在连接创建和关闭的过程当中可能出现边界状况。

三次握手

所谓三次握手(Three-Way Handshake)即建立TCP连接,就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立,整个流程如图所示:

《TCP连接三次握手及关闭连接过程边界状况分析》

一对终端同时初始化一个它们之间的连接是可能的。但通常是由一端打开一个套接字(socket)然后监听来自另一方的连接,这就是通常所指的被动打开(passive open)。服务器端被被动打开以后,用户端就能开始创建主动打开(active open)。

具体的连接过程为:

  1. 客户端通过向服务器端发送一个SYN来创建一个主动打开,作为三路握手的一部分。客户端把这段连接的序号设定为随机数A。

  2. 服务器端应当为一个合法的SYN回送一个SYN/ACK。ACK的确认码应为A+1,SYN/ACK包本身又有一个随机序号B。

  3. 最后,客户端再发送一个ACK。当服务端受到这个ACK的时候,就完成了三路握手,并进入了连接创建状态。此时包序号被设定为收到的确认号A+1,而响应则为B+1。

四次握手

四次握手(four-way handshake)即终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。

TCP是全双工模式,可以实现半关闭,就是说无论是客户端还是服务器端,可以关闭掉自己写的功能,但是仍然可以读对方发送给自己的数据。因此必须四次挥手,确认了两端都关闭了自己写的功能,没有数据会在此连接中进行传递了才能将连接关闭。

《TCP连接三次握手及关闭连接过程边界状况分析》

具体的终止过程为:

  1. 客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。

  2. 服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号。

  3. 服务器B关闭与客户端A的连接,发送一个FIN给客户端A。

  4. 客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。

过程中的变化

TCP状态转换
《TCP连接三次握手及关闭连接过程边界状况分析》

TCP时序转换
《TCP连接三次握手及关闭连接过程边界状况分析》

边界状况分析

1.被中断的系统调用

accept函数属于慢系统调用(slow system call),即那些可能永远阻塞的系统调用,即调用可能永远都无法返回。适用于慢系统调用的基本规则时:当阻塞与某个慢系统调用的一个进程捕获某个信号且相应信号处理函数返回时,该系统调用可能返回一个EINTR错误。

不过有些系统调用不存在这里的问题,这跟具体内核有关,有些内核会自动重启一些被中断的系统调用,但是也有一些内核不会自动重启,甚至有些系统调用会重启而有些不会,这跟sigaction系统调用设定的对捕获某信号所执行的操作中,是否有选择SA_RESTART标志有关。

2.accept返回前连接终止

这里假设一种情况,服务器调用socket、bind、listen后过了一会再调用accept,而客户端则在服务器端调用listen、accept之间的时间内调用了connect,并且在成功返回后调用了设置了SO_LINGER套接字的close对套接字进行关闭(这里不去讨论SO_LINGER如何设置,以及含义,只是说明一种需要注意的情况),但是发送了一个RST给服务器端,这种情况下服务器端调用accept会产生一个错误,POSIX规定返回的errno值必须为ECONNABORTED(“software caused connection abort”,软件引起的连接中止),对于这样的错误服务器只需要忽略并再次调用accept即可。

3.服务端进程终止

这和客户端关闭socket本质上没有太大区别,都是完成了四次挥手的一半,只是这里服务端是主动端,客户端是被动端,那么此时客户端同样需要感知服务器端关闭socket了。 此时的情况就比较复杂了,有这样几种情况,如果客户端继续读这个socket那么会返回0,客户端根据返回0就可以知道服务器端已经关闭socket了,这点和客户端主动关闭时一样的,但是如果客户端继续往这个socket写数据那么会发生什么情况呢(写入是成功的),此时服务器端会发送一个RST报文给客户端,然后客户端再次读取的时候就会返回错误,错误码是ECONNRESET(连接被重置)。

4.服务器崩溃

在服务器端和客户端正常通信的情况下,服务器此时奔溃(不是服务器端进程崩溃),此时客户端向服务器端发送数据会出现什么情况呢,发送完数据后再recv接收数据又会出现什么情况呢? 此时客户端向服务器端发送数据是可以发送成功的,你在客户端查看客户单的套接字的连接状态是ESTABLISHED的(服务器挂了,内核也没来得及发送FIN报文,如果是服务器端进程挂了,内核是负责发送FIN报文给客户端的) 但是当客户端通过recv接收数据的时候被阻塞了(数据虽然是发送成功了,但是内核始终没有接收到服务器端响应的ACK报文,所以内核会不断的进行数据重传,大概持续9分钟),recv阻塞一段时间后将会返回错误。错误码有几种情况:主机不可达(EHOSTUNRAEACH 或者是 ENETUNREACH)又或者是主机可达但是主机不响应(ETIMEDOUT)在服务器端崩溃的这种情况下客户端需要立即知道,如果上述情况中在服务器崩溃后没有再次写入数据的话,recv会一直阻塞。因为此时客户端根本不知道服务器崩溃了,所以存在两个问题,第一个问题就是如何在服务器崩溃后立即知道服务器崩溃了,而不是等再次写入数据的时候才知道,第二个问题是服务器崩溃接收数据阻塞时间太长(数据重传的时间),应该让recv具有超时机制,避免在这种情况下阻塞太长时间。

5.服务器崩溃后又重启

服务器崩溃重启后会丢失之前所有的连接信息,所以如果此时有客户端发来数据服务端会响应一个RST报文,此时客户端写入是成功,客户端的写入会导致服务端发送RST报文,那么此后客户端使用read/recv读取数据的时候就会返回0,表示服务器端连接被关闭了。如果客户端再次写入就会引发SIGPIPE信号的产生。这种情况和服务器端进程终止的情况是一样的。

6.服务器主动关机

如果是服务器主机关机的话,init进程是会向所有进程发送SIGTREM信号(可以捕捉),然后等待一段时间(5~20)会给仍然在运行的进程发送SIGKILL(这个信号无法捕捉)如果是服务器主机关机,那么子进程就会被信号杀死,导致内核发送FIN报文,此后的情况就和服务器端进程终止的情况是一样的。

小结

对于TCP了解在三次握手和四次握手时可能出现的边界情况的分析,总结出图所示的情况。

《TCP连接三次握手及关闭连接过程边界状况分析》

    原文作者:TCP
    原文地址: https://segmentfault.com/a/1190000007571652
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞