您的位置:首页 > 理论基础 > 计算机网络

网络协议 -- TCP协议(3)TIME_WAIT及MSL

2018-01-30 15:56 731 查看
关于TCP的4次挥手过程参考:网络协议 – TCP协议(2)三次握手与四次挥手

一、TIME_WAIT状态为何存在?

这里我们不使用“客户端”、“服务端”来表示TCP连接的2端,转而使用“主动断开连接端”、“被动断开连接端”来表示TCP通讯的2端。因为执行主动断开连接的端可能是服务端也可能是客户端(虽然我们大多数情况下遇到的是客户端执行主动断开)。

在“主动断开连接端”收到了“被动断开连接端”发来的
LAST_ACK
之后,会给“被动断开连接端”回复一个
ACK
确认消息。但这个时候为了确保“被动断开连接端”有足够的时间能够收到该消息,“主动断开连接端”不能马上关闭socket,需要等待一定的时间来确保“被动断开连接端”可以收到
ACK
确认消息。“主动断开连接端”在等待的这个时间段内的状态我们称之为
TIME_WAIT
状态。

归纳为一句话就是:TIME_WAIT状态就是“主动断开的一方”在发送完最后一次ACK后进入的等待状态。

二、等待时间

那么
TIME_WAIT
状态需要持续多久了,也就是“主动断开连接端”在发送完最后一个ACK之后需要等待多久了?

《TCP/IP详解 卷1:协议》中提到:默认TIME_WAIT的超时时间是2倍的MSL。MSL是
Maximum Segment Lifetime
的缩写,表示报文的最大生存时间,这个时间和系统的TCP实现有关,每个系统是不一样的。

2.1 windows系统MSL

注册表
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
下的
TcpTimedWaitDelay
键(如果没有可以新建一个)就对应了
2*MSL
(2倍的MSL)的值。



2.2 Linux系统MSL

以CentOS为例(摘自网络,仅供参考):

查看默认的MSL值(60s):

cat /proc/sys/net/ipv4/tcp_fin_timeout


修改默认为120:

echo 120 > /proc/sys/net/ipv4/tcp_fin_timeout


修改完成后,重新加载配置文件:

sysctl -p /etc/sysctl.conf


查看是否已经生效:

sysctl -a | grep fin


三、SO_REUSEADDR

如果进程中的某个TCP连接处于
TIME_WAIT
等待状态,因为这个等待时间比较长,在这期间该连接使用的端口将一直被占用。

如果一个服务端进程(绑定了某个端口)退出(正常退出或异常退出)后,立即启动一个新的该进程,可能由于Windows系统对端口的释放不及时,导致这个端口还没有被释放,不能被再次绑定,从而导致新进程绑定端口失败。

那么遇到上面的问题如何解决了?

我们在网络编程中经常设置的
SO_REUSEADDR
选项就可以解决这个问题,

int flag = 1;
setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char*>(&flag), sizeof(flag));


SO_REUSEADDR
提供如下四个功能:


SO_REUSEADDR允许启动一个监听服务器并捆绑其众所周知端口,即使以前建立的将此端口用做他们的本地端口的连接仍存在。这通常是重启监听服务器时出现,若不设置此选项,则
bind
时将出错。

SO_REUSEADDR允许在同一端口上启动同一服务器的多个实例,只要每个实例捆绑一个不同的本地IP地址即可。对于TCP,我们根本不可能启动捆绑相同IP地址和相同端口号的多个服务器。

SO_REUSEADDR允许单个进程捆绑同一端口到多个套接口上,只要每个捆绑指定不同的本地IP地址即可。这一般不用于TCP服务器。

SO_REUSEADDR允许完全重复的捆绑:当一个IP地址和端口绑定到某个套接口上时,还允许此IP地址和端口捆绑到另一个套接口上。一般来说,这个特性仅在支持多播的系统上才有,而且只对UDP套接口而言(TCP不支持多播)。

其中第一个功能就可以用来解决该问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: