`
frenchmay
  • 浏览: 229157 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

CLOSE_WAIT生成原因

阅读更多

原文地址:http://blog.csdn.net/eroswang/archive/2008/03/10/2162986.aspx
关闭socket分为主动关闭(Active closure)和被动关闭(Passive closure)两种情况。前者是指有本地主机主动发起的关闭;而后者则是指本地主机检测到远程主机发起关闭之后,作出回应,从而关闭整个连接。
    其状态图如下图所示:

      起初每个socket都是CLOSED状态,当客户端初使化一个连接,他发送一个SYN包到服务器,客户端进入SYN_SENT状态。
服务器接收到SYN包,反馈一个SYN-ACK包,客户端接收后返馈一个ACK包客户端变成ESTABLISHED状态,如果长时间没收到SYN-ACK包,客户端超时进入CLOSED状态。
  当服务器绑定并监听某一端口时,socket的状态是LISTEN,当客户企图建立连接时,服务器收到一个SYN包,并反馈SYN-ACK包。服务器状态变成SYN_RCVD,当客户端发送一个ACK包时,服务器socket变成ESTABLISHED状态。

  当一个程序在ESTABLISHED状态时有两种图径关闭它, 第一是主动关闭,第二是被动关闭。如果你要主动关闭的话,发送一个FIN包。当你的程序closesocket或者shutdown(标记),你的程序发 送一个FIN包到peer,你的socket变成FIN_WAIT_1状态。peer反馈一个ACK包,你的socket进入FIN_WAIT_2状态。 如果peer也在关闭连接,那么它将发送一个FIN包到你的电脑,你反馈一个ACK包,并转成TIME_WAIT状态。
  TIME_WAIT状态又号2MSL等待状态。MSL意思是最大段生命周期(Maximum Segment Lifetime)表明一个包存在于网络上到被丢弃之间的时间。每个IP包有一个TTL(time_to_live),当它减到0时则包被丢弃。每个路由 器使TTL减一并且传送该包。当一个程序进入TIME_WAIT状态时,他有2个MSL的时间,这个充许TCP重发最后的ACK,万一最后的ACK丢失 了,使得FIN被重新传输。在2MSL等待状态完成后,socket进入CLOSED状态。
  被动关闭:当程序收到一个FIN包从peer,并反馈一个ACK包,于是程序的socket转入CLOSE_WAIT状态。因为peer已经关闭了, 所以不能发任何消息了。但程序还可以。要关闭连接,程序自已发送给自已FIN,使程序的TCP socket状态变成LAST_ACK状态,当程序从peer收到ACK包时,程序进入CLOSED状态。

/////////////////////////////////////////////////////////////////////////

root权限执行 lsof -n httpd | grep TCP ,可以查看是否有大量CLOSE_WAIT

摘要:本文阐述了为何socket连接锁定在CLOSE_WAIT状态,以及通过什么措施力求避免这种情况。

不久前,我的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包呢,难道会在关闭己方连接前有那么多事情要做吗?

elssann举例说,当对方调用closesocket的时候,我的程序正在调用recv中,这时候有可能对方发送的FIN包我没有收到,而是由TCP代回了一个ACK包,所以我这边套接字进入CLOSE_WAIT状态。

所以他建议在这里判断recv函数的返回值是否已出错,是的话就主动closesocket,这样防止没有接收到FIN包。

因为前面我们已经设置了recv超时时间为30秒,那么如果真的是超时了,这里收到的错误应该是WSAETIMEDOUT,这种情况下也可以主动关闭连接的。

还有一个问题,为什么有数千个连接都处于这个状态呢?难道那段时间内,服务器端总是主动拆除我们的连接吗?

不管怎么样,我们必须防止类似情况再度发生!

首先,我们要保证原来的端口可以被重用,这可以通过设置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状态踢掉。

分享到:
评论

相关推荐

    挖掘鸡3.0豪华版

     修改close_wait状态,忽略扫描域名无效等问题。是否更新。  挖掘鸡 v7.0更新20:46 2009-7-30:  进一步增大扫描范围,改进扫描算法和效率;自动关键词联想,支持超长时间扫描等。建议更新。  挖掘鸡 v.6.9更新...

    TELNET批处理工具

    Usage Syntax: ...TST will disconnect and close as soon as its done with the last entry of the script. If you need to, you can type in the terminal window while the script is running.

    分割合并文件

    m_parts += _T("个文件生成"); UpdateData(FALSE); l++; UpdateWindow(); } while (dwRead &gt; 0); // close source m_SourceFile.Close(); m_path = _T(""); m_filename = _T(""); // pProgress.SetPos(0)...

    MFC制作的MP3

    if (dwReturn=mciSendCommand(NULL,MCI_OPEN,MCI_OPEN_ELEMENT|MCI_WAIT,(DWORD)(LPVOID)&mciopenparms)) { //Èç¹û´ò¿ªÊ§°Ü£¬½«³ö´íÐÅÏ¢´æÔÚbuffer²¢ÏÔʾ³ö´í¾¯¸æ ...

    java绘制音频波形图

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); javax.swing.GroupLayout layout = new GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout....

    matlab回调函数代码-PSO_PathPlaningNew:这个开源项目是一个matlabGUI项目,是一个使用粒子群优化(PSO)算法的

    单击按钮'添加圆'-&gt;左键单击并拖动光标以生成圆数据集-&gt;单击按钮'结束添加'-&gt;单击按钮'选择起始点'-&gt;左键单击并选择起点和终点- &gt;点击“规划路径”按钮-&gt;结束 更多用法 以Bezier曲线获取结果,结果(x11, y11 )和...

    摄像头设密码软件摄像头设密码软件

    另外,如果你想设置自己离开电脑5分钟后,使系统自动进入锁定状态,那么只要在软件界面中点击“Settings”,然后在右侧窗口中下方将“Wait min”后的“10”设置为“5”即可(如图4)。在此标签页中我们还可以在“Hot ...

    工业机器人离线编程:机器人基本命令总结(机器人包老师).docx

    MOVET &lt;位置&gt;,&lt;手开度&gt; 功能是生成关节插值运动使机器人到达位置变量所给定的位姿,运动中若手为伺服控制,则手由闭合改变到手开度变量给定的值。 又例如: OPEN [&lt;手开度&gt;] 表示使机器人手爪打开到指定的开度。 2...

    莱昂氏UNIX源代码分析(全面剖析unix)PDF

    18.11 close(5846) 318 18.12 closef(6643) 319 18.13 iput(7344) 319 18.14 删除文件 319 18.15 读和写文件 319 18.16 rdwr(5731) 320 18.17 readi(6221) 321 18.18 writei(6276) 322 18.19 iomove(6364) 322 18.20...

    莱昂氏UNIX源代码分析

    18.11 close(5846) 318 18.12 closef(6643) 319 18.13 iput(7344) 319 18.14 删除文件 319 18.15 读和写文件 319 18.16 rdwr(5731) 320 18.17 readi(6221) 321 18.18 writei(6276) 322 18.19 iomove(6364) 322 18.20...

    达内 coreJava 习题答案

    1,编写程序,判断给定的某个年份是否是闰年。 闰年的判断规则如下: (1)若某个年份能被4整除但不能被100整除,则是闰年。 (2)若某个年份能被400整除,则也是闰年。 import java.util.Scanner;...

    MaxDOS 5.6s U盘版

    输入的,输入为自后自动激活网卡,及IP奄码配置文件,自动生成IP地址为 192.168.5.45 子奄码为:255.255.255.0 网关:192.168.5.100),详细请见下表各网卡的批处理. 以下为5.5S版本或更旧的版本,全盘网刻例子: NV 网卡,...

    编写设备驱动程序

    编写设备驱动程序》提供有关为面向字符的设备、面向块的设备、网络设备、SCSI目标和HBA设备以及USB设备开发SolarisolarisOperatingSystem,SolarisOS)驱动程序的信息。本书讨论了如何为符合SolarisOSDDI/DKI(Device...

Global site tag (gtag.js) - Google Analytics