解决socket通信时TIME_WAIT的socket过多

TIME_WAIT状态

TCP要保证在所有可能的情况下使得所有的数据都能够正确被投递。

当关闭一个 socket 连接时,主动关闭一端的 socket 将进入TIME_WAIT状态,而被动关闭一方则转入CLOSED状态。

见图解。

Time_wait

 

当一个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对于我们来说往往是有利的。

 

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

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

A: 这个套接字选项通知内核,如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用
端口。如果端口忙,而TCP状态位于其他状态,重用端口时依旧得到一个错误信息,
指明"地址已经使用中"。如果你的服务程序停止后想立即重启,而新套接字依旧
使用同一端口,此时 SO_REUSEADDR 选项非常有用。必须意识到,此时任何非期
望数据到达,都可能导致服务程序反应混乱,不过这只是一种可能,事实上很不
可能。

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

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

A: W. Richard Stevens <1999年逝世,享年49岁>

下面我来解释一下 TIME_WAIT 状态,这些在<>
中2.6节解释很清楚了。

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
<------------------------------- CLOSED

ACK N+1
------------------------------->
TIME_WAIT

为什么需要 TIME_WAIT 状态?

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

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

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

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

A: 小四

在Solaris 7下有内核参数对应 TIME_WAIT 状态保持时间

# ndd -get /dev/tcp tcp_time_wait_interval
240000
# ndd -set /dev/tcp tcp_time_wait_interval 1000

缺省设置是240000ms,也就是4分钟。如果用ndd修改这个值,最小只能设置到1000ms,
也就是1秒。显然内核做了限制,需要Kernel Hacking。

# echo "tcp_param_arr/W 0t0" | adb -kw /dev/ksyms /dev/mem
physmem 3b72
tcp_param_arr: 0x3e8 = 0x0
# ndd -set /dev/tcp tcp_time_wait_interval 0

我不知道这样做有什么灾难性后果,参看<>的声明。

Q: TIME_WAIT 状态保持时间为0会有什么灾难性后果?在普遍的现实应用中,好象也
就是服务器不稳定点,不见得有什么灾难性后果吧?

D: rain@bbs.whnet.edu.cn

Linux 内核源码 /usr/src/linux/include/net/tcp.h 中

#define TCP_TIMEWAIT_LEN (60*HZ)

最好不要改为0,改成1。端口分配是从上一次分配的端口号+1开始分配的,所以一般
不会有什么问题。端口分配算法在tcp_ipv4.c中tcp_v4_get_port中。

 

----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
TIME_WAIT状态对HTTP影响

根据TCP协议,主动发起关闭的一方,会进入TIME_WAIT状态,持续2*MSL(Max Segment Lifetime),缺省为240秒,在这个post中简洁的介绍了为什么需要这个状态。

值得一说的是,对于基于TCP的HTTP协议,关闭TCP连接的是Server端,这样,Server端会进入TIME_WAIT状态,可想而知,对于访问量大的Web Server,会存在大量的TIME_WAIT状态,假如server一秒钟接收1000个请求,那么就会积压240*1000=240,000个 TIME_WAIT的记录,维护这些状态给Server带来负担。当然现代操作系统都会用快速的查找算法来管理这些TIME_WAIT,所以对于新的 TCP连接请求,判断是否hit中一个TIME_WAIT不会太费时间,但是有这么多状态要维护总是不好。

HTTP协议1.1版规定default行为是Keep-Alive,也就是会重用TCP连接传输多个request/response,一个主要原因就是发现了这个问题。还有一个方法减缓TIME_WAIT压力就是把系统的2*MSL时间减少,因为240秒的时间实在是忒长了点,对于 Windows,修改注册表,在HKEY_LOCAL_MACHINE\ SYSTEM\CurrentControlSet\Services\ Tcpip\Parameters上添加一个DWORD类型的值TcpTimedWaitDelay,一般认为不要少于60,不然可能会有麻烦。

对于大型的服务,一台server搞不定,需要一个LB(Load Balancer)把流量分配到若干后端服务器上,如果这个LB是以NAT方式工作的话,可能会带来问题。假如所有从LB到后端Server的IP包的 source address都是一样的(LB的对内地址),那么LB到后端Server的TCP连接会受限制,因为频繁的TCP连接建立和关闭,会在server上留下TIME_WAIT状态,而且这些状态对应的remote address都是LB的,LB的source port撑死也就60000多个(2^16=65536,1~1023是保留端口,还有一些其他端口缺省也不会用),每个LB上的端口一旦进入 Server的TIME_WAIT黑名单,就有240秒不能再用来建立和Server的连接,这样LB和Server最多也就能支持300个左右的连接。如果没有LB,不会有这个问题,因为这样server看到的remote address是internet上广阔无垠的集合,对每个address,60000多个port实在是够用了。

一开始我觉得用上LB会很大程度上限制TCP的连接数,但是实验表明没这回事,LB后面的一台Windows Server 2003每秒处理请求数照样达到了600个,难道TIME_WAIT状态没起作用?用Net Monitor和netstat观察后发现,Server和LB的XXXX端口之间的连接进入TIME_WAIT状态后,再来一个LB的XXXX端口的 SYN包,Server照样接收处理了,而是想像的那样被drop掉了。翻书,从书堆里面找出覆满尘土的大学时代买的《UNIX Network Programming, Volume 1, Second Edition: Networking APIs: Sockets and XTI》,中间提到一句,对于BSD-derived实现,只要SYN的sequence number比上一次关闭时的最大sequence number还要大,那么TIME_WAIT状态一样接受这个SYN,难不成Windows也算BSD-derived?有了这点线索和关键字 (BSD),找到这个post,在NT4.0的时候,还是和BSD-derived不一样的,不过Windows Server 2003已经是NT5.2了,也许有点差别了。

做个试验,用Socket API编一个Client端,每次都Bind到本地一个端口比如2345,重复的建立TCP连接往一个Server发送Keep-Alive=false 的HTTP请求,Windows的实现让sequence number不断的增长,所以虽然Server对于Client的2345端口连接保持TIME_WAIT状态,但是总是能够接受新的请求,不会拒绝。那如果SYN的Sequence Number变小会怎么样呢?同样用Socket API,不过这次用Raw IP,发送一个小sequence number的SYN包过去,Net Monitor里面看到,这个SYN被Server接收后如泥牛如海,一点反应没有,被drop掉了。

按照书上的说法,BSD-derived和Windows Server 2003的做法有安全隐患,不过至少这样至少不会出现TIME_WAIT阻止TCP请求的问题,当然,客户端要配合,保证不同TCP连接的sequence number要上涨不要下降。

 

文章链接:
socket中的TIME_WAIT状态  http://www.cnblogs.com/alon/archive/2009/04/01/1427625.html
TCP TIME_WAIT状态QA      http://www.cnblogs.com/jason-jiang/archive/2006/11/03/549337.html
TIME_WAIT状态对HTTP影响 http://morganchengmo.spaces.live.com/blog/cns!9950CE918939932E!1721.entry

============================================

写过TCP服务器的人都知道,要解决主动关闭后的TIME_WAIT状态是件很麻烦的事情,如果服务器设置Linger生效且延迟为0秒,则服务器发送给Client的最后一个数据包极可能丢失。ServerTIME_WAIT过多会导致服务器效率急剧下降,ClientTIME_WAIT过多会导致connect to server失败(WSAEADDRINUSE错误,休息一段时间让部分处于TIME_WAIT状态的句柄超时后又能connect成功到服务器)

几乎翻遍互联网也没找到比较好的解决方案,其中有老外提出修改TCP/IP协议栈的方案(前几天我也在考虑这个方案的可行性)。 

下面给出我的关于解决TIME_WAIT的方案。
 首先分析下从ESTABLISHED状态到关闭状态的过程:
只有两种方式: 1,主动关闭socket连接。 

         2,被动关闭socket连接。

我们知道主动关闭至少会经过TIME_WAIT_1--->TIME_WAIT(2MSL timeout)--->CLOSED
而被动关闭会经过CLOSE_WAIT--->LAST_ACK--->CLOSED

