1月 102014
 

#netstat -n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’

LAST_ACK 14
SYN_RECV 348
ESTABLISHED 70
FIN_WAIT1 229
FIN_WAIT2 30
CLOSING 33
TIME_WAIT 18122

状态:描述
CLOSED:无连接是活动的或正在进行
LISTEN:服务器在等待进入呼叫
SYN_RECV:一个连接请求已经到达,等待确认
SYN_SENT:应用已经开始,打开一个连接
ESTABLISHED:正常数据传输状态
FIN_WAIT1:应用说它已经完成
FIN_WAIT2:另一边已同意释放
ITMED_WAIT:等待所有分组死掉
CLOSING:两边同时尝试关闭
TIME_WAIT:另一边已初始化一个释放
LAST_ACK:等待所有分组死掉

也就是说,这条命令可以把当前系统的网络连接状态分类汇总。

下面解释一下为啥要这样写:

一个简单的管道符连接了netstat和awk命令。

先来看看netstat:

netstat -n

Active Internet connections (w/o servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 123.123.123.123:80 234.234.234.234:12345 TIME_WAIT

你实际执行这条命令的时候,可能会得到成千上万条类似上面的记录,不过我们就拿其中的一条就足够了。

再来看看awk:

/^tcp/
滤出tcp开头的记录,屏蔽udp, socket等无关记录。

state[]
相当于定义了一个名叫state的数组

NF
表示记录的字段数,如上所示的记录,NF等于6

$NF
表示某个字段的值,如上所示的记录,$NF也就是$6,表示第6个字段的值,也就是TIME_WAIT

state[$NF]
表示数组元素的值,如上所示的记录,就是state[TIME_WAIT]状态的连接数

++state[$NF]
表示把某个数加一,如上所示的记录,就是把state[TIME_WAIT]状态的连接数加一

END
表示在最后阶段要执行的命令

for(key in state)
遍历数组

print key,”t”,state[key]
打印数组的键和值,中间用t制表符分割,美化一下。

如发现系统存在大量TIME_WAIT状态的连接,通过调整内核参数解决,
vim /etc/sysctl.conf
编辑文件,加入以下内容:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30
然后执行 /sbin/sysctl -p 让参数生效。

net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
net.ipv4.tcp_fin_timeout 修改系?默认的 TIMEOUT 时间

下面附上TIME_WAIT状态的意义:

客户端与服务器端建立TCP/IP连接后关闭SOCKET后,服务器端连接的端口
状态为TIME_WAIT

是不是所有执行主动关闭的socket都会进入TIME_WAIT状态呢?
有没有什么情况使主动关闭的socket直接进入CLOSED状态呢?

主动关闭的一方在发送最后一个 ack 后
就会进入 TIME_WAIT 状态 停留2MSL(max segment lifetime)时间
这个是TCP/IP必不可少的,也就是“解决”不了的。

也就是TCP/IP设计者本来是这么设计的
主要有两个原因
1。防止上一次连接中的包,迷路后重新出现,影响新连接
(经过2MSL,上一次连接中所有的重复包都会消失)
2。可靠的关闭TCP连接
在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发
fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以
主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。

TIME_WAIT 并不会占用很大资源的,除非受到攻击。

8月 142013
 

转自:http://ganquan.org/blog/2009/09/tcp%E5%8D%8F%E8%AE%AE%E7%9A%84time_wait%E7%8A%B6%E6%80%81%E8%AF%A6%E8%A7%A3/

我发现有个公司面试的时候问了对TCP协议比较细节的地方,那就写一下帮助自己加深理解和记忆。在开始说TIME_WAIT状态之前,要知道TCP协议是如何关闭连接的。
很多人对TCP协议的三次握手都很熟悉(不知道的可以参考我以前的帖子),因为学校的垃圾考试都爱考三次握手,但是很多知道三次握手的人都对TCP协议是如何关闭连接不了解。不说废话了,TCP关闭连接过程如下图,寡人用photoshop画的,凑合看一下:
tcpclose
为了方便描述,我给这个TCP连接的一端起名为Client,给另外一端起名为Server。上图描述的是Client主动关闭的过程,FTP协议中就这样的。如果要描述Server主动关闭的过程,只要交换描述过程中的Server和Client就可以了,HTTP协议就是这样的。

描述过程:
Client调用close()函数,给Server发送FIN,请求关闭连接;Server收到FIN之后给Client返回确认ACK,同时关闭读通道(不清楚就去看一下shutdown和close的差别),也就是说现在不能再从这个连接上读取东西,现在read返回0。此时Server的TCP状态转化为CLOSE_WAIT状态。
Client收到对自己的FIN确认后,关闭 写通道,不再向连接中写入任何数据。
接下来Server调用close()来关闭连接,给Client发送FIN,Client收到后给Server回复ACK确认,同时Client关闭读通道,进入TIME_WAIT状态。
Server接收到Client对自己的FIN的确认ACK,关闭写通道,TCP连接转化为CLOSED,也就是关闭连接。
Client在TIME_WAIT状态下要等待最大数据段生存期的两倍,然后才进入CLOSED状态,TCP协议关闭连接过程彻底结束。

以上就是TCP协议关闭连接的过程,现在说一下TIME_WAIT状态。
从上面可以看到,主动发起关闭连接的操作的一方将达到TIME_WAIT状态,而且这个状态要保持Maximum Segment Lifetime的两倍时间。为什么要这样做而不是直接进入CLOSED状态?

原因有二:
一、保证TCP协议的全双工连接能够可靠关闭
二、保证这次连接的重复数据段从网络中消失

先说第一点,如果Client直接CLOSED了,那么由于IP协议的不可靠性或者是其它网络原因,导致Server没有收到Client最后回复的ACK。那么Server就会在超时之后继续发送FIN,此时由于Client已经CLOSED了,就找不到与重发的FIN对应的连接,最后Server就会收到RST而不是ACK,Server就会以为是连接错误把问题报告给高层。这样的情况虽然不会造成数据丢失,但是却导致TCP协议不符合可靠连接的要求。所以,Client不是直接进入CLOSED,而是要保持TIME_WAIT,当再次收到FIN的时候,能够保证对方收到ACK,最后正确的关闭连接。

再说第二点,如果Client直接CLOSED,然后又再向Server发起一个新连接,我们不能保证这个新连接与刚关闭的连接的端口号是不同的。也就是说有可能新连接和老连接的端口号是相同的。一般来说不会发生什么问题,但是还是有特殊情况出现:假设新连接和已经关闭的老连接端口号是一样的,如果前一次连接的某些数据仍然滞留在网络中,这些延迟数据在建立新连接之后才到达Server,由于新连接和老连接的端口号是一样的,又因为TCP协议判断不同连接的依据是socket pair,于是,TCP协议就认为那个延迟的数据是属于新连接的,这样就和真正的新连接的数据包发生混淆了。所以TCP连接还要在TIME_WAIT状态等待2倍MSL,这样可以保证本次连接的所有数据都从网络中消失。

各种协议都是前人千锤百炼后得到的标准,规范。从细节中都能感受到精巧和严谨。每次深入都有同一个感觉,精妙。

 
8月 142013
 

转自:http://blog.csdn.net/shootyou/article/details/6622226

昨天解决了一个HttpClient调用错误导致的服务器异常,具体过程如下:

http://blog.csdn.net/shootyou/article/details/6615051

里头的分析过程有提到,通过查看服务器网络状态检测到服务器有大量的CLOSE_WAIT的状态。

 

在服务器的日常维护过程中,会经常用到下面的命令:

 

[plain] view plaincopyprint?

 

 

 

 

  1. netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'    

它会显示例如下面的信息:

TIME_WAIT 814
CLOSE_WAIT 1
FIN_WAIT1 1
ESTABLISHED 634
SYN_RECV 2
LAST_ACK 1

常用的三个状态是:ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭。

 

具体每种状态什么意思,其实无需多说,看看下面这种图就明白了,注意这里提到的服务器应该是业务请求接受处理的一方:

 

这么多状态不用都记住,只要了解到我上面提到的最常见的三种状态的意义就可以了。一般不到万不得已的情况也不会去查看网络状态,如果服务器出了异常,百分之八九十都是下面两种情况:

1.服务器保持了大量TIME_WAIT状态

2.服务器保持了大量CLOSE_WAIT状态

因为linux分配给一个用户的文件句柄是有限的(可以参考:http://blog.csdn.net/shootyou/article/details/6579139),而TIME_WAIT和CLOSE_WAIT两种状态如果一直被保持,那么意味着对应数目的通道就一直被占着,而且是“占着茅坑不使劲”,一旦达到句柄数上限,新的请求就无法被处理了,接着就是大量Too Many Open Files异常,tomcat崩溃。。。

下面来讨论下这两种情况的处理方法,网上有很多资料把这两种情况的处理方法混为一谈,以为优化系统内核参数就可以解决问题,其实是不恰当的,优化系统内核参数解决TIME_WAIT可能很容易,但是应对CLOSE_WAIT的情况还是需要从程序本身出发。现在来分别说说这两种情况的处理方法:

 

1.服务器保持了大量TIME_WAIT状态

这种情况比较常见,一些爬虫服务器或者WEB服务器(如果网管在安装的时候没有做内核参数优化的话)上经常会遇到这个问题,这个问题是怎么产生的呢?

从上面的示意图可以看得出来,TIME_WAIT是主动关闭连接的一方保持的状态,对于爬虫服务器来说他本身就是“客户端”,在完成一个爬取任务之后,他就会发起主动关闭连接,从而进入TIME_WAIT的状态,然后在保持这个状态2MSL(max segment lifetime)时间之后,彻底关闭回收资源。为什么要这么做?明明就已经主动关闭连接了为啥还要保持资源一段时间呢?这个是TCP/IP的设计者规定的,主要出于以下两个方面的考虑:

1.防止上一次连接中的包,迷路后重新出现,影响新连接(经过2MSL,上一次连接中所有的重复包都会消失)
2.可靠的关闭TCP连接。在主动关闭方发送的最后一个 ack(fin) ,有可能丢失,这时被动方会重新发fin, 如果这时主动方处于 CLOSED 状态 ,就会响应 rst 而不是 ack。所以主动方要处于 TIME_WAIT 状态,而不能是 CLOSED 。另外这么设计TIME_WAIT 会定时的回收资源,并不会占用很大资源的,除非短时间内接受大量请求或者受到攻击。

关于MSL引用下面一段话:

 

[plain] view plaincopyprint?

 

 

 

 

  1. MSL 為一個 TCP Segment (某一塊 TCP 網路封包) 從來源送到目的之間可續存的時間 (也就是一個網路封包在網路上傳輸時能存活的時間),由於 RFC 793 TCP 傳輸協定是在 1981 年定義的,當時的網路速度不像現在的網際網路那樣發達,你可以想像你從瀏覽器輸入網址等到第一個 byte 出現要等 4 分鐘嗎?在現在的網路環境下幾乎不可能有這種事情發生,因此我們大可將 TIME_WAIT 狀態的續存時間大幅調低,好讓 連線埠 (Ports) 能更快空出來給其他連線使用。  

 

再引用网络资源的一段话:

[plain] view plaincopyprint?

 

 

 

 

  1. 值得一说的是,对于基于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不会太费时间,但是有这么多状态要维护总是不好。  
  2. HTTP协议1.1版规定default行为是Keep-Alive,也就是会重用TCP连接传输多个 request/response,一个主要原因就是发现了这个问题。  

也就是说HTTP的交互跟上面画的那个图是不一样的,关闭连接的不是客户端,而是服务器,所以web服务器也是会出现大量的TIME_WAIT的情况的。

 
现在来说如何来解决这个问题。
 
解决思路很简单,就是让服务器能够快速回收和重用那些TIME_WAIT的资源。
 
下面来看一下我们网管对/etc/sysctl.conf文件的修改:
[plain] view plaincopyprint?

 

 

 

 

  1. #对于一个新建连接,内核要发送多少个 SYN 连接请求才决定放弃,不应该大于255,默认值是5,对应于180秒左右时间   
  2. net.ipv4.tcp_syn_retries=2  
  3. #net.ipv4.tcp_synack_retries=2  
  4. #表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为300秒  
  5. net.ipv4.tcp_keepalive_time=1200  
  6. net.ipv4.tcp_orphan_retries=3  
  7. #表示如果套接字由本端要求关闭,这个参数决定了它保持在FIN-WAIT-2状态的时间  
  8. net.ipv4.tcp_fin_timeout=30    
  9. #表示SYN队列的长度,默认为1024,加大队列长度为8192,可以容纳更多等待连接的网络连接数。  
  10. net.ipv4.tcp_max_syn_backlog = 4096  
  11. #表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭  
  12. net.ipv4.tcp_syncookies = 1  
  13.   
  14. #表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭  
  15. net.ipv4.tcp_tw_reuse = 1  
  16. #表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭  
  17. net.ipv4.tcp_tw_recycle = 1  
  18.   
  19. ##减少超时前的探测次数   
  20. net.ipv4.tcp_keepalive_probes=5   
  21. ##优化网络设备接收队列   
  22. net.core.netdev_max_backlog=3000   
[plain] view plaincopyprint?

 

 

 

 

  1.   
修改完之后执行/sbin/sysctl -p让参数生效。
 
这里头主要注意到的是net.ipv4.tcp_tw_reuse
net.ipv4.tcp_tw_recycle
net.ipv4.tcp_fin_timeout
net.ipv4.tcp_keepalive_*
这几个参数。
 
net.ipv4.tcp_tw_reuse和net.ipv4.tcp_tw_recycle的开启都是为了回收处于TIME_WAIT状态的资源。
net.ipv4.tcp_fin_timeout这个时间可以减少在异常情况下服务器从FIN-WAIT-2转到TIME_WAIT的时间。
net.ipv4.tcp_keepalive_*一系列参数,是用来设置服务器检测连接存活的相关配置。
 
2.服务器保持了大量CLOSE_WAIT状态
休息一下,喘口气,一开始只是打算说说TIME_WAIT和CLOSE_WAIT的区别,没想到越挖越深,这也是写博客总结的好处,总可以有意外的收获。
 
TIME_WAIT状态可以通过优化服务器参数得到解决,因为发生TIME_WAIT的情况是服务器自己可控的,要么就是对方连接的异常,要么就是自己没有迅速回收资源,总之不是由于自己程序错误导致的。
但是CLOSE_WAIT就不一样了,从上面的图可以看出来,如果一直保持在CLOSE_WAIT状态,那么只有一种情况,就是在对方关闭连接之后服务器程序自己没有进一步发出ack信号。换句话说,就是在对方连接关闭之后,程序里没有检测到,或者程序压根就忘记了这个时候需要关闭连接,于是这个资源就一直被程序占着。个人觉得这种情况,通过服务器内核参数也没办法解决,服务器对于程序抢占的资源没有主动回收的权利,除非终止程序运行。
 
如果你使用的是HttpClient并且你遇到了大量CLOSE_WAIT的情况,那么这篇日志也许对你有用:http://blog.csdn.net/shootyou/article/details/6615051
在那边日志里头我举了个场景,来说明CLOSE_WAIT和TIME_WAIT的区别,这里重新描述一下:
服务器A是一台爬虫服务器,它使用简单的HttpClient去请求资源服务器B上面的apache获取文件资源,正常情况下,如果请求成功,那么在抓取完资源后,服务器A会主动发出关闭连接的请求,这个时候就是主动关闭连接,服务器A的连接状态我们可以看到是TIME_WAIT。如果一旦发生异常呢?假设请求的资源服务器B上并不存在,那么这个时候就会由服务器B发出关闭连接的请求,服务器A就是被动的关闭了连接,如果服务器A被动关闭连接之后程序员忘了让HttpClient释放连接,那就会造成CLOSE_WAIT的状态了。
 
所以如果将大量CLOSE_WAIT的解决办法总结为一句话那就是:查代码。因为问题出在服务器程序里头啊。
 
6月 062013
 

转自:http://www.360doc.com/content/10/1009/17/2245786_59637481.shtml

TCP状态迁移

大家对netstat -a命令很熟悉,但是,你有没有注意到STATE一栏呢,基本上显示着established,time_wait,close_wait等,这些到底是 什么意思呢,在这篇文章,我将会详细的阐述。

大家很明白TCP初始化连接三次握手吧:发SYN包,然后返回SYN/ACK包,再发ACK包,连接正式建立。但是这里有点出入,当请求者收到SYS /ACK包后,就开始建立连接了,而被请求者第三次握手结束后才建立连接。但是大家明白关闭连接的工作原理吗?关闭连接要四次握手:发FIN包,ACK 包,FIN包,ACK包,四次握手!!为什么呢,因为TCP连接是全双工,我关了你的连接,并不等于你关了我的连接。




客户端TCP状态迁移:

CLOSED->SYN_SENT->ESTABLISHED->FIN_WAIT_1->FIN_WAIT_2->TIME_WAIT->CLOSED

服务器TCP状态迁移:

CLOSED->LISTEN->SYN收到 ->ESTABLISHED->CLOSE_WAIT->LAST_ACK->CLOSED



当客户端开始连接时,服务器还处于LISTENING,

客户端发一个SYN包后,他就处于SYN_SENT状态,服务器就处于SYS收到状态,

然后互相确认进入连接状态ESTABLISHED.



当客户端请求关闭连接时,客户端发送一个FIN包后,客户端就进入FIN_WAIT_1状态,等待对方的确认包,

服务器发送一个ACK包给客户,客户端收到ACK包后结束FIN_WAIT_1状态,进入FIN_WAIT_2状态,等待服务器发过来的关闭请求,

服务器发一个FIN包后,进入CLOSE_WAIT状态,

当客户端收到服务器的FIN包,FIN_WAIT_2状态就结束,然后给服务器端的FIN包给以一个确认包,客户端这时进入TIME_WAIT,

当服务器收到确认包后,CLOSE_WAIT状态结束了,

这时候服务器端真正的关闭了连接.但是客户端还在TIME_WAIT状态下,



什么时候结束呢.我在这里再讲到一个新名词:2MSL等待状态,其实TIME_WAIT就是2MSL等待状态,

为什么要设置这个状态,原因是有足够的时间让ACK包到达服务器端,如果服务器端没收到ACK包,超时了,然后重新发一个FIN包,直到服务器收到ACK 包.



TIME_WAIT状态等待时间是在TCP重新启动后不连接任何请求的两倍.

大家有没有发现一个问题:如果对方在第三次握手的时候出问题,如发FIN包的时候,不知道什么原因丢了这个包,然而这边一直处在FIN_WAIT_2状 态,而且TCP/IP并没有设置这个状态的过期时间,那他一直会保留这个状态下去,越来越多的FIN_WAIT_2状态会导致系统崩溃.

上面我碰到的这个问题主要因为TCP的结束流程未走完,造成连接未释放。现设客户端主动断开连接,流程如下

 

如上图所示,



Client                            消息                                    Server

         close()

—— FIN ——->

FIN_WAIT1                                                         CLOSE_WAIT

<—– ACK ——-

FIN_WAIT2 

close()

<—— FIN ——                     

TIME_WAIT                                                       LAST_ACK      

                                      —— ACK ——-> 

CLOSED

CLOSED



由于Server的Socket在客户端已经关闭时而没有调用关闭,

造成服务器端的连接处在“挂起”状态,而客户端则处在等待应答的状态上。

此问题的典型特征是:

一端处于FIN_WAIT2 ,而另一端处于CLOSE_WAIT.

不过,根本问题还是程序写的不好,有待提高

————————————————————————-

CLOSE_WAIT,TCP的癌症,TCP的朋友。

CLOSE_WAIT状态的生成原因

首先我们知道,如果我们的服务器程序APACHE处于CLOSE_WAIT状态的话,说明套接字是被动关闭的!

因为如果是CLIENT端主动断掉当前连接的话,那么双方关闭这个TCP连接共需要四个packet:

Client —> FIN —> Server

Client <— ACK <— Server

这时候Client端处于FIN_WAIT_2状态;而Server 程序处于CLOSE_WAIT状态。

Client <— FIN <— Server

这时Server 发送FIN给Client,Server 就置为LAST_ACK状态。

Client —> ACK —> Server

Client回应了ACK,那么Server 的套接字才会真正置为CLOSED状态。

Server 程序处于CLOSE_WAIT状态,而不是LAST_ACK状态,说明还没有发FIN给Client,那么可能是在关闭连接之前还有许多数据要发送或者其 他事要做,导致没有发这个FIN packet。

通常来说,一个CLOSE_WAIT会维持至少2个小时的时间。如果有个流氓特地写了个程序,给你造成一堆的 CLOSE_WAIT,消耗你的资源,那么通常是等不到释放那一刻,系统就已经解决崩溃了。

只能通过修改一下TCP/IP的参数,来缩短这个时间:修改tcp_keepalive_*系列参数有助于解决这个 问题。

 

解决这个问题的方法是修改系统的参数,系统默认超时时间的是7200秒,也就是2小时, 这个太大了,可以修改如下几个参数:

sysctl -w net.ipv4.tcp_keepalive_time=30

sysctl -w net.ipv4.tcp_keepalive_probes=2

sysctl -w net.ipv4.tcp_keepalive_intvl=2

然后,执行sysctl命令使修改生效。

 

连接进程是通过一系列状态表示的,这些状态有:

LISTEN,SYN-SENT,SYN-RECEIVED,ESTABLISHED,FIN-WAIT-1,FIN-WAIT-2,CLOSE- WAIT,CLOSING,LAST-ACK,TIME-WAIT和CLOSED。




各个状态的意义如下: 

LISTEN – 侦听来自远方TCP端口的连接请求; 

SYN-SENT -在发送连接请求后等待匹配的连接请求; 

SYN-RECEIVED – 在收到和发送一个连接请求后等待对连接请求的确认; 

ESTABLISHED- 代表一个打开的连接,数据可以传送给用户; 

FIN-WAIT-1 – 等待远程TCP的连接中断请求,或先前的连接中断请求的确认;

FIN-WAIT-2 – 从远程TCP等待连接中断请求; 

CLOSE-WAIT – 等待从本地用户发来的连接中断请求; 

CLOSING -等待远程TCP对连接中断的确认; 

LAST-ACK – 等待原来发向远程TCP的连接中断请求的确认; 

TIME-WAIT -等待足够的时间以确保远程TCP接收到连接中断请求的确认; 

CLOSED – 没有任何连接状态;



TCP连接过程是状态的转换,促使发生状态转换的是用户调用:

OPEN,SEND,RECEIVE,CLOSE,ABORT和STATUS;

传送过来的数据段,特别那些包括以下标记的数据段SYN,ACK,RST和FIN;

还有超时,上面所说的都会时TCP状态发生变化。

 

TCP连接的状态转换图

 

 

n多人都 知道,排除和定 位网络或系统故障大有帮助,但是怎牢牢地这张图刻在中呢?那你就一定要对 这张图一个状态,及转换的过程有深刻地认识,不能只停留在一知半解之中。下面对这张图的11种状 态详细解释一下,以便加强记忆!不过在这之前,先回顾一下TCP建立连接的三次握手过程,以及关闭连接的四次握手过程。

1、建立连接协议(三次握手)

1)客户 端发送一个带SYN标志的TCP报文到服务器。这是三次握手过程中的报文1

2) 服务器端回应客户端的,这是三次握手中的第2个报文,这个报文同时带ACK标志和SYN标 志。因此它表示对刚才客户端SYN报文的回应;同时又标志SYN给客户端,询问客户端是否准备好进行数据通 讯。

3) 客户必须再次回应服务段一个ACK报文,这是报文段3

2、连接终止协议(四次握手)

   由于TCP连 接是全双工的,因此每个方向都必须单独进行关闭。这原则是当一方完成它的数据发送任务后就能发送一个FIN来终 止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接 在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

 (1 TCP客 户端发送一个FIN,用来关闭客户到服务器的数据传送(报文段4)。

 (2) 服务器收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一 样,一个FIN将占用一个序号。

 (3) 服务器关闭客户端的连接,发送一个FIN给客户端(报文段6)。

 (4) 客户段发回ACK报文确认,并将确认序号设置为收到序号加1(报文段7)。

CLOSED: 这个没什么好说的了,表示初始状态。

LISTEN: 这个也是非常容易理解的一个状态,表示服务器端的某个SOCKET处 于监听状态,可以接受连接了。

SYN_RCVD: 这个状态表示接受到了SYN报 文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手 过程中最后一个ACK报文不予发送。因此这种状态时,当收到客户端的ACK报文 后,它会进入到ESTABLISHED状态。

SYN_SENT: 这个状态与SYN_RCVD遥想呼应,当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。SYN_SENT状态表示客户端已发送SYN报文。

ESTABLISHED:这个容易理解了,表示连接已经建立了。

FIN_WAIT_1: 这个状态要好好解释一下,其实FIN_WAIT_1FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报 文。而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKETESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况 下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。

FIN_WAIT_2:上面已经详细解释了这种状态,实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点 数据需要传送给你,稍后再关闭连接。

TIME_WAIT: 表示收到了对方的FIN报 文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。如果FIN_WAIT_1状态下,收到了对方同时带FIN标 志和ACK标志的报文时,可以直接进入到TIME_WAIT状态,而无须经过FIN_WAIT_2状态。

CLOSING: 这种状态比较特殊,实际情况中应该是很少见,属于一种比较罕见的例外状态。正常情况下,当你发 送FIN报文后,按理来说是应该先收到(或同时收到)对方的ACK报 文,再收到对方的FIN报文。但是CLOSING状态表示你发送FIN报文后,并没有收到对方的ACK报 文,反而却也收到了对方的FIN报文。什么情况下会出现此种情况呢?其实细想一下,也不难得出结论:那就是如果双方几乎在同时close一 个SOCKET的话,那么就出现了双方同时发送FIN报文的情况,也即会出现CLOSING状态,表示双方都正在关闭SOCKET连接。

CLOSE_WAIT: 这种状态的含义其实是表示在等待关闭。怎么理解呢?当对方close一 个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文 给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话, 那么你也就可以close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。

LAST_ACK: 这个状态还是比较容易好理解的,它是被动关闭一方在发送FIN报 文后,最后等待对方的ACK报文。当收到ACK报文后,也即可以进入到CLOSED可用状态了。

最后有2个问题 的回答,我自己分析后的结论(不一定保证100%正确)

1、 为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?

这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACKSYNACK起 应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文 通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文 和FIN报文多数情况下都是分开发送的。

2、 为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状 态?

这是因为:虽然双方 都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状 态(就好比从SYN_SEND状态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报 文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报 文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报 文,并保证于此。

 

 

 

 

 

        断开连接的时候, 当发起主动关闭的左边这方发送一个FIN过去后,

右边被动关闭的这方要回应一个ACK,这个ACK是TCP回应的,而不是应用程序发送的,

此时,被动关闭的一方就处于CLOSE_WAIT状态了。




如果此时被动关闭的这一方不再继续调用closesocket,那么他就不会发送接下来的FIN,导致自己老是处于CLOSE_WAIT。

只有被动关闭的这一方调用了 closesocket,才会发送一个FIN给主动关闭的这一方,同时也使得自己的状态变迁为LAST_ACK。 



比如被动关闭的是客户端.



当对方调用closesocket的时候,你的程序正在 



int nRet = recv(s,….); 

if (nRet == SOCKET_ERROR) 



// closesocket(s); 

return FALSE; 





很多人就是忘记了那句closesocket,这种代码太常见了。 

我的理解,

当主动关闭的一方发送FIN到被动关闭这边后,被动关闭这边的TCP马上回应一个ACK过去,同时向上面应用程序提交一个ERROR,

导致上面的SOCKET的send或者recv返回SOCKET_ERROR.



正常情况下,如果上面在返回SOCKET_ERROR后调用了closesocket, 那么被动关闭的者一方的TCP就会发送一个FIN过去,自己的状态就变迁到LAST_ACK.

 

 

服务器上出现大量的close_wait的例子和解决方法(例子从网上找的,基本差不多)



[email protected] oracle9i]$ /usr/sbin/lsof -i | grep 6800

oracle    22725 oracle9i    3u IPv4 18621468       TCP RHEL3:6800 (LISTEN)

oracle    22725 oracle9i    4u IPv4 18621469       TCP RHEL3:6800->RHEL3:2174 (CLOSE_WAIT)

oracle    22725 oracle9i    8u IPv4 18621568       TCP RHEL3:6800->RHEL3:2175 (CLOSE_WAIT)

oracle    22725 oracle9i    9u IPv4 18621578       TCP RHEL3:6800->RHEL3:2176 (CLOSE_WAIT)

oracle    22726 oracle9i    3u IPv4 18621468       TCP RHEL3:6800 (LISTEN)

oracle    22726 oracle9i    4u IPv4 18621469       TCP RHEL3:6800->RHEL3:2174 (CLOSE_WAIT)

oracle    22726 oracle9i    8u IPv4 18621568       TCP RHEL3:6800->RHEL3:2175 (CLOSE_WAIT)

oracle    22726 oracle9i    9u IPv4 18621578       TCP RHEL3:6800->RHEL3:2176 (CLOSE_WAIT)



[[email protected] oracle9i]$ kill -9 22725



# 22725, 22726就是使用该6800端口的进程号(PID)。

[[email protected] oracle9i]$ /usr/sbin/lsof -i | grep 6800

进程被kill时,会释放占用的所有链接句柄。 

FIN_WAIT、TIME_WAIT与CLOSE_WAIT的含义

 tcp/ip  FIN_WAIT、TIME_WAIT与CLOSE_WAIT的含义已关闭评论
4月 182013
 

netstat显示的连接状态有几种WAIT: FIN_WAIT_1,FIN_WAIT_2,CLOSE_WAIT和TIME_WAIT. 他们的含义要从TCP的连接中断过程说起

Server              Client
  -------- FIN -------->
  <------- ACK ---------
  <------- FIN ---------
  -------- ACK -------->

假设服务器主动关闭连接(Active Close)

  1. 服务器首先向客户机发送FIN包,然后服务器进入FIN_WAIT_1状态。
  2. 客户机向服务器确认FIN包收到,向服务器发送FIN/ACK,客户机进入CLOSE_WAIT状态。
  3. 服务器收到来自客户机的FIN/ACK后,进入FIN_WAIT_2状态
  4. 现在客户机进入被动关闭(“passive close”)状态,客户机操作系统等待他上面的应用程序关闭连接。一旦连接被关闭,客户端会发送FIN包到服务器
  5. 当服务器收到FIN包后,服务器会向客户机发送FIN/ACK确认,然后进入著名的TIME_WAIT状态

由于在连接关闭后,还不能确定所有连接关闭前的包都被服务器接受到了(包的接受是没有先后顺序的),因此有了TIME_WAIT状态。在这个状态中,服务器仍然在等待客户机发送的但是还未到达服务器的包。这个状态将保持2*MSL的时间,这里的MSL指的是一个TCP包在网络中存在的最长时间。一般情况下2*MSL=240秒。

转自:http://www.cnblogs.com/happyhotty/articles/1877925.html