写过TCP服务器的人都知道,要解决主动关闭后的TIME_WAIT状态是件很麻烦的事情,如果服务器设置Linger生效且延迟为0秒,则服务器发送给Client的最后一个数据包极可能丢失。Server端TIME_WAIT过多会导致服务器效率急剧下降,Client端TIME_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生效,且延迟为0秒XSetSocketLinger(),设置为非阻塞模式XSetSocketBlockingMode(),便于快速connect到SERVER ,异步检测是否连接成功。
 将句柄压入写检测队列。

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内完成响应,平均每秒约能完成1422个TCP并发请求。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端主动关闭,则SERVER端socket 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,   /*   now   a   valid   state   */  
   
      TCP_MAX_STATES   /*   Leave   at   the   end!   */  
  };  
   
   
  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("%20s   %10s   %20s   %10s   %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("%20s   %10u   %20s   %10u   %s/n",    
                                  szLocalIp,     ntohs((unsigned   short)(0x0000FFFF   &   pTcpTable->table[i].dwLocalPort)),  
                                  szRemIp,   ntohs((unsigned   short)(0x0000FFFF   &   dwRemotePort)),  
                                  strState);  
                  }  
          }  
  }

TCP TIME_WAIT解决方案相关推荐

  1. TCP TIME_WAIT详解

    转自:http://m.blog.chinaunix.net/uid-20384806-id-1954363.html TIME_WAIT状态 TCP要保证在所有可能的情况下使得所有的数据都能够正确被 ...

  2. 亚信AX58200 EtherCAT转Modbus TCP网关解决方案介绍视频

    为何需要使用EtherCAT转Modbus TCP网关解决方案? Modbus TCP并非实时性的网络协议栈,资料传输的速度也比较慢:因此,可以利用此EtherCAT转Modbus TCP网关解决方案 ...

  3. TCP TIME_WAIT 详解

    先放上TCP状态转换图 再放上三次握手 然后放上四次挥手 OK,我们发现在四次挥手中出现了TIME_WAIT 状态 1.time_wait状态是什么 简单来说:time_wait状态是四次挥手中ser ...

  4. md+邮件服务器+334错误,邮件发送,无尽的501错误。TCP发送邮件解决方案

    先贴上错误信息,便于搜索引擎采集,也送给遇到此问题的技术朋友们. smtp 501 Syntax error (no parameters allowed) (#5.5.4) 背景描述: 使用TCP发 ...

  5. TCP time_wait 的存在意义

    <TCP/IP详解>中的TCP的建立和终止中有关"TCP的终止"的讲解 TCP的终止通过双方的四次挥手协议实现.发起终止的一方执行主动关闭,响应的另一方执行被动关闭. ...

  6. tcp port numbers reused出现原因_谈谈 TCP 的 TIME_WAIT

    由来 最近有同事在用 ab 进行服务压测,到 QPS 瓶颈后怀疑是起压机的问题,来跟我借测试机,于是我就趁机分析了一波起压机可能成为压测瓶颈的可能,除了网络 I/O.机器性能外,还考虑到了网络协议的问 ...

  7. go tcp连接_TCP漫谈之keepalive和time_wait

    TCP是一个有状态通讯协议,所谓的有状态是指通信过程中通信的双方各自维护连接的状态. 一.TCP keepalive 先简单回顾一下TCP连接建立和断开的整个过程.(这里主要考虑主流程,关于丢包.拥塞 ...

  8. go tcp客户端自动重连_深入分析TCP的keepalive和time_wait,总能发现新东西

    TCP是一个有状态通讯协议,所谓的有状态是指通信过程中通信的双方各自维护连接的状态. 一.TCP keepalive 先简单回顾一下TCP连接建立和断开的整个过程.(这里主要考虑主流程,关于丢包.拥塞 ...

  9. 解决socket通信时TIME_WAIT的socket过多

    TIME_WAIT状态 TCP要保证在所有可能的情况下使得所有的数据都能够正确被投递. 当关闭一个 socket 连接时,主动关闭一端的 socket 将进入TIME_WAIT状态,而被动关闭一方则转 ...

最新文章

  1. 给View 添加手势,点击无反应 如何给View添加点击事件,手势方法
  2. owc_绘图区(PlotArea)背景显示多种颜色
  3. Python入门100题 | 第052题
  4. 牛客小白月赛18-记录
  5. OSPF定义的5种区域类型:标准区域、主干区域、存根区域、完全存根区域
  6. [转]Ubuntu下快速安装python
  7. .NET项目工程生成一份项目帮助文档chm--Sandcastle工具
  8. java笔记:SpringSecurity应用(二)
  9. Atitit 函数调用的原理与本质attilax总结 stdcall cdecl区别
  10. 容器技术Docker K8s 28 容器服务ACK基础与进阶-弹性伸缩
  11. opencv 模式识别学习
  12. 电商需求分析mysql建表_电商-专题表 - 数据库设计 - 数据库表结构 - 果创云
  13. 边界值法中的上点、内点和离点分析
  14. 【re】python正则表达式的用法汇总 + 使用正则表达式提取不让复制的网页的文本内容!
  15. Android DNK开发错误记录
  16. vue中用js将json数据按英文字母顺序进行排序
  17. 手动修改dns服务器设置,如何修改DNS设置 修改DNS设置方法【详解】
  18. 多个Ajax请求成功后再执行后续方法
  19. Notepad++下载与安装教程
  20. 关于PEAP认证的过程说明

热门文章

  1. 2017年计算机ppt考试试题,2017年职称计算机考试(PPT练习题大全)
  2. 《那些年啊,那些事——一个程序员的奋斗史》——68
  3. 植物大战 类和对象 ——C++
  4. FL Studio 21最新中文版安装教程
  5. Linux系统如何更新升级
  6. 车载以太网协议:SOME/IP (layer5-7)简介
  7. 动圈耳机振膜_小白大讲堂: 耳机振膜材料对音质的影响
  8. Processing Arduino 音频频谱显示
  9. 你是不是程序员菜鸡,一句话就能证明!
  10. iframe去除边框