Step 1:

 分配SERVER端处理CLIENT并发TCP connection请求的节点数目要大于Client单次select()能发起的最大连接数目。这样SERVER端保证不会有请求得不到临时节点而无法接入到SERVER.

 设置新accept接入的句柄reuse address, XSetSocketReuseAddress(),这个是我自己封装的跨平台函数,后面同,大家可以自己用setsockopt()实现。

  Step 2:
 将新接入的节点压入读检测队列(select or epoll). 

Step 3:
 有数据到来,读取数据(如果读取长度为0,则说明对方关闭了该TCP connection)。如果读取完毕,准备好回应数据,并压入写检测队列。 

Step 4:
 检测数据可写,发送数据。如果发送完毕,将TCP connection压入读检测队列(重复Step 2~Step 4以实现长连接J)。 

Step 5: 
        SERVER检测超时节点。
----------------------------------------------------------------------------------------------------

Step 6: 

CLIENT new socket handle,设置Linger生效,且延迟为0XSetSocketLinger(),设置为非阻塞模式XSetSocketBlockingMode(),便于快速connectSERVER ,异步检测是否连接成功。
 将句柄压入写检测队列。 

Step 7:
 如果可写,CLIENT发送请求数据给SERVER,发送完毕将句柄压入读检测队列。 

Step 8:
 如果有数据可读,则读取数据,读取完毕可以直接关闭连接。 

Step 9:
 CLIENT检测超时节点。 

效果:
D:\vcoutput\Release>SimuCGIClient.exe 172.30.14.13 25000 1000000
usage:SimuCGIClient.exe CacheServerHost Port LoopTimes
total ssuccess query:1000012 in 703 seconds.
1422.49 querys/second.

windows下向SERVER请求100万个连接,在703seconds内完成响应,平均每秒约能完成1422TCP并发请求。SERVER,CLIENT均为单线程程序。
在这100万个节点查询过程中,查看服务器和client的协议栈句柄消耗情况:
----------------------------------------------------------------------------------------------------
Active Connections
 Proto Local Address Foreign Address State
 TCP WOOKIN-NB:1847 WOOKIN-NB:25000 ESTABLISHED
 TCP WOOKIN-NB:1848 WOOKIN-NB:25000 ESTABLISHED
 TCP WOOKIN-NB:1849 WOOKIN-NB:25000 ESTABLISHED
 TCP WOOKIN-NB:1850 WOOKIN-NB:25000 ESTABLISHED
 TCP WOOKIN-NB:1851 WOOKIN-NB:25000 ESTABLISHED
 TCP WOOKIN-NB:1852 WOOKIN-NB:25000 ESTABLISHED
 TCP WOOKIN-NB:1853 WOOKIN-NB:25000 ESTABLISHED
 TCP WOOKIN-NB:25000  WOOKIN-NB:0 LISTENING
 TCP WOOKIN-NB:25000 WOOKIN-NB:1847 ESTABLISHED
 TCP WOOKIN-NB:25000 WOOKIN-NB:1848 ESTABLISHED
 TCP WOOKIN-NB:25000 WOOKIN-NB:1849 ESTABLISHED
 TCP WOOKIN-NB:25000 WOOKIN-NB:1850 ESTABLISHED
 TCP WOOKIN-NB:25000 WOOKIN-NB:1851 ESTABLISHED
 TCP WOOKIN-NB:25000 WOOKIN-NB:1852 ESTABLISHED
 TCP WOOKIN-NB:25000 WOOKIN-NB:1853 ESTABLISHED
 UDP  WOOKIN-NB:microsoft-ds *:* 
----------------------------------------------------------------------------------------------------
 只看到LISTENING, ESTABLISHED状态,太漂亮了。 接下来我会在Linux环境下进行继续验证。 

方案关键点:
 将通常情况下应该由SERVER端主动关闭的句柄逆向思维转由CLIENT端主动关闭,则SERVERsocket handle能快速优雅地进入到CLOSED状态。 CLIENT端接收完数据以后采用Linger on延迟0秒的模式,“武断”地关闭client socket handle。其实这种做法一点都不武断,因为CLIENT已经收完数据包,延迟为0关闭句柄并不会造成数据丢失(如果是发送方这样关闭则会造成数据丢失,接收方可以安全快速关闭)。
 SERVER端采用支持长连接的方式巧妙地检测到CLIENT关闭socket connection,又能正常支持长短连接.





