Linux(二十四)TCP的三次握手与四次挥手

三次握手

这里写图片描述

服务器在开始的时候,调用socket()函数分配一个文件描述符,然后是填充本地sockaddr_in结构体信息,绑定分配的文件描述符和服务器地址端口,下面就开始建立监听描述符,调用listen函数,使刚才的文件描述符成为一个监听描述符,
然后就是阻塞等待客户端连接;
客户端做的工作就是分配一个文件描述符,填充sockaddr_in信息,然后调用connect函数向服务器发起连接请求,阻塞等待服务器应答

客户端发起请求,发送SYN同步报文段给服务器;
服务器收到请求后,发送一个ACK应答,同时也发送一个SYN同步报文段;
客户端收到响应后,发送一个ACK应答,由此双方通信开始

TCP协议是面向连接的,开始的时候客户端TCP处于SYN_SENT请求连接状态,服务器端TCP处于SYN_RCVD等待连接状态,当客户端接收到服务器的ACK应答,并且给服务器端发送ACK应答,此时客户端服务器都变成ESTABLISHED,即连接成功状态

四次挥手

这里写图片描述

客户端主动调用close时,就会向服务器发送结束报文段FIN,同时进入FIN_WAIT_1状态,服务器收到了FIN结束报文段,进入CLOSE_WAIT状态,处理完之前的数据后,调用close,向客户端发送FIN,此时服务器处于LAST_ACK状态,等待最后一个ACK到来,当客户端收到服务器发送的FIN后,进入TIME_WAIT状态,然后向服务器发送ACK确认,客户端要等待2MSL时间才会进入CLOSED状态,服务器收到了FIN的ACK就彻底关闭

理解TIME_WAIT状态

我们先启动server,然后启动client,然后用Ctrl-c使server终止,这时马上运行server,结果是
这里写图片描述
这里写图片描述

这里的绑定失败原因就是服务器正处于TIME_WAIT状态,此端口号已经被占用,所以不能绑定成功,我们用netstat命令查看一下

这里写图片描述

*TCP协议规定,主动断开连接的一方要处于TIME_WAIT状态,等待两个MSL的时间之后才能回到CLOSED状态。
*我们用Ctrl+c终止了server,所以server是主动断开的乙方,在TIME_WAIT期间内不能鉴听同样的server端口;
*MSL在RFC1122中规定为两分钟,但是各操作系统的实现不同,在Centos7上默认的是60s;
*可以通过

cat /proc/sys/net/ipv4/tcp_fin_timeout

查看msl的值

这里写图片描述

为什么TIME_WAIT的时间是2MSL呢?
*MSL是TCP报文的最大生存时间,因此TIME_WAIT持续存在2MSL的话就可以保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启,可能会受到来自上一个进程的迟到的数据,但是这种数据很可能是有问题的);
*同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失,那么服务器再重发一个FIN,这是虽然客户端的进程不在了,但是TCP连接还在,仍然可以重发LAST_ACK);

如何解决TIME_WAIT状态引起的bind失败
如果服务器需要处理非常大量的客户端连接,这个时候如果由服务器端主动关闭连接,就会产生大量的TIME_WAIT连接,由于我们的请求量很大,就可能导致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/mignatian/article/details/80771395