TCP的状态兼谈Close_Wait和Time_Wait的状态

 

一些查看链接状态的命令

 

TCP连接中的TIME_WAIT状态 

netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"\t",state[key]}'

 

TCP的状态:

LISTEN 首先服务端需要打开一个socket进行监听,状态为LISTEN.

SYN_SENT:客户端通过应用程序调用connect进行active open.于是客户端tcp发送一个SYN以请求建立一个连接.之后状态置为SYN_SENT.

SYN_RECV:服务端应发出ACK确认客户端的SYN,同时自己向客户端发送一个SYN.之后状态置为SYN_RECV

ESTABLISHED: 代表一个打开的连接,双方可以进行或已经在数据交互了

FIN_WAIT1:主动关闭(active close)端(可以是客户端也可以是服务器端)应用程序调用close,于是其TCP发出FIN请求主动关闭连接,之后进入FIN_WAIT1状态

CLOSE_WAIT:被动关闭(passive close)端TCP接到FIN后,就发出ACK以回应FIN请求(它的接收也作为文件结束符传递给上层应用程序),并进入CLOSE_WAIT.

FIN_WAIT2:主动关闭端接到ACK后,就进入了FIN_WAIT2

LAST_ACK:被动关闭端一段时间后,接收到文件结束符的应用程序将调用CLOSE关闭连接。这导致它的TCP也发送一个 FIN,等待对方的ACK.就进入了LAST_ACK

TIME_WAIT:在主动关闭端接收到FIN后,TCP就发送ACK包,并进入TIME_WAIT状态

 

  CLOSE_WAIT状态解释  CLOSE_WAIT状态的生成原因 

通过TCP的状态图我们可以看出只有被动关闭的一端才有CLOSE_WAIT状态,当收到Fin并发送了Ack后 

服务器状态就变成了CLOSE_WAIT状态,如果我们的服务器一直处于CLOSE_WAIT状态的话,说明套接字是被动关闭的,并且没有发送Fin信令!原因往往是没有调用TCP的CloseSocket。 


解决CLOSE_WAIT的方法: 

1 一般原因都是TCP连接没有调用关闭方法。需要应用来处理网络链接关闭。 

2 对于Web请求出现这个原因,经常是因为Response的BodyStream没有调用Close. 

比如Widnows下: 

使用HttpWebRequest 一定要保证GetRequestStream和GetResponse对象关闭,否则容易造成连接处于CLOSE_WAIT状态 

3 TCP的KeepLive功能,可以让操作系统替我们自动清理掉CLOSE_WAIT的连接。 

但是KeepLive在Windows操作系统下默认是7200秒,也就是2个小时才清理一次。往往满足不了要求。可以调小该数值。 

Windows下的调整方法为 

HKEY_LOCAL_MACHINE/CurrentControlSet/Services/Tcpip/Parameters下的以下三个参数: 

KeepAliveInterval,设置其值为1000 www.2cto.com 

KeepAliveTime,设置其值为300000(单位为毫秒,300000代表5分钟) 

TcpMaxDataRetransmissions,设置其值为5 

Close_Wait引发的问题: 

Close_Wait会占用一个连接,网络可用连接小。数量过多,可能会引起网络性能下降,并占用系统非换页内存。 尤其是在有连接池的情况下(比如HttpRequest) 

会耗尽连接池的网络连接数,导致无法建立网络连接

#cat /proc/sys/net/ipv4/tcp_keepalive_time
7200
#cat /proc/sys/net/ipv4/tcp_keepalive_intvl
75
#cat /proc/sys/net/ipv4/tcp_keepalive_probes
9
 
// Set
#echo 1000> /proc/sys/net/ipv4/tcp_keepalive_time  //多长时间之内没有数据到来就进行探测
#echo 50> /proc/sys/net/ipv4/tcp_keepalive_intvl       //发送探测包的间隔
#echo 20> /proc/sys/net/ipv4/tcp_keepalive_probes   //尝试次数

 

在代码中逐个socket设置的方式:

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netinet/tcp.h>
 
 /* Set TCP KeepAlive proporties */
 int keepAlive = 1; // 开启keepalive属性
 int keepIdle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测
 int keepInterval = 5; // 探测时发包的时间间隔为5 秒
 int keepCount = 3; // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.
    
 int rs = socket(AF_INET, SOCK_STREAM, 0);
 setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));
 setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));
 setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));
 setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));

 

如果有码流申请:

tcp 0 0 192.168.1.230:8557 192.168.0.80:49352 ESTABLISHED 
 
 
 

   tcp 0 0 192.168.1.230:8557 192.168.0.155:1953 ESTABLISHED    1080P 
 
 
 

     
 
 
 

   tcp 0 0 192.168.1.230:8556 192.168.0.80:49533 ESTABLISHED    480P 
 
 
 

   tcp 0 0 192.168.1.230:8555 192.168.0.80:49556 ESTABLISHED    1080P JPEG