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

关闭socket链接过程中的TCP状态:TIME_WAIT状态(开启地址重用),CLOSE_WAIT状态

2013-04-23 09:06 716 查看
关闭链接过程中的TCP状态和SOCKET处理,及可能出现的问题:

1、 TIME_WAIT

TIME_WAIT 是主动关闭 TCP 连接的那一方出现的状态,系统会在
TIME_WAIT 状态下等待 2MSL(maximum segment lifetime )后才能释放连接(端口)。通常约合 4 分钟以内。

TIME_WAIT
状态等待 2MSL(MSL(最大分段生存期)指明TCP报文在Internet上最长生存时间,每个具体的TCP实现都必须选择一个确定的MSL值)
的意义:

a、确保连接可靠地关闭; 即防止最后一个ACK丢失。

b、避免产生套接字混淆(同一个端口对应多个套接字)。

为什么说可以用来避免套接字混淆呢?

一方close发送了关闭链接请求,对方的应答迟迟到不了(例如网络原因),导致TIME_WAIT超时,此时这个端口又可用了,我们在这个端口上又建立了另外一个socket链接。 如果此时对方的应答到了,怎么处理呢?其实这个在TCP层已经处理了,由于有TCP序列号,所以内核TCP层,就会将包丢掉,并给对方发包,让对方将sockfd关闭。所以应用层是没有关系的。即我们用socket
API编写程序,就不用处理。

注意::

TIME_WAIT是指操作系统的定时器会等2MSL,而主动关闭sockfd的一方,并不会阻塞。(但是如果开启tcp的SO_LINGER标志,要在close时将buf中的数据发送完毕,那么close可能会阻塞)。

当主动方关闭sockfd后,对方可能不知道这个事件。那么当对方(被动方)写数据,即send时,将会产生错误,即errno为: ECONNRESET。

服务器产生大量 TIME_WAIT 的原因:(一般我们不这样开发Server,但是web服务器等这种多客户端的Server,是需要在完成一次请求后,主动关闭连接的,否则可能因为句柄不够用,而造成无法提供服务。)

服务器存在大量的主动关闭操作,需关注程序何时会执行主动关闭(如批量清理长期空闲的套接字等操作)。

一般我们自己写的服务器进行主动断开连接的不多,除非做了空闲超时之类的管理。(TCP短链接是指,客户端发送请求给服务器,客户端收到服务器端的响应后,关闭链接)。

问题一: 如何避免等待60秒之后才能重启服务

方法:使用setsockopt,比如

--------------------------------------------------------------------------

int option = 1;

if ( setsockopt ( masterSocket, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(
option ) ) < 0 )

{

die( "setsockopt" );

}

--------------------------------------------------------------------------

这个作用类似:

#让TIME_WAIT状态可以重用,这样即使TIME_WAIT占满了所有端口,也不会拒绝新的请求造成障碍

echo "1" > /proc/sys/net/ipv4/tcp_tw_reuse

#让TIME_WAIT尽快回收,我也不知是多久,观察大概是一秒钟

echo "1" > /proc/sys/net/ipv4/tcp_tw_recycle

本地可以使用的端口有:

cat /proc/sys/net/ipv4/ip_local_port_range

用这条命令会返回两个数字,默认是:32768 61000,说明这台机器本地能向外连接61000-32768=28232个连接,注意是本地向外连接,不是这台机器的所有连接。

本机上listen监听的端口尽量设置在ip_local_port_range之外,否则可能因为loop connect而发生错误。

问题二: 编写
TCP/SOCK_STREAM 服务程序时,SO_REUSEADDR到底什么意思?

解释: 这个套接字选项通知内核,如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用端口。如果端口忙,而TCP状态位于其他状态,重用端口时依旧得到一个错误信息, 指明"地址已经使用中"。如果你的服务程序停止后想立即重启,而新套接字依旧使用同一端口,此时
SO_REUSEADDR 选项非常有用。必须意识到,此时任何非期望数据到达,都可能导致服务程序反应混乱,不过这只是一种可能(重用端口,但主动关闭放的最后一个ACK丢失,被动关闭放重传FIN包,可能影响新建立的连接)。

