TCP协议的连接管理机制------三次握手,四次挥手

有关TCP协议的相关知识见:这篇博客

        TCP与UDP最大的区别就是TCP保证可靠性数据传输。从TCP与UDP的协议报头就可以看出差别。TCP的协议报头比UDP报头多了很多东西,而多出来的这些都是用于保证数据的可靠性传输的。下面将具体介绍TCP保证可靠传输的机制以及报头中的字段是如何用于可靠性传输机制的。

        TCP协议保证可靠性的一个重要机制就是连接管理机制。

1. 连接管理机制

        下图为客户端和服务器端根据TCP协议:三次握手建立连接,数据通信,四次挥手释放连接的示意图:




        上图中:

        三次握手:

(1)服务器程序先运行起来后,当调用listen设置监听套接字之后,进入LISTEN状态。然后调用accept阻塞等待来自客户端的连接请求;

(2)客户端再运行,分配文件描述符之后,调用connect向服务器发起连接请求,阻塞等待服务器的应答。发出带有SYN同步标志位的同步报文段,此时进入SYN_SENT状态;

(3)服务器收到客户端的SYN同步报文段后,将该连接放入内核等待队列中,并进入SYN_RECV状态。此时,服务器向客户端发出带有ACK标志位的确认报文段,确认收到连接请求,并等待客户端连接建立之后的确认信号;

(4)客户端收到服务器的ACK确认报文段之后,认为服务器同意建立连接,此时客户端进人ESTABLISH状态,并建立连接(将连接描述组织起来),然后向客户端发出连接已经建立的确认信号;

(5)服务器收到确认信号之后,知道客户端已经建立连接,此时服务器也进入ESTABLISH状态建立连接;并分配新的文件描述符connfd与客户端进行数据通信;

        此时,三次握手后,双方的连接建立成功。开始进行数据通信:

(6)然后服务器调用read阻塞等待来自客户端的数据请求;

(7)客户端调用write向服务器发出数据请求;

(8)服务器收到数据请求之后,从read返回,向客户端发出带有ACK的确认数据报,确认收到请求,然后对客户端的数据进行处理;

(9)客户端收到服务器的确认信号后,调用read阻塞等待服务器的数据响应信号;

(10)服务器处理完客户端的数据之后,向客户端发出响应数据报;

(11)客户端收到服务器的响应数据报之后,从read返回,并向服务器发出收到响应的确认信号;

(12)服务器收到确认信号之后,继续调用read阻塞等待来自客户端的数据请求;此时,循环(6)~(11);

        四次挥手断开连接:

(13)某一时刻,客户端不再发出数据请求,而是调用close关闭连接。并向服务器发出带有FIN标志位的结束报文段,此时,客户端进入FIN_WAIT_1状态,等待服务器的确认信号;

(14)服务器收到客户端的FIN结束报文段,知道客户端要关闭连接了,此时服务器进入CLOSE_WAIT状态,进行关闭连接前的一些后序操作,并向客户端发出带有ACK的确认报文段,表明他知道客户端要关闭连接了;

(15)客户端收到服务器的确认报文段之后进入FIN_TIME_2状态,等待服务器处理完后续操作和服务器的结束报文段;

(16)服务器处理完后续操作之后,调用close关闭连接,进入LASK_ACK状态,并向客户端发出关闭连接的FIN结束报文段,然后等待客户端的最后一个ACK确认报文段;

(17)客户端收到服务器端的结束报文段之后,向服务器发出ACK确认报文段,此时,进入TIMA_WAIT状态。

(18)服务器收到确认报文段之后,进入CLOSED状态彻底断开连接。

(19)客户端在TIME_WAIT之后等待一个2MSL后,进入CLOSED状态,彻底断开连接。

2. 为什么要进行三次握手,而不是两次握手或更多次握手?

        如果是两次握手,当服务器收到SYN同步报文段之后认为连接建立,然后会维护该连接,将该连接组织管理起来。此时就会进入ESTABLISHED状态,并发送ACK报文段给客户端,当客户端收到ACK后才会进入ESTABLISHED状态建立连接。如果ACK在传输过程中丢包,此时,客户端会认为连接没有建立,而重新向服务器发送重复的SYN报文段,服务器接收后会再次建立该连接。如果ACK一直丢失,客户端会一直发送SYN,服务器就会建立很多相同的无效的连接,从而使服务器资源浪费,使服务器受到影响。所以不能进行两次握手。

        如果进行三次握手,当客户端收到服务器的SYN+ACK报文后,会进入ESTABLISHED状态,并将该连接维护并建立。然后向服务器发送ACK报文段,当服务器接收到ACK之后,才会建立连接并进入ESTABLISHED状态。如果ACK在发送给服务器时丢失,此时,服务器认为连接没有建立,所以会重新发送SYN+ACK报文段。此时就变成了客户端会建立无效连接。但是客户端认为连接建立成功之后会向服务器发送数据请求。当送至服务器时,服务器认为连接并没有建立,所以会向客户端发送RST复位报文段要求重新建立连接。此时,客户端会重新发起连接请求,进而建立连接。在此过程中,虽然客户端会建立一些无效连接,会受到影响,但是服务器并没有受到影响,从而保证了服务器的安全。同时客户端虽然受到了影响,但随后会重新建立连接,所以会减小影响。

        所以,之所以进行三次握手,是因为:

(1)保证服务器的安全;

(2)虽然客户端会受影响,但随后会重建;

(3)三次握手的成功率很高,不需要更多次握手。多次握手还会浪费资源。

3. TIME_WAIT状态

        当我们启动服务器和客户端之后,用Ctrl+C终止服务器之后。在立即运行服务器,发现会出现绑定错误的消息:

        TCP协议规定,主动断开连接的一方会进入TIME_WAIT状态,等待一个2MSL时间之后才会进入CLOSED状态。所以,在服务器主动断开连接后会进入TIME_WAIT状态,此时进程虽然终止了,但TCP层的连接还没有完全断开,因此不能再次监听同样的端口号,所以会出现绑定错误的消息,待2MSL时间之后,才能绑定成功。

        MSL在Centos7上默认的时间是60s。

        TIME_WAIT之后为什么要等待2MSL才进入CLOSED状态?

        首先MSL是报文在网络中生存的最大时间。所以TIME_WAIT之后等待2MSL是因为:

(1)在两个传输方向上使尚未被接受或迟到的报文都消失。防止上一个连接的无效报文发给下一个连接;

(2)保证最后一个ACK报文可靠送达。如果最后一个ACK报文花费了MSL时间后丢失,此时,被关闭连接的而一端就会重新发送FIN报文段,可能也会花费MSL时间后才会送到。如果主动断开连接的一方在TIME_WAIT之后的2MSL时间内又收到了FIN报文,说明最后一个ACK报文丢失,此时可以在发送一个ACK报文给对端,再次等待2SL时间。如果在2MSL时间内没有收到FIN报文说明ACK安全送达,此时,直接关闭连接进入CLOSED状态即可。

4. 当服务器需要处理大量的客户端的连接时,而又可能很短客户端连接不活跃,服务器就会主动与这些客户端断开连接,从而形成大量的TIME_WAIT连接,导致服务器的端口号不够用,无法处理新的连接。

        所以使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1。表示允许穿件端口号相同但IP地址不同的socket描述符,使用时在server代码中的socket()和bind()之间插入如下代码:

int opt = 1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));












猜你喜欢

转载自blog.csdn.net/sandmm112/article/details/80484815