TCPIP详解--TIME_WAIT状态详解

TCPIP详解--TIME_WAIT状态详解
Socket中的TIME_WAIT状态
在⾼并发短连接的server端,当server处理完client的请求后⽴刻closesocket此时会出现time_wait状态然后如果client再并发2000个连接,此时部分连接就连接不上了,⽤linger强制关闭可以解决此问题,但是linger会导致数据丢失,linger值为0时是强制关闭,⽆论并发多少多能正常连接上,如果⾮0会发⽣部分连接不上的情况!(可调⽤setsockopt设置套接字的linger延时标志,同时将延时时间设置为0。)
TCP/IP的RFC⽂档。TIME_WAIT是TCP连接断开时必定会出现的状态。
是⽆法避免掉的,这是TCP协议实现的⼀部分。
在WINDOWS下,可以修改注册表让这个时间变短⼀些
time_wait的时间为2msl,默认为4min.
你可以通过改变这个变量:
TcpTimedWaitDelay
把它缩短到30s
TCP要保证在所有可能的情况下使得所有的数据都能够被投递。当你关闭⼀个socket时,主动关闭⼀端的socket将进⼊TIME_WAIT状态,⽽被动关闭⼀⽅则转⼊CLOSED状态,这的确能够保证所有的数据都被传输。当⼀个socket关闭的时候,是通过两端互发信息的四次握⼿过程完成的,当⼀端调⽤close()时,就说明本端没有数据再要发送了。这好似看来在握⼿完成以后,socket就都应该处于关闭CLOSED状态了。但这有两个问题,⾸先,我们没有任何机制保证最后的⼀个ACK能够正常传输,第⼆,⽹络上仍然有可能有残余的数据包(wandering duplicates),我们也必须能够正常处理。
通过正确的状态机,我们知道双⽅的关闭过程如下
假设最后⼀个ACK丢失了,服务器会重发它发送的最后⼀个FIN,所以客户端必须维持⼀个状态信息,以便能够重发ACK;如果不维持这种状态,客户端在接收到FIN后将会响应⼀个RST,服务器端接收到RST后会认为这是⼀个错误。如果TCP协议能够正常完成必要的操作⽽终⽌双⽅的数据流传输,就必须完全正确的传输四次握⼿的四个节,不能有任何的丢失。这就是为什么socket在关闭后,仍然处于
TIME_WAIT状态,因为他要等待以便重发ACK。
如果⽬前连接的通信双⽅都已经调⽤了close(),假定双⽅都到达CLOSED状态,⽽没有TIME_WAIT状态时,就会出现如下的情况。现在有⼀个新的连接被建⽴起来,使⽤的IP地址与端⼝与先前的完全相同,后建⽴的连接⼜称作是原先连接的⼀个化⾝。还假定原先的连接中有数据报残存于⽹络之中,这样新的连接收到的数据报中有可能是先前连接的数据报。为了防⽌这⼀点,TCP不允许从处于TIME_WAIT状态的socket建⽴⼀个连接。处于TIME_WAIT状态的socket在等待两倍的MSL时间以后(之所以是两倍的MSL,是由于MSL是⼀个数据报在⽹络中单向发出到认定丢失的时间,⼀个数据报有可能在发送图中或是其响应过程中成为残余数据报,确认⼀个数据报及其响应的丢弃的需要两倍的MSL),将会转变为CLOSED状态。这就意味着,⼀个成功建⽴的连接,必然使得先前⽹络中残余的数据报都丢失了。
美味关系 电影由于TIME_WAIT状态所带来的相关问题,我们可以通过设置SO_LINGER标志来避免socket进⼊TIME_WAIT状态,这可以通过发送RST⽽取代正常的TCP四次握⼿的终⽌⽅式。但这并不是⼀个很好的主意,TIME_WAIT对于我们来说往往是有利的。
nsis客户端与服务器端建⽴TCP/IP连接后关闭SOCKET后,服务器端连接的端⼝
状态为TIME_WAIT
是不是所有执⾏主动关闭的socket都会进⼊TIME_WAIT状态呢?
有没有什么情况使主动关闭的socket直接进⼊CLOSED状态呢?
主动关闭的⼀⽅在发送最后⼀个 ack 后
就会进⼊ TIME_WAIT 状态 停留2MSL(max segment lifetime)时间
这个是TCP/IP必不可少的,也就是“解决”不了的。
u20战场
也就是TCP/IP设计者本来是这么设计的
主要有两个原因
1。防⽌上⼀次连接中的包,迷路后重新出现,影响新连接
(经过2MSL,上⼀次连接中所有的重复包都会消失)
2。可靠的关闭TCP连接
在主动关闭⽅发送的最后⼀个 ack(fin) ,有可能丢失,这时被动⽅会重新发
fin, 如果这时主动⽅处于 CLOSED 状态 ,就会响应 rst ⽽不是 ack。所以
主动⽅要处于 TIME_WAIT 状态,⽽不能是 CLOSED 。
TIME_WAIT 并不会占⽤很⼤资源的,除⾮受到攻击。
还有,如果⼀⽅ send 或 recv 超时,就会直接进⼊ CLOSED 状态
TCP状态转移要点
TCP协议规定,对于已经建⽴的连接,⽹络双⽅要进⾏四次握⼿才能成功断开连接,如果缺少了其中某个步骤,将会使连接处于假死状态,连接本⾝占⽤的资源不会被释放。⽹络服务器程序要同时管理⼤量连接,所以很有必要保证⽆⽤连接完全断开,否则⼤量僵死的连接会浪费许多服务器资源。在众多TCP状态中,最值得注意的状态有两个:CLOSE_WAIT和TIME_WAIT。
1、LISTENING状态
  FTP服务启动后⾸先处于侦听(LISTENING)状态。
2、ESTABLISHED状态
  ESTABLISHED的意思是建⽴连接。表⽰两台机器正在通信。
3、CLOSE_WAIT
对⽅主动关闭连接或者⽹络异常导致连接中断,这时我⽅的状态会变成CLOSE_WAIT 此时我⽅要调⽤close()来使得连接正确关闭
4、TIME_WAIT
我⽅主动调⽤close()断开连接,收到对⽅确认后状态变为TIME_WAIT。TCP协议规定TIME_WAIT状态会⼀直持续2MSL(即两倍的分段最⼤⽣存期),以此来确保旧的连接状态不会对新连接产⽣影响。处于TIME_WAIT状态的连接占⽤的资源不会被内核释放,所以作为服务器,在可能的情况下,尽量不要主动断开连接,以减少TIME_WAIT状态造成的资源浪费。
///
⽬前有⼀种避免TIME_WAIT资源浪费的⽅法,就是关闭socket的LINGER选项。但这种做法是TCP协议不推荐使⽤的,在某些情况下这个操作可能会带来错误。
不久前,我的Socket Client程序遇到了⼀个⾮常尴尬的错误。它本来应该在⼀个socket长连接上持续不断地向服务器发送数据,如果socket连接断开,那么程序会⾃动不断地重试建⽴连接。
有⼀天发现程序在不断尝试建⽴连接,但是总是失败。⽤netstat查看,这个程序竟然有上千个socket连接处于CLOSE_WAIT状态,以⾄于达到了上限,所以⽆法建⽴新的socket连接了。
为什么会这样呢?
它们为什么会都处在CLOSE_WAIT状态呢?
CLOSE_WAIT状态的⽣成原因
⾸先我们知道,如果我们的Client程序处于CLOSE_WAIT状态的话,说明套接字是被动关闭的!
因为如果是Server端主动断掉当前连接的话,那么双⽅关闭这个TCP连接共需要四个packet:
Server ---> FIN ---> Client
Server <--- ACK <--- Client
这时候Server端处于FIN_WAIT_2状态;⽽我们的程序处于CLOSE_WAIT状态。
Server <--- FIN <--- Client
这时Client发送FIN给Server,Client就置为LAST_ACK状态。
Server ---> ACK ---> Client
Server回应了ACK,那么Client的套接字才会真正置为CLOSED状态。
我们的程序处于CLOSE_WAIT状态,⽽不是LAST_ACK状态,说明还没有发FIN给Server,那么可能是在关闭连接之前还有许多数据要发送或者其他事要做,导致没有发这个FIN packet。
原因知道了,那么为什么不发FIN包呢,难道会在关闭⼰⽅连接前有那么多事情要做吗?
还有⼀个问题,为什么有数千个连接都处于这个状态呢?难道那段时间内,服务器端总是主动拆除我们的连接吗?
不管怎么样,我们必须防⽌类似情况再度发⽣!
⾸先,我们要防⽌不断开辟新的端⼝,这可以通过设置SO_REUSEADDR套接字选项做到:
重⽤本地地址和端⼝
以前我总是⼀个端⼝不⾏,就换⼀个新的使⽤,所以导致让数千个端⼝进⼊CLOSE_WAIT状态。如果
下次还发⽣这种尴尬状况,我希望加⼀个限定,只是当前这个端⼝处于CLOSE_WAIT状态!
在调⽤
sockConnected = socket(AF_INET, SOCK_STREAM, 0);
之后,我们要设置该套接字的选项来重⽤:
/// 允许重⽤本地地址和端⼝:
/// 这样的好处是,即使socket断了,调⽤前⾯的socket函数也不会占⽤另⼀个,⽽是始终就是⼀个端⼝
/// 这样防⽌socket始终连接不上,那么按照原来的做法,会不断地换端⼝。
int nREUSEADDR = 1;
setsockopt(sockConnected,
SOL_SOCKET,
SO_REUSEADDR,
(const char*)&nREUSEADDR,
sizeof(int));
教科书上是这么说的:这样,假如服务器关闭或者退出,造成本地地址和端⼝都处于TIME_WAIT状态,那么SO_REUSEADDR就显得⾮常有⽤。
也许我们⽆法避免被冻结在CLOSE_WAIT状态永远不出现,但起码可以保证不会占⽤新的端⼝。
其次,我们要设置SO_LINGER套接字选项:
从容关闭还是强⾏关闭?
LINGER是“拖延”的意思。
默认情况下(Win2k),SO_DONTLINGER套接字选项的是1;SO_LINGER选项是,linger为{l_onoff:0,l_linger:0}。
如果在发送数据的过程中(send()没有完成,还有数据没发送)⽽调⽤了closesocket(),以前我们⼀般采取的措施是“从容关闭”:
因为在退出服务或者每次重新建⽴socket之前,我都会先调⽤
/// 先将双向的通讯关闭
shutdown(sockConnected, SD_BOTH);
/// 安全起见,每次建⽴Socket连接前,先把这个旧连接关闭
closesocket(sockConnected);
我们这次要这么做:
设置SO_LINGER为零(亦即linger结构中的l_onoff域设为⾮零,但l_linger为0),便不⽤担⼼closesocket调⽤进⼊“锁定”状态(等待完成),不论是否有排队数据未发送或未被确认。这种关闭⽅式称为“强⾏关闭”,因为套接字的虚电路⽴即被复位,尚未发出的所有数据都会丢失。在远端的recv()调⽤都会失败,并返回WSAECONNRESET错误。
在connect成功建⽴连接之后设置该选项:
linger m_sLinger;
m_sLinger.l_onoff = 1;  // (在closesocket()调⽤,但是还有数据没发送完毕的时候容许逗留)
m_sLinger.l_linger = 0; // (容许逗留的时间为0秒)
setsockopt(sockConnected,
SOL_SOCKET,
SO_LINGER,
(const char*)&m_sLinger,
风险评估体系sizeof(linger));
总结
也许我们避免不了CLOSE_WAIT状态冻结的再次出现,但我们会使影响降到最⼩,希望那个重⽤套接字选项能够使得下⼀次重新建⽴连接时可以把CLOSE_WAIT状态踢掉。
Feedback
# 回复:[Socket]尴尬的CLOSE_WAIT状态以及应对策略 2005-01-30 3:41 PM yun.zheng
回复⼈: elssann(⾍和他的开⼼果) ( ) 信誉:51 2005-01-30 14:00:00 得分: 0
我的意思是:当⼀⽅关闭连接后,另外⼀⽅没有检测到,就导致了CLOSE_WAIT的出现,上次我的⼀个朋友也是这样,他写了⼀个客户端和 APACHE连接,当APACHE把连接断掉后,他没检测到,出现了CLOSE_WAIT,后来我叫他检测了这个地⽅,他添加了调⽤closesocket的代码后,这个问题就消除了。
如果你在关闭连接前还是出现CLOSE_WAIT,建议你取消shutdown的调⽤,直接两边closesocket试试。
另外⼀个问题:
⽐如这样的⼀个例⼦:
当客户端登录上服务器后,发送⾝份验证的请求,服务器收到了数据,对客户端⾝份进⾏验证,发现密码错误,这时候服务器的⼀般做法应该是先发送⼀个密码错误的信息给客户端,然后把连接断掉。
如果把
m_sLinger.l_onoff = 1;
m_sLinger.l_linger = 0;
抽象函数
这样设置后,很多情况下,客户端根本就收不到密码错误的消息,连接就被断了。
# 回复:[Socket]尴尬的CLOSE_WAIT状态以及应对策略 2005-01-30 3:41 PM yun.zheng
elssann(⾍和他的开⼼果) ( ) 信誉:51 2005-01-30 13:24:00 得分: 0
出现CLOSE_WAIT的原因很简单,就是某⼀⽅在⽹络连接断开后,没有检测到这个错误,没有执⾏closesocket,导致了这个状态的实现,这在TCP/IP协议的状态变迁图上可以清楚看到。同时和这个相对应的还有⼀种叫TIME_WAIT的。
另外,把SOCKET的SO_LINGER设置为0秒拖延(也就是⽴即关闭)在很多时候是有害处的。
还有,把端⼝设置为可复⽤是⼀种不安全的⽹络编程⽅法。
# 回复:[Socket]尴尬的CLOSE_WAIT状态以及应对策略 2005-01-30 3:42 PM yun.zheng
elssann(⾍和他的开⼼果) ( ) 信誉:51 2005-01-30 14:48:00 得分: 0
断开连接的时候,
当发起主动关闭的左边这⽅发送⼀个FIN过去后,右边被动关闭的这⽅要回应⼀个ACK,这个ACK是TCP回应的,⽽不 是应⽤程序发送的,此时,被动关闭的⼀⽅就处于CLOSE_WAIT状态了。如果此时被动关闭的这⼀⽅不再继续调⽤closesocket,那么他就不会 发送接下来的FIN,导致⾃⼰⽼是处于CLOSE_WAIT。只有被动关闭的这⼀⽅调⽤了closesocket,才会发送⼀个FIN给主动关闭的这⼀ ⽅,同时也使得⾃⼰的状态变迁为LAST_ACK。
# 回复:[Socket]尴尬的CLOSE_WAIT状态以及应对策略 2005-01-30 3:54 PM yun.zheng
elssann(⾍和他的开⼼果) ( ) 信誉:51 2005-01-30 15:39:00 得分: 0
⽐如被动关闭的是客户端。。。
当对⽅调⽤closesocket的时候,你的程序正在
int nRet = recv(s,....);
if (nRet == SOCKET_ERROR)
{
// closesocket(s);
return FALSE;
}
原地掷铅球很多⼈就是忘记了那句closesocket,这种代码太常见了。
我的理解,当主动关闭的⼀⽅发送FIN到被动关闭这边后,被动关闭这边的TCP马上回应⼀个ACK过去,同时向上⾯应⽤程序提交⼀个ERROR,导 致上⾯的SOCKET的send或者recv返回SOCKET_ERROR,正常情况下,如果上⾯在返回SOCKET_ERROR后调⽤了closesocket,那么被动关闭的者⼀⽅的TCP就会发送⼀个FIN过去,⾃⼰的状态就变迁到LAST_ACK.

本文发布于:2024-09-21 20:53:45,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/599973.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:连接   状态   关闭   数据   发送   处于
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议