一个套接字由相关五元组构成,协议、本地地址、本地端口、远程地址、远程端口。SO_REUSEADDR 仅仅表示可以重用本地地址、本地端口,整个相关五元组还是唯一确定的。所以,重启后的服务程序有可能收到非期望数据。必须慎重使用
SO_REUSEADDR 选项。

问题三: 在客户机/服务器编程中(TCP/SOCK_STREAM),如何理解TCP自动机
TIME_WAIT 状态?

解释:下面我来解释一下 TIME_WAIT 状态,MSL(最大分段生存期)指明TCP报文在Internet上最长生存时间,每个具体的TCP实现都必须选择一个确定的MSL值。RFC
1122建议是2分钟,但BSD传统实现采用了30秒。

TIME_WAIT 状态最大保持时间是2 * MSL,也就是1-4分钟。

IP头部有一个TTL,最大值255。尽管TTL的单位不是秒(根本和时间无关),我们仍需假设,TTL为255的TCP报文在Internet上生存时间不能超过MSL。

TCP报文在传送过程中可能因为路由故障被迫缓冲延迟、选择非最优路径等等,结果发送方TCP机制开始超时重传。前一个TCP报文可以称为"漫游TCP重复报文",后一个TCP报文可以称为"超时重传TCP重复报文",作为面向连接的可靠协议,TCP实现必须正确处理这种重复报文,因为二者可能最终都到达。

一个通常的TCP连接终止可以用图描述如下:

client server

FIN M

close -----------------> (被动关闭)

ACK M+1

<-----------------

FIN N

<----------------- close

ACK N+1

----------------->

问题四:为什么需要
TIME_WAIT 状态?

解释:假设最终的ACK丢失,被动关闭放将重发FIN,主动关闭放必须维护TCP状态信息以便可以重发最终的ACK,否则会发送RST,结果server认为发生错误。TCP实现必须可靠地终止连接的两个方向(全双工关闭),client必须进入
TIME_WAIT 状态,因为client可能面临重发最终ACK的情形。

{

scz 2001-08-31 13:28

先调用close()的一方会进入TIME_WAIT状态

}

此外,考虑一种情况,TCP实现可能面临先后两个同样的相关五元组。如果前一个连接处在 TIME_WAIT 状态,而允许另一个拥有相同相关五元组的连接出现,可能处理TCP报文时,两个连接互相干扰。使用
SO_REUSEADDR 选项就需要考虑这种情况。

问题五:为什么
TIME_WAIT 状态需要保持 2MSL 这么长的时间?

如果 TIME_WAIT 状态保持时间不足够长(比如小于2MSL),第一个连接就正常终止了。 第二个拥有相同相关五元组的连接出现,而第一个连接的重复报文到达(最后的ACK没有被对端收到,被动关闭放重传FIN包),干扰了第二个连接。TCP实现必须防止某个连接的重复报文在连接终止后出现,所以让TIME_WAIT状态保持时间足够长(2MSL),连接相应方向上的TCP报文要么完全响应完毕,要么被丢弃。建立第二个连接的时候,不会混淆。

2、 CLOSE_WAIT

CLOSE_WAIT 是被动关闭 TCP 连接时产生的,

如果收到另一端关闭连接的请求后,本地(Server端)不关闭相应套接字就会导致本地套接字进入这一状态。

(如果对方关闭了,没有收到关闭链接请求,就是下面的不正常情况)

按TCP状态机,我方收到FIN,则由TCP实现发送ACK,因此进入CLOSE_WAIT状态。但如果我方不执行close(),就不能由CLOSE_WAIT迁移到LAST_ACK,则系统中会存在很多CLOSE_WAIT状态的连接。

如果存在大量的 CLOSE_WAIT,则说明客户端并发量大,且服务器未能正常感知客户端的退出,也并未及时 close
这些套接字。(如果不及时处理,将会出现没有可用的socket描述符的问题,原因是sockfd耗尽)。

正常情况下::

一方关闭sockfd,另外一方将会有读事件产生, 当recv数据时,如果返回值为0,表示对端已经关闭。此时我们应该调用close,将对应的sockfd也关闭掉。

不正常情况下::

一方关闭sockfd,另外一方并不知道,(比如在close时,自己断网了,对方就收不到发送的数据包)。此时,如果另外一方在对应的sockfd上写send或读recv数据。

recv时,将会返回0,表示链接已经断开。

send时, 将会产生错误,errno为ECONNRESET。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: