tcptcp ip 协议栈 测试close-wait 怎么解决

深入浅出TCP之半关闭与CLOSE_WAIT - 网络协议 - 红黑联盟
深入浅出TCP之半关闭与CLOSE_WAIT
深入浅出TCP之半关闭与CLOSE_WAIT
& & 终止一个连接要经过4次握手。这由TCP的半关闭(half-close)造成的。既然一个TCP连接是全双工(即数据在两个方向上能同时传递,可理解为两个方向相反的独立通道),因此每个方向必须单独地进行关闭。
这原则就是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向连接。当一端收到一个FIN,内核让read返回0来通知应用层另一端已经终止了向本端的数据传送。发送FIN通常是应用层对socket进行关闭的结果。
例如:TCP客户端发送一个FIN,用来关闭从客户到服务器的数据传送。
& & 半关闭对服务器究竟有什么影响呢?先看看下面的TCP状态转化图
& & & & & & & & & & & & & & & & & tcp状态装换图
& & 客户端主动关闭时,发出FIN包,收到服务器的ACK,客户端停留在FIN_WAIT2状态。而服务端收到FIN,发出ACK后,停留在COLSE_WAIT状态。
& & 这个CLOSE_WAIT状态非常讨厌,它持续的时间非常长,服务器端如果积攒大量的COLSE_WAIT状态的socket,有可能将服务器资源耗尽,进而无法提供服务。
& & 那么,服务器上是怎么产生大量的失去控制的COLSE_WAIT状态的socket呢?我们来追踪一下。
& & 一个很浅显的原因是,服务器没有继续发FIN包给客户端。
& & 服务器为什么不发FIN,可能是业务实现上的需要,现在不是发送FIN的时机,因为服务器还有数据要发往客户端,发送完了自然就要通过调用发FIN了,这个场景并不是上面我们提到的持续的COLSE_WAIT状态,这个在受控范围之内。
& & 那么究竟是什么原因呢,咱们引入两个系统调用close(sockfd)和shutdown(sockfd,how)接着往下分析。
& & 在这儿,需要明确的一个概念---- 一个进程打开一个socket,然后此进程再派生子进程的时候,此socket的sockfd会被继承。socket是系统级的对象,现在的结果是,此socket被两个进程打开,此socket的引用计数会变成2。
& & 继续说上述两个系统调用对socket的关闭情况。
& & 调用close(sockfd)时,内核检查此fd对应的socket上的引用计数。如果引用计数大于1,那么将这个引用计数减1,然后返回。如果引用计数等于1,那么内核会真正通过发FIN来关闭TCP连接。
& & 调用shutdown(sockfd,SHUT_RDWR)时,内核不会检查此fd对应的socket上的引用计数,直接通过发FIN来关闭TCP连接。
& & &现在应该真相大白了,可能是服务器的实现有点问题,父进程打开了socket,然后用派生子进程来处理业务,父进程继续对网络请求进行监听,永远不会终止。客户端发FIN过来的时候,处理业务的子进程的read返回0,子进程发现对端已经关闭了,直接调用close()对本端进行关闭。实际上,仅仅使socket的引用计数减1,socket并没关闭。从而导致系统中又多了一个CLOSE_WAIT的socket。。。
如何避免这样的情况发生?
子进程的关闭处理应该是这样的:
shutdown(sockfd, SHUT_RDWR);
close(sockfd);
这样处理,服务器的FIN会被发出,socket进入LAST_ACK状态,等待最后的ACK到来,就能进入初始状态CLOSED。
补充一下shutdown()的函数说明
linux系统下使用shutdown系统调用来控制socket的关闭方式
int shutdown(int sockfd,int how);
参数 how允许为shutdown操作选择以下几种方式:
SHUT_RD:关闭连接的读端。也就是该套接字不再接受数据,任何当前在套接字接受缓冲区的数据将被丢弃。进程将不能对该套接字发出任何读操作。对TCP套接字该调用之后接受到的任何数据将被确认然后被丢弃。
SHUT_WR:关闭连接的写端。
SHUT_RDWR:相当于调用shutdown两次:首先是以SHUT_RD,然后以SHUT_WR
在多进程中如果一个进程中shutdown(sfd, SHUT_RDWR)后其它的进程将无法进行通信. 如果一个进程close(sfd)将不会影响到其它进程.netstat- listening close_wait established等是什么意思?_百度知道
netstat- listening close_wait established等是什么意思?
提问者采纳
端口占用查询命令netstat详解 Netstat用于显示与IP、TCP、UDPICMP协议相关统计数据般用于检验本机各端口网络连接情况 计算机候接收数据报导致错数据或故障必奇怪TCP/IP容许些类型错误并能够自重发数据报累计错情况数目占所接收IP数据报相百比或者数目迅速增加应该使用Netstat查查现些情况 Netstat 详细参数列表 C:\&netstat /? 显示协议统计信息前 TCP/IP 网络连接 NETSTAT [-a] [-b] [-e] [-n] [-o] [-p proto] [-r] [-s] [-v] [interval] -a 显示所连接监听端口 -b 显示包含于创建每连接或监听端口执行组件某些情况已知执行组件 拥独立组件并且些情况包含于创建连接或监听端口组件序列显示 种情况执行组件名底部 [] 顶部其调用组件等等直 TCP/IP 部注意选项能需要间没足够权限能失败 -e 显示太网统计信息选项与 -s 选项组合使用 -n 数字形式显示址端口号 -o 显示与每连接相关所属进程 ID -p proto 显示 proto 指定协议连接;proto
列协议: TCP、UDP、TCPv6 或 UDPv6 与 -s 选项起使用显示按协议统计信息proto 列协议: IP、IPv6、ICMP、ICMPv6、TCP、TCPv6、UDP 或 UDPv6 -r 显示路由表 -s 显示按协议统计信息默认显示 IP、 IPv6、ICMP、ICMPv6、TCP、TCPv6、UDP
UDPv6 统计信息; -p 选项用于指定默认情况集 -v 与 -b 选项起使用显示包含于 所执行组件创建连接或监听端口 组件 interval 重新显示选定统计信息每显示间 暂停间间隔(秒计)按 CTRL+C 停止重新 显示统计信息省略netstat 显示前 配置信息(显示) (Win2000) C:\&netstat /? Displays protocol statistics and current TCP/IP network connections. NETSTAT [-a] [-e] [-n] [-s] [-p proto] [-r] [interval] -a Displays all connections and listening ports. -e Displays Ethernet statistics. This may be combined with the -s option. -n Displays addresses and port numbers in numerical form. -p proto Shows connections for the protoco proto may be TCP or UDP. If used with the -s option to display per-protocol statistics, proto may be TCP, UDP, or IP. -r Displays the routing table. -s Displays per-protocol statistics. By default, statistics are shown for TCP, UDP and IP; the -p option may be used to specify a subset of the default. interval Redisplays selected statistics, pausing interval seconds between each display. Press CTRL+C to stop redisplaying statistics. If omitted, netstat will print the current configuration information once. Netstat些用选项 netstat -s——本选项能够按照各协议别显示其统计数据应用程序(Web浏览器)运行速度比较慢或者能显示Web页类数据用本选项查看所显示信息需要仔细查看统计数据各行找错关键字进确定问题所 netstat -e——本选项用于显示关于太网统计数据列项目包括传送数据报总字节数、错误数、删除数、数据报数量广播数量些统计数据既发送数据报数量接收数据报数量选项用统计些基本网络流量 netstat -r——本选项显示关于路由表信息类似于面所讲使用route print命令看 信息除显示效路由外显示前效连接 netstat -a——本选项显示所效连接信息列表包括已建立连接(ESTABLISHED)包括监听连接请求(LISTENING)些连接断连接(CLOSE_WAIT)或者处于联机等待状态(TIME_WAIT)等 netstat -n——显示所已建立效连接 接我看微软说 微软公司故意功能强命令隐藏起于普通用户说些复杂我已经知道:Netstat用获系统网络连接信息(使用端口使用协议等 )收发数据连接远程系统端口Netstat内存读取所网络信息 Internet RFC标准Netstat定义: Netstat内核访问网络及相关信息程序能提供TCP连接TCPUDP监听进程内存管理相关报告 看其localaddress foreignaddress没打任何网络连接却established说明问题注意保证没任何网络连接包括台进程established说明才问题采纳哦
其他类似问题
为您推荐:
netstat的相关知识
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁网络的FIN_WAIT_2状态解释和分析 - kld208 - ITeye技术网站
博客分类:
关于网络设备的FIN_WAIT_2状态解释出处:/netdemon1981/blog/item/584bfbb2aeb1d4acd9335ad9.html
在HTTP应用中,存在一个问题,SERVER由于某种原因关闭连接,如KEEPALIVE的超时,这样,作为主动关闭的SERVER一方就会进入 FIN_WAIT2状态,但TCP/IP协议栈有个问题,FIN_WAIT2状态是没有超时的(不象TIME_WAIT状态),所以如果CLIENT不关闭,这个FIN_WAIT_2状态将保持到系统重新启动,越来越多的FIN_WAIT_2状态会致使内核crash。
  产生原因:
1。常连接并且当连接一直处于IDLE状态导致SERVER CLOSE时,CLIENT编程缺陷,没有向SERVER 发出FIN和ACK包
2。APACHE1.1和APACHE1.2增加了linger_close()函数,前面的帖子有介绍,这个函数可能引起了这个问题(为什么我也不清楚)
  解决办法:
1。对FIN_WAIT_2状态增加超时机制,这个特性在协议里没有体现,但在一些OS中已经实现
如:LINUX、SOLARIS、FREEBSD、HP-UNIX、IRIX等
2。不要用linger_close()编译
3。用SO_LINGER代替,这个在某些系统中还能很好地处理
4。增加用于存储网络连接状态的内存mbuf,以防止内核crash
5。DISABLE KEEPALIVE
TCP FIN_WAIT_2状态问题分析
出处:/huochai2020/item/eb3fcd48bace5
1、出现fin_wait_2一般为客户端,如果为服务端出现,则表明是服务端主动发起的断开。
C:\Documents and Settings\Administrator&netstat -an|findstr 10.208.8.2:
TCP&&& 10.88.2.26:9002&&&&&&& 10.208.8.2:1040&&&&&&& FIN_WAIT_2
TCP&&& 10.88.2.26:9002&&&&&&& 10.208.8.2:1048&&&&&&& FIN_WAIT_2
TCP&&& 10.88.2.26:9002&&&&&&& 10.208.8.2:1051&&&&&&& FIN_WAIT_2
TCP&&& 10.88.2.26:9002&&&&&&& 10.208.8.2:1052&&&&&&& FIN_WAIT_2
TCP&&& 10.88.2.26:9002&&&&&&& 10.208.8.2:1056&&&&&&& FIN_WAIT_2
TCP&&& 10.88.2.26:9002&&&&&&& 10.208.8.2:1058&&&&&&& FIN_WAIT_2 #netstat -an|grep 10.116.50.30
tcp&&&&&&& 0&&&&& 0 192.168.129.44.64306&& 10.116.50.30.53081&&&&&& FIN_WAIT_2
tcp&&&&&&& 0&&&&& 0 192.168.129.44.63611&& 10.116.50.30.57966&&&&&& FIN_WAIT_2
tcp&&&&&&& 0&&&&& 0 192.168.129.44.57835&& 10.116.50.30.49188&&&&&& FIN_WAIT_2
tcp&&&&&&& 0&&&&& 0 192.168.129.44.57502&& 10.116.50.30.52615&&&&&& ESTABLISHED
2、为什么发生
a.客户端状态迁移(主动结束连接)CLOSED-&SYN_SENT-&ESTABLISHED-&FIN_WAIT_1-&FIN_WAIT_2-&TIME_WAIT-&CLOSEDb.服务器状态迁移CLOSED-&LISTEN-&SYN收到-&ESTABLISHED-&CLOSE_WAIT-&LAST_ACK-&CLOSED 有缺陷的客户端与持久连接
有一些客户端在处理持久连接(aka keepalives)时存在问题。当连接空闲下来服务器关闭连接时(基于KeepAliveTimeout指令), 客户端的程序编制使它不发送FIN和ACK回服务器。这样就意味着这个连接 将停留在FIN_WAIT_2状态直到以下之一发生:
客户端为同一个或者不同的站点打开新的连接,这样会使它在该个套接字上完全关闭以前的连接。
用户退出客户端程序,这样在一些(也许是大多数?)客户端上会使操作系统完全关闭连接。
FIN_WAIT_2超时,在那些具有FIN_WAIT_2状态超时设置的服务器上。
如果你够幸运,这样意味着那些有缺陷的客户端会完全关闭连接并释放你服务器的资源。 然而,有一些情况下套接字永远不会完全关闭,比如一个拨号客户端在关闭客户端程序之前从ISP断开。 此外,有的客户端有可能空置好几天不创建新连接,并且这样在好几天里保持着套接字的有效即使已经不再使用。 这是浏览器或者操作系统的TCP实现的Bug。
3、如何解决
为 FIN_WAIT_2 增加 超时机制
开始-&运行-&输入regedit
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
在详细信息窗格中双击 TCPFinWait2Delay ,然后从 30 到 300 中输入一个值。
请注意 如果不存在 TCPFinWait2Delay 值,必须为 REG _ DWORD 注册表值来创建它。 注册表该值控制 TCP 连接之前等待的秒数它被强制关闭, 关闭 (s,SD_SEND) 函数调用之后。 在默认值为 240 秒。 此值范围是 30 到 300。 必须手动创建此注册表值。 否则,使用默认值。
#ndd -set /dev/tcp tcp_fin_wait_2_timeout 60000&& (1分钟)
执行上述命令,重起系统后将失效,如果需要一致起作用,则修改下列文件:/etc/rc.config.d/nddconf
设置参数tcp_fin_wait_2_timeout值。
禁止KeepAlive
Apache:编辑你的httpd.conf并把"KeepAlive On"改为"KeepAlive Off"。
使用linger:
linger&&&&&
lig.l_onoff=1;&&
lig.l_linger=0;&&
int&&& ilen=sizeof(linger);&&
setsockopt(Socket,SOL_SOCKET,SO_LINGER,(char*)&lig,ilen);
浏览: 88586 次
来自: 北京
这个解决了没?
kld208 写道public class XMemcache ...
讲的简单易懂,很好
为什么我的图标显示的是乱码啊~简析TCP协议的TIME_WAIT与CLOSE_WAIT状态_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
简析TCP协议的TIME_WAIT与CLOSE_WAIT状态
上传于||文档简介
&&简​析​T​C​P​协​议​的​T​I​M​E​_​W​A​I​T​与​C​L​O​S​E​_​W​A​I​T​状​态
阅读已结束,如果下载本文需要使用0下载券
想免费下载更多文档?
下载文档到电脑,查找使用更方便
还剩1页未读,继续阅读
你可能喜欢& TCP链接主动关闭不发fin包奇怪行为分析
TCP链接主动关闭不发fin包奇怪行为分析
原创文章,转载请注明: 转载自
本文链接地址:
问题描述:
多隆同学在做网络框架的时候,发现一条tcp链接在close的时候,对端会收到econnrest,而不是正常的fin包.
通过抓包发现close系统调用的时候,我端发出rst报文, 而不是正常的fin。这个问题比较有意思,我们来演示下:
Erlang R14B03 (erts-5.8.4)
[64-bit] [smp:16:16] [rq:16] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.8.4
(abort with ^G)
1& {ok,Sock} = gen_tcp:connect(&&, 80, [{active,false}]).
{ok,#Port&0.582&}
2& gen_tcp:send(Sock, &GET / HTTP/1.1\r\n\r\n&).
3& gen_tcp:close(Sock).
我们往baidu的首页发了个http请求,百度会给我们回应报文的,我们send完立即调用close.
然后我们在另外一个终端开tcpdump抓包确认:
$ sudo tcpdump port 80 -i bond0
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on bond0, link-type EN10MB (Ethernet), capture size 96 bytes
17:22:38.246507 IP my031089.sqa.cm4.tbsite.net.19500 & 220.181.111.86.http: S :(0) win 5840 &mss 1460,sackOK,timestamp
0,nop,wscale 7&
17:22:38.284602 IP 220.181.111.86.http & my031089.sqa.cm4.tbsite.net.19500: S :(0) ack
win 8190 &mss 1436&
17:22:38.284624 IP my031089.sqa.cm4.tbsite.net.19500 & 220.181.111.86.http: . ack 1 win 5840
17:22:52.748468 IP my031089.sqa.cm4.tbsite.net.19500 & 220.181.111.86.http: P 1:19(18) ack 1 win 5840
17:22:52.786855 IP 220.181.111.86.http & my031089.sqa.cm4.tbsite.net.19500: . ack 19 win 5840
17:22:52.787194 IP 220.181.111.86.http & my031089.sqa.cm4.tbsite.net.19500: P 1:179(178) ack 19 win 5840
17:22:52.787203 IP my031089.sqa.cm4.tbsite.net.19500 & 220.181.111.86.http: . ack 179 win 6432
17:22:52.787209 IP 220.181.111.86.http & my031089.sqa.cm4.tbsite.net.19500: P 179:486(307) ack 19 win 5840
17:22:52.787214 IP my031089.sqa.cm4.tbsite.net.19500 & 220.181.111.86.http: . ack 486 win 7504
17:23:01.564358 IP my031089.sqa.cm4.tbsite.net.19500 & 220.181.111.86.http: R 19:19(0) ack 486 win 7504
我们可以清楚的看到
R 19:19(0) ack 486 win 7504,发了个rst包,通过strace系统调用也确认erlang确实调用了close系统调用。
那为什么呢?,tcp协议栈专家回答了这个问题:
在net/ipv4/tcp.c:1900附近
/* As outlined in RFC 2525, section 2.17, we send a RST here because
* data was lost. To witness the awful effects of the old behavior of
* always doing a FIN, run an older 2.1.x kernel or 2.0.x, start a bulk
* GET in an FTP client, suspend the process, wait for the client to
* advertise a zero window, then kill -9 the FTP client, wheee...
* Note: timeout is always zero in such a case.
if (data_was_unread) {
/* Unread data was tossed, zap the connection. */
NET_INC_STATS_USER(sock_net(sk), LINUX_MIB_TCPABORTONCLOSE);
tcp_set_state(sk, TCP_CLOSE);
tcp_send_active_reset(sk, sk-&sk_allocation);
代码里面写的很清楚,如果你的接收缓冲去还有数据,协议栈就会发rst代替fin.
我们再来验证一下:
Erlang R14B03 (erts-5.8.4)
[64-bit] [smp:16:16] [rq:16] [async-threads:0] [hipe] [kernel-poll:false]
Eshell V5.8.4
(abort with ^G)
1& {ok,Sock} = gen_tcp:connect(&&, 80, [{active,false}]).
{ok,#Port&0.582&}
2& gen_tcp:send(Sock, &GET / HTTP/1.1\r\n\r\n&).
3& gen_tcp:recv(Sock,0).
{ok,&HTTP/1.1 400 Bad Request\r\nDate: Fri, 01 Jul :37 GMT\r\nServer: Apache\r\nConnection: Keep-Alive\r\nTransfer-Encoding: chunked\r\nContent-Type: text/ charset=iso-8859-1\r\n\r\n127\r\n&!DOCTYPE HTML PUBLIC \&-//IETF//DTD HTML 2.0//EN\&&\n&HTML&&HEAD&\n&TITLE&400 Bad Request&/TITLE&\n&/HEAD&&BODY&\n&H1&Bad Request&/H1&\nYour browser sent a request that this server could not understand.&P&\nclient sent HTTP/1.1 request without hostname (see RFC2616 section 14.23): /&P&\n&/BODY&&/HTML&\n\r\n0\r\n\r\n&}
4& gen_tcp:close(Sock).
这次我们把接收缓冲区里的东西拉干净了。
再看下tcpdump:
17:36:07.236627 IP my031089.sqa.cm4.tbsite.net.9405 & 123.125.114.144.http: S :(0) win 5840 &mss 1460,sackOK,timestamp
0,nop,wscale 7&
17:36:07.274661 IP 123.125.114.144.http & my031089.sqa.cm4.tbsite.net.9405: S ) ack
win 8190 &mss 1436&
17:36:07.274685 IP my031089.sqa.cm4.tbsite.net.9405 & 123.125.114.144.http: . ack 1 win 5840
17:36:10.295795 IP my031089.sqa.cm4.tbsite.net.9405 & 123.125.114.144.http: P 1:19(18) ack 1 win 5840
17:36:10.334280 IP 123.125.114.144.http & my031089.sqa.cm4.tbsite.net.9405: . ack 19 win 5840
17:36:10.334547 IP 123.125.114.144.http & my031089.sqa.cm4.tbsite.net.9405: P 1:179(178) ack 19 win 5840
17:36:10.334554 IP my031089.sqa.cm4.tbsite.net.9405 & 123.125.114.144.http: . ack 179 win 6432
17:36:10.334563 IP 123.125.114.144.http & my031089.sqa.cm4.tbsite.net.9405: P 179:486(307) ack 19 win 5840
17:36:10.334566 IP my031089.sqa.cm4.tbsite.net.9405 & 123.125.114.144.http: . ack 486 win 7504
17:36:19.671374 IP my031089.sqa.cm4.tbsite.net.9405 & 123.125.114.144.http: F 19:19(0) ack 486 win 7504
17:36:19.709619 IP 123.125.114.144.http & my031089.sqa.cm4.tbsite.net.9405: . ack 20 win 5840
17:36:19.709643 IP 123.125.114.144.http & my031089.sqa.cm4.tbsite.net.9405: F 486:486(0) ack 20 win 5840
17:36:19.709652 IP my031089.sqa.cm4.tbsite.net.9405 & 123.125.114.144.http: . ack 487 win 7504
这次是发fin包了。
多隆同学再进一步,找出来之前squid client代码中不能理解的一句话:
client_side.c
/* prevent those nasty RST packets */
char buf[SQUID_TCP_SO_RCVBUF];
while (FD_READ_METHOD(fd, buf, SQUID_TCP_SO_RCVBUF) & 0);
总算明白了这句话的意思了!
小结:认真学习协议栈太重要了。
玩得开心!
Post Footer automatically generated by
for wordpress.
No related posts.
Categories: ,
buy me a coffee.
阿里核心系统数据库组招募高手!
招聘信息:
Recent Posts}

我要回帖

更多关于 tcp协议栈 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信