请问我如何

检查 自己的socket连接当前处于什么TCP状态呢?  
  因为我想如果检查到当前连接处于CLOSE_WAIT状态的话,就调用closesocket()函数。  
   
  是否是使用select+fd_isset来做到这一点呢?  
  Thanks!  

        是否是使用select+fd_isset来做到这一点呢?  
  ---  
  肯定不是。  
   
  因为我想如果检查到当前连接处于CLOSE_WAIT状态的话,就调用closesocket()函数。  
  --------  
  我印象中CLOSE_WAIT和调用closesocket没关系。你为什么要检测CLOSE_WAIT状态?奇怪的想法。  
       
     
  Top      
     
    回复人:   xiaohaiyan(xiaohaiyan)   (   )   信誉:100     2005-02-03   12:15:00    得分:   0      
     
     
          如果是linux

系统 ,可以参考一下这个,  
  enum   {  
      TCP_ESTABLISHED   =   1,  
      TCP_SYN_SENT,  
      TCP_SYN_RECV,  
      TCP_FIN_WAIT1,  
      TCP_FIN_WAIT2,  
      TCP_TIME_WAIT,  
      TCP_CLOSE,  
      TCP_CLOSE_WAIT,  
      TCP_LAST_ACK,  
      TCP_LISTEN,  
      TCP_CLOSING,    
   
      TCP_MAX_STATES    
  };  
   
   
  socket->sock->sk_state     记录上述状态  
   
  调用getsockname   ,可以通过fd得到socket结构地址。  
   
   
       
     
  Top      
     
    回复人:   zhengyun_ustc(郑昀)   (   )   信誉:100     2005-02-03   12:42:00     得分:   0      
     
     
        因为如果我是客户端,当我给服务器端发送数据后,正准备recv接收响应时,服务器突然直接关闭session和socket,于是我这边立刻进入 CLOSE_WAIT状态,并且始终处于这种状态;这是我的猜想,我可以通过检查send和recv的返回值,以找到这种错误。  
  但是为了以防万一,我想主动检查TCP状态。  
  我的OS是Windows。  
       请读一下PlatformSDK的示例  
  Samples\NetDS\IPHelp\IPStat

void   DumpTcpTable(PMIB_TCPTABLE   pTcpTable)  
  {  
          char         strState[MAX_STRLEN];  
          struct     in_addr         inadLocal,   inadRemote;  
          DWORD       dwRemotePort   =   0;  
          char         szLocalIp[MAX_STRLEN];  
          char         szRemIp[MAX_STRLEN];  
   
          if   (pTcpTable   !=   NULL)  
          {  
                  printf("TCP   TABLE\n");  
                  printf(" s   s   s   s   %s\n",   "Loc   Addr",   "Loc   Port",   "Rem   Addr",  
                                  "Rem   Port",   "State");  
                  for   (UINT   i   =   0;   i   <   pTcpTable->dwNumEntries;  ++i)  
                  {  
                          switch   (pTcpTable->table[i].dwState)  
                          {  
                          case   MIB_TCP_STATE_CLOSED:  
                                  strcpy(strState,   "CLOSED");  
                                  break;  
                          case   MIB_TCP_STATE_TIME_WAIT:  
                                  strcpy(strState,   "TIME_WAIT");  
                                  break;  
                          case   MIB_TCP_STATE_LAST_ACK:  
                                  strcpy(strState,   "LAST_ACK");  
                                  break;  
                          case   MIB_TCP_STATE_CLOSING:  
                                  strcpy(strState,   "CLOSING");  
                                  break;  
                          case   MIB_TCP_STATE_CLOSE_WAIT:  
                                  strcpy(strState,   "CLOSE_WAIT");  
                                  break;  
                          case   MIB_TCP_STATE_FIN_WAIT1:  
                                  strcpy(strState,   "FIN_WAIT1");  
                                  break;  
                          case   MIB_TCP_STATE_ESTAB:  
                                  strcpy(strState,   "ESTAB");  
                                  break;  
                          case   MIB_TCP_STATE_SYN_RCVD:  
                                  strcpy(strState,   "SYN_RCVD");  
                                  break;  
                          case   MIB_TCP_STATE_SYN_SENT:  
                                  strcpy(strState,   "SYN_SENT");  
                                  break;  
                          case   MIB_TCP_STATE_LISTEN:  
                                  strcpy(strState,   "LISTEN");  
                                  break;  
                          case   MIB_TCP_STATE_DELETE_TCB:  
                                  strcpy(strState,   "DELETE");  
                                  break;  
                          default:  
                                  printf("Error:   unknown   state!\n");  
                                  break;  
                          }  
                          inadLocal.s_addr   =   pTcpTable->table[i].dwLocalAddr;  
   
                          if   (strcmp(strState,   "LISTEN")   !=   0)  
                          {  
                                  dwRemotePort   =   pTcpTable->table[i].dwRemotePort;  
                          }  
                          else  
                                  dwRemotePort   =   0;  
   
   
                          inadRemote.s_addr   =   pTcpTable->table[i].dwRemoteAddr;  
                          strcpy(szLocalIp,   inet_ntoa(inadLocal));  
                          strcpy(szRemIp,   inet_ntoa(inadRemote));  
                          printf(" s   u   s   u   %s\n",    
                                  szLocalIp,     ntohs((unsigned   short)(0x0000FFFF   &   pTcpTable->table[i].dwLocalPort)),  
                                  szRemIp,   ntohs((unsigned   short)(0x0000FFFF   &   dwRemotePort)),  
                                  strState);  
                  }  
          }  
  }

===================================================

一般来说,tcp正常关闭需要四个包。比如a和b关闭连接,a先给b发一个fin,b会进行确认ack,然后b也会发出fin,当a接受到这个fin,并发出最后一个ack后,就会处于time_wait状态。这个时间长短跟操作系统有关,一般会在1-4分钟,也就是两倍的数据包(2msl)最大生存时间。TCP主动关闭方采用TIME_WAIT主要是为了实现终止 TCP全双工连接的可靠性及允许老的重复分节在网络中消逝,等过了2msl(大约1~4分钟)后TIME_WAIT就会消失。   
TIME_WAIT状态的目的是为了防止最后a发出的ack丢失,让b处于LAST_ACK超时重发FIN

Time_wait


   所以说,主动发起关闭连接的一方会进入time_wait状态,这个时候,进程所占用的端口号不能被释放。除非在你的程序中用setsockopt设置端口可重用(SOCK_REUSE)的选项,但这不是所有操作系统都支持的


   解决TIME_WAIT的办法主要有以下几种:    


   1、修改LINGER值,缩短关闭时间
   LINGER    lingerStruct;
   lingerStruct.l_onoff    =    1;
   lingerStruct.l_linger     =    0;
   setsockopt(m_socket,SOL_SOCKET,SO_LINGER,(char *)&lingerStruct,sizeof(lingerStruct));
   不过这种办法不是很安全的,不过现在网络都很好啦,不会有问题的。

   2、修改注册表
   [HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\Tcpip\Parameters]
   "TcpTimedWaitDelay"=dword:00000005
   这个值好像是300秒到30秒之间,改成30秒后你会发现TIME_WAIT很快就会消失了。


   3、禁用LINGER
   //如果你使用的是Socket API,可以这样
   BOOL bDontLinger=FALSE;      
   setsockopt(m_socket,SOL_SOCKET,SO_DONTLINGER,(LPCTSTR)&bDontLinger,sizeof(BOOL));
   closesocket(s);

   //如果你使用的是CAsyncSocket,需要响应的修改,例如禁用LINGER可以这样
   BOOL bDontLinger=FALSE;   
   m_socket->SetSockOpt(SO_DONTLINGER,(const char *)&bDontLinger,sizeof(bDontLinger),SOL_SOCKET);
   m_socket->Close();

   4、客户端可以不BIND(),这样,即使断开连接后再次连接,SOCKET将使用不同的端口(1025-5000),
   等几分钟后,原有的端口就会自动关闭。

关闭BITCOMET后系统出现的几个TCP状态
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值