在TCP/IP协议取消连接的时候会进行四次挥手过程:
当某个应用进程主动关闭的时候,该端TCP会发送一个FIN分节,表示数据发送完毕。
接收到这个FIN的对端执行被动关闭,这个FIN由TCP进行确认,他的接受也作为一个文件结束符EOF传递给接收端应用进程,因为FIN的接受意味着接收端应用进程在相应连接上再无额外数据可以接受。
一段时间后,接受到这个文件结束符的应用进程将调用close关闭他的套接字,这导致他的TCP也发送一个FIN。
接受这个最终FIN的原发送端TCP,即执行主动关闭的那一端确认这个FIN。
一个FIN占据一个字节的序列号空间,因此每个FIN的ACK确认好就是这个FIN的序列号加一。
值得注意的是:
当套接字被关闭的时候,其所在端TCP各自发送了一个FIN。这是由应用进程调用close而发生的,但是必须知道。
当一个Unix进程无论是自愿的,即调用exit或从main函数返回,还是非自愿的(终止)时,所有打开的文件描述符都被关闭,这导致仍然打开的任何TCP连接上也发出一个FIN。<喎?http: www.2cto.com/kf/ware/vc/"="" target="_blank" class="keylink">vcD4NCjxoMSBpZD0="tcp四次挥手状态转换">TCP四次挥手状态转换
如果某个应用进程在接收到一个FIN之前调用close,即主动关闭,那就转换到FIN_WAIT _1状态,如果一个进程在close之前收到了一个FIN分节,即表示这个进程是被动关闭的,那就转换到CLOSE_WAIT状态。
如上面的图所示。
我们假设在这里主动关闭的进程为A,被动关闭的进程为B。
那么A首先给B发送一个FIN,然后A状态变为FIN_WAIT_1。
接着B接收到FIN,并给A进程返回一个ACK,然后B状态变为CLOSE_WAIT,如果,此时B进程close关闭进程,会给A进程发一个FIN分节,并且B进程状态变为LAST_ACK。
当A进程收到FIN后,A进程状态变为TIME_WAIT,然后A再给B发送ACK,此时进程B状态转换为CLOSED时。
此时连接取消成功。
经过上面对四次挥手过程的分析,我们可以得出结论:
TCP连接后处于TIME_WAIT状态,一定是发起了主动关闭,并且已经收到了对方的FIN,这时候进入了TIME_WAIT 状态。
TIME_WAIT状态的时间是2倍的MSL(最大生存时间),在TIME_WAIT状态TCP连接实际上已经断掉,但是该端口又不能被新的连接实例使用。这种情况一般都是程序中建立了大量的短连接,而操作系统中对使用端口数量做了限制,那么非常容易出现连接数占满的异常。
那么TIME_WAIT存在的意义是什么呢?
其实TIME_WAIT状态有两个存在的理由:
首先看第一个理由:
如果在四次挥手中,最终的ACK丢失,那么服务器将重新发送他最终的那个FIN,那么客户必须维护状态信息,以允许它重新发送最终那个ACK,要是客户不维护状态信息,它将相应以一个RST,该分解将被服务器解释成一个错误。
如果TCP打算执行所有必要的工作以彻底终止某个连接上两个方向的数据流,即全双关关闭,那么它必须正确处理连接终止序列4个分节中任何一个分节丢失的情况。
这也就说明了,为什么执行主动关闭的那一端是TIME_WAIT状态的那一端。
因为可能不得不重传最终那个ACK的就是那一端
现在看第二个理由
假设现在A,B有TCP连接,我们现在关闭这个连接,过一段时间后在相同的IP地址和端口之间建立另一个连接,这个连接成为前一个连接的化身,因为他们的IP地址和端口号都相同。
TCP必须防止来自某个连接的老的重复分组在次连接已终止后重现,即防止上一个连接的数据发送在当前连接中。
所以TCP将不给处于TIME_WAIT状态的连接发起新的化身。
这个时候,MSL的作用就来了
既然TIME_WAIT状态的持续时间是2MSL,这足以让某个方向上的分组最多存活MSL秒及被丢弃,另一个方向也存活MSL秒丢弃,然后老连接中的分组就会消逝。
在写一个unix server程序时,经常需要在命令行上重启它,绝大多数时候工作正常,但是某些时候会报告”bind: address in use”,于是重启失败。
上面那个就是地址reuse问题,是就是由于TIME_WAIT状态产生的,那么我们如何解决这个问题呢?
使用setsockopt函数
int option = 1; if (setsockopt ( masterSocket, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option) ) < 0) { die( "setsockopt" ); }
那么编写 TCP/SOCK_STREAM 服务程序时,SO_REUSEADDR到底什么意思?
这个套接字选项通知内核,如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用端口。如果端口忙,而TCP状态位于其他状态,重用端口时依旧得到一个错误信息,指明”地址已经使用中”。如果你的服务程序停止后想立即重启,而新套接字依旧使用同一端口,此时SO_REUSEADDR 选项非常有用。必须意识到,此时任何非期望数据到达,都可能导致服务程序反应混乱,不过这只是一种可能,事实上很不可能。
一个套接字由相关五元组构成,协议、本地地址、本地端口、远程地址、远程端口。SO_REUSEADDR 仅仅表示可以重用本地本地地址、本地端口,整个相关五元组还是唯一确定的。所以,重启后的服务程序有可能收到非期望数据。必须慎重使用SO_REUSEADDR 选项。
SO_REUSEADDR用于对TCP套接字处于TIME_WAIT状态下的socket,才可以重复绑定使用。server程序总是应该在调用bind()之前设置SO_REUSEADDR套接字选项。TCP,先调用close()的一方会进入TIME_WAIT状态
如对本文有疑问, 点击进行留言回复!!
tensorflow, keras, Tokenizer 获取文本信息, NLP
网友评论