传输层--TCP的连接管理(三次握手与四次挥手)

我们知道TCP是一种保证可靠性的传输层协议,TCP可靠性保证的重要支撑就是TCP的连接管理机制,也就是三次握手与四次挥手。

建立连接

例如:在日常生活中我们经常会打电话,下面是小A和小B通话过程:
这里写图片描述
当小A给小B打电话时,小A不确定小B是否听的见,那么小A就会询问小B是否听的见,小B回复之后不确定小A是否听得见,就会询问小A,当小B收到小A得确认之后认为小A能够听见,此时双方都能够确定对方听见自己的声音,那么双方就可以进行通话了,这就类似于TCP建立连接的过程。

TCP连接管理各种状态解释

1.CLOSED:关闭
2.SYN_SENT:同步发送
3.SYN_RECVD:同步接收
4.LISTEN:监听
5.ESTABLISHED:已建立连接
6.FIN_WAIT:终止等待
7.CLOSE_WAIT:关闭等待
8.TIME_WAIT:时间等待

三次握手

这里写图片描述
主动建立连接的一方称之为客户端,被动的那一方称之为服务器。
三次握手由客户端发起。
(1)第一次握手:客户端向服务器发送一个连接请求报文,这个报文的首部里SYN标志位被置为1,这时客户端就进入SYN_SENT状态。
(2)第二次握手,当服务器收到客户端发送的连接请求报文后,如果服务器同意建立连接,则会发送一个确认报文,该确认报文里面SYN和ACK标志位都被置为1,这是服务器就进入SYN_RECVD状态。
(3)第三次握手:当客户端收到服务器发来的请求确认报文,客户端就向服务器发送一个确认报文,里面ACK标志位被置为1,这时客户端连接建立成功,客户端进入ESTABLISHED状态,当服务器收到客户端发来的确认报文,服务器认为连接已经建立成果,服务器也进入ESTABLISHED状态。

四次挥手

建立连接是由客户端发起的,而断开连接双方都可以发起,下图是客户端主动断开连接的过程图。
1.如果客户端主动断开连接:
这里写图片描述

(1)第一次挥手:当客户端没有任何数据向服务器发送时,客户端主动断开连接。客户端发送一个请求断开连接的报文,里面FIN标志位被置为1,这是客户端就进入FIN_WAIT1状态。
(2)第二次挥手:当服务器收到客户端发送来的请求断开连接的报文,服务器会对该报文进行确认,就会发送一个确认报文给客户端,该报文的ACK标志位被置为1,这时服务器就进入CLOSE_WAIT状态。当客户端收到服务器发来确认报文后,客户端就进入FIN_WAIT2状态,此时客户端到服务器的通道就被关闭,客户端不能向服务器发送数据,但是,服务器到客户端的通道并没有关闭,服务器仍然可以向客户端发送数据,客户端也会对发来的数据进行确认,此时就处于半关闭状态。
(3)第三次挥手:当服务器不用向客户端发送数据的时候,服务器就会向客户端发送一个请求断开连接报文,表示服务器到客户端的通道也要关闭了,这时服务器就进入LAST_ACK状态,等待最后一个ACK的到来。
(4)第四次挥手:当客户端收到服务器发来的请求断开连接报文后,客户端会向服务器发送一个确认报文,随即进入TIME_WAIT状态,并且会等待2MSL(MSL:最大报文生存时间),如果在2MSL里面服务器没有收到客户端发来的ACK,那么服务器就会重新发送一个FIN报文,此时客户端又会发送一个ACK报文并会重新等待2MSL.如果服务器收到客户端发送的ACK报文,此时服务器就会进入CLOSED状态,2MSL之后,客户端也进入CLOSED状态。可以看到服务器比客户端先进入CLOSED状态。
2.如果双方同时断开连接:
这里写图片描述
可以看到:双方同时断开连接,此时会出现CLOSING状态。

三次握手与四次挥手过程中状态转换和调用接口的对应过程

1.三次握手
这里写图片描述
2.四次挥手
这里写图片描述
注意:主动断开连接的一方会进入TIME_WAIT状态,被动断开连接的一方会进入CLOSE_WAIT状态。

TIME_WAIT状态

1.主动断开连接的一方会进入TIME_WAIT状态。

2.为什么进入TIME_WAIT状态会等待2倍的MSL?

(1)为了保证最后一个ACK能够被对方接收到。以客户端主动断开连接为例,因为这个ACK报文有可能会丢失,那么就会使得处于LAST_ACK状态的服务器无法得到对FIN报文的确认,则服务器就会进行重传,重新向客户端发送FIN报文,那么客户端就再次发送ACK然后重新等待2MSL,最后客户端和服务器进入CLOSED状态。如果客户端没有等待2MSL直接释放连接,那么最后一个ACK丢了,服务器会进行重传FIN,客户端就不会收到服务器重传的FIN报文,服务器就无法按照正常步骤进入CLOSED状态。
(2)另一方面,因为MSL是报文的最大生存周期,从客户端到服务器发送的报文的最大生存周期为MSL,从服务器向客户端发送的报文的最大生存周期也为MSL,那么从客户端到服务器往返路程中,报文的最大生存周期不超过2MSL,等待2MSL就会使得从客户端到服务器这条双向通信道路里所有的报文都会消失,那么下次重新连接时就不会收到上次发送的报文,就不会出现收到非法数据包。

3.服务器出现大量的TIME_WAIT状态,导致绑定失败的问题

(1)如果服务器主动断开连接,那么服务器就会进入TIME_WAIT状态,那么服务器就会等待2MSL,此时再启动服务器就会出现bind(绑定)错误,因为服务器等待2MSL过程中,连接还没有完全断开,此时端口号就不够用,无法处理新的连接。
(2)因此通过setsockopt函数,将可以对端口号进行重用。
函数原型如下:

int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);

使用:level表示设置的层级,这里是SOCKET层,optname表示相关操作设置为SO_REUSEADDR表示端口号重用。

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

为什么建立连接是三次握手,断开连接是四次挥手

1.为什么建立连接是三次握手?

(1)一方面,因为双方都要对对方发送的数据进行确认,那么最少都需要两次握手,如果是两次握手的话,就类似于下面这张图,当服务器向客户端发送一个ACK后,服务器会认为连接已经建立完成,服务器就会进入ESTABLISHED状态,如果一个ACK丢了,服务器认为连接建立好了,服务器就会为此维护相应的资源用来管理这个连接,而客户端认为连接没有成功建立,所以这个连接无意义,但是服务器会一直维持这个连接,就会浪费服务器的资源。
这里写图片描述

对于三次握手而言,如果最后一个ACK丢了,服务器认为连接没有成功建立,虽然客户端认为连接已经成功建立,但是对服务器没有太大消耗,服务器收不到ACK就会重新发送建立连接请求。
因为三次握手已经可以满足需求了,所以不需要更多次的握手。
(2)另一方面,建立连接是双方的事情,你要向我建立连接,我也要向你建立连接,所以双方都需要向对方请求建立连接,然后双方互相确认,看起来像四次握手,因为TCP能够捎带应答,所以服务器向客户端请求建立连接的SYN就可以乘着对客户端的ACK一起发送,所以就将四次握手合并成了三次握手。

2.为什么断开连接是四次挥手?

(1)同样断开连接也是双方的事情,你要向我断开连接,我也要向你断开连接,双方还要进行确认,所以是四次挥手,但是服务器的ACK和FIN却不能合并,所以无法是三次挥手。
(2)为什么断开连接时,服务器对客户端的FIN和ACK无法合并?
①因为如果客户端先断开连接,只是表明客户端没有数据要发送给服务器了,但是服务器有可能还会向客户端发送数据,那么服务器向客户端发送ACK之后到服务器发送FIN之间(即CLOSE_WAIT状态到LAST_ACK之间)会有很长的时间间隔,那么这两个步骤就不能合并。
②另一方面,服务器发送FIN进入LAST_ACK状态是由用户调用close函数实现的,而服务器发送ACK是由内核实现的也说明这两步不能合成一步,但是三次握手中建立连接和确认都是由内核实现所以可以合成一步。

猜你喜欢

转载自blog.csdn.net/xu1105775448/article/details/81022483