TCP TIME_WAIT解决方案
写过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解决方案相关推荐
- TCP TIME_WAIT详解
转自:http://m.blog.chinaunix.net/uid-20384806-id-1954363.html TIME_WAIT状态 TCP要保证在所有可能的情况下使得所有的数据都能够正确被 ...
- 亚信AX58200 EtherCAT转Modbus TCP网关解决方案介绍视频
为何需要使用EtherCAT转Modbus TCP网关解决方案? Modbus TCP并非实时性的网络协议栈,资料传输的速度也比较慢:因此,可以利用此EtherCAT转Modbus TCP网关解决方案 ...
- TCP TIME_WAIT 详解
先放上TCP状态转换图 再放上三次握手 然后放上四次挥手 OK,我们发现在四次挥手中出现了TIME_WAIT 状态 1.time_wait状态是什么 简单来说:time_wait状态是四次挥手中ser ...
- md+邮件服务器+334错误,邮件发送,无尽的501错误。TCP发送邮件解决方案
先贴上错误信息,便于搜索引擎采集,也送给遇到此问题的技术朋友们. smtp 501 Syntax error (no parameters allowed) (#5.5.4) 背景描述: 使用TCP发 ...
- TCP time_wait 的存在意义
<TCP/IP详解>中的TCP的建立和终止中有关"TCP的终止"的讲解 TCP的终止通过双方的四次挥手协议实现.发起终止的一方执行主动关闭,响应的另一方执行被动关闭. ...
- tcp port numbers reused出现原因_谈谈 TCP 的 TIME_WAIT
由来 最近有同事在用 ab 进行服务压测,到 QPS 瓶颈后怀疑是起压机的问题,来跟我借测试机,于是我就趁机分析了一波起压机可能成为压测瓶颈的可能,除了网络 I/O.机器性能外,还考虑到了网络协议的问 ...
- go tcp连接_TCP漫谈之keepalive和time_wait
TCP是一个有状态通讯协议,所谓的有状态是指通信过程中通信的双方各自维护连接的状态. 一.TCP keepalive 先简单回顾一下TCP连接建立和断开的整个过程.(这里主要考虑主流程,关于丢包.拥塞 ...
- go tcp客户端自动重连_深入分析TCP的keepalive和time_wait,总能发现新东西
TCP是一个有状态通讯协议,所谓的有状态是指通信过程中通信的双方各自维护连接的状态. 一.TCP keepalive 先简单回顾一下TCP连接建立和断开的整个过程.(这里主要考虑主流程,关于丢包.拥塞 ...
- 解决socket通信时TIME_WAIT的socket过多
TIME_WAIT状态 TCP要保证在所有可能的情况下使得所有的数据都能够正确被投递. 当关闭一个 socket 连接时,主动关闭一端的 socket 将进入TIME_WAIT状态,而被动关闭一方则转 ...
最新文章
- 给View 添加手势,点击无反应 如何给View添加点击事件,手势方法
- owc_绘图区(PlotArea)背景显示多种颜色
- Python入门100题 | 第052题
- 牛客小白月赛18-记录
- OSPF定义的5种区域类型:标准区域、主干区域、存根区域、完全存根区域
- [转]Ubuntu下快速安装python
- .NET项目工程生成一份项目帮助文档chm--Sandcastle工具
- java笔记:SpringSecurity应用(二)
- Atitit 函数调用的原理与本质attilax总结 stdcall cdecl区别
- 容器技术Docker K8s 28 容器服务ACK基础与进阶-弹性伸缩
- opencv 模式识别学习
- 电商需求分析mysql建表_电商-专题表 - 数据库设计 - 数据库表结构 - 果创云
- 边界值法中的上点、内点和离点分析
- 【re】python正则表达式的用法汇总 + 使用正则表达式提取不让复制的网页的文本内容!
- Android DNK开发错误记录
- vue中用js将json数据按英文字母顺序进行排序
- 手动修改dns服务器设置,如何修改DNS设置 修改DNS设置方法【详解】
- 多个Ajax请求成功后再执行后续方法
- Notepad++下载与安装教程
- 关于PEAP认证的过程说明