本文主要介绍CLOSE_WAIT状态解决方案。本文通过一个简单的案例来说明对这项技术的理解和使用。以下是详细内容,有需要的朋友可以参考一下。
之前没怎么关注这个问题,但是最近面试遇到的一个问题。我遇到了两家公司,两家公司其实都遇到了这个问题,所以不得不重视。说到CLOSE_WAIT状态,如果你不知道,我们先来看看TCP的状态转换图。
关闭套接字可分为主动关闭和被动关闭两种情况。前者是指本地主机发起的关机;后者意味着本地主机检测到远程主机发起的关闭,然后做出响应,从而关闭整个连接。挑出封闭部分的状态转移,会得到下图:
发生原因
从图中,我们来分析一下,什么情况下连接处于CLOSE_WAIT状态?
在被动关闭连接的情况下,连接在已经接收到FIN的时刻处于CLOSE_WAIT状态,但是它自己的FIN还没有发送。
一般来说,CLOSE_WAIT状态的持续时间应该很短,就像SYN_RCVD状态一样。但是,在某些特殊情况下,连接会长时间处于CLOSE_WAIT状态。
有很多close_wait现象。主要原因是对方在某些情况下关闭了socket链接,而我们在忙着读或者写,没有关闭连接。代码需要判断套接字。一旦它的读数为0,断开它,read返回负值。检查errno,如果不是,就断开它。
如参考文献4所述,发送SYN-FIN消息来实现CLOSE_WAIT状态连接,没有进行具体的实验。但是,我个人认为协议栈会丢弃这个非法消息。有兴趣的同学可以测试一下,告诉我结果。-)
为了更清楚地解释这个问题,我们编写一个测试程序,并注意到这个测试程序是有缺陷的。
只要我们建构一个对方关闭套接字的情境,我们还是在阅读,或者我们会建构这样一个情境而不直接关闭套接字。
server.c:
#包含stdio.h
#包含字符串. h
#包含netinet/in.h
#定义最高限额80
#定义SERV港8000
int main(void)
{
struct sockaddr_in servaddr,cliaddr
socklen _ t cliaddr _ len
int listenfd,connfd
char buf[MAXLINE];
char str[INET _ ADDRSTRLEN];
int i,n;
listenfd=socket(AF_INET,SOCK_STREAM,0);
int opt=1;
setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,opt,sizeof(opt));
bzero(servaddr,sizeof(serv addr));
servaddr.sin _ family=AF _ INET
serv addr . sin _ addr . s _ addr=htonl(in addr _ ANY);
servaddr . sin _ PORT=htons(SERV _港口);
bind(listenfd,(struct sockaddr *)servaddr,sizeof(servaddr));
听(listenfd,20);
printf(接受连接. n’);
while (1) {
CLI addr _ len=sizeof(CLI addr);
connfd=accept(listenfd,
(struct sockaddr *)cliaddr,CLI addr _ len);
//while (1)
{
n=read(connfd,buf,MAXLINE);
if (n==0) {
printf(另一边已经关闭。 n’);
打破;
}
printf(从端口%d的%s接收n ,
inet_ntop(AF_INET,cliaddr.sin_addr,str,sizeof(str)),
ntohs(cliaddr . sin _ port));
for(I=0;I n;我)
buf[I]=toupper(buf[I]);
write(connfd,buf,n);
}
//这里可以故意不关闭套接字,或者在关闭前加一个睡眠。
//睡眠(5);
//close(conn FD);
}
}
客户端. c:
#包含stdio.h
#包含stdlib.h
#包含字符串. h
#包括unistd.h
#包含sys/socket.h
#包含netinet/in.h
#定义最高限额80
#定义SERV港8000
int main(int argc,char *argv[])
{
struct sockaddr _ in servaddr
char buf[MAXLINE];
int sockfd,n;
char * str
如果(argc!=2) {
fputs(用法:/客户端消息n ,stderr);
出口(1);
}
str=argv[1];
sockfd=socket(AF_INET,SOCK_STREAM,0);
bzero(servaddr,sizeof(serv addr));
servaddr.sin _ family=AF _ INET
inet_pton(AF_INET, 127.0.0.1 ,servaddr。sin _ addr);
servaddr。sin _ PORT=htons(SERV _港口);
connect(sockfd,(struct sockaddr *)servaddr,sizeof(servaddr));
write(sockfd,str,strlen(str));
n=read(sockfd,buf,MAXLINE);
printf("来自服务器的响应: n ");
write(STDOUT_FILENO,buf,n);
write(STDOUT_FILENO, n ,1);
关闭(sockfd);
返回0;
}
结果如下:
黛比安-王耀:~ $ 1000。/客户端a
来自服务器的响应:
A
黛比安-王耀:~ $ 1000。/客户端b
来自服务器的响应:
B
黛比安-王耀:~ $ 1000。/客户端c
来自服务器的响应:
C
黛比安-王耀:~$ netstat -antp | grep CLOSE_WAIT
(并非所有流程都可以识别,非所有流程信息
将不显示,您必须是根用户才能看到这一切。)
TCP 1 0 127。0 .0 .1:8000 127 .0 .0 .1:58309关闭_等待6979/服务器
TCP 1 0 127。0 .0 .1:8000 127 .0 .0 .1:58308关闭_等待6979/服务器
TCP 1 0 127。0 .0 .1:8000 127 .0 .0 .1:58307关闭_等待6979/服务器
解决方法
基本的思想就是要检测出对方已经关闭的插座,然后关闭它。
1.代码需要判断插座,一旦阅读返回0,断开连接,阅读返回负,检查一下错误,如果不是再说一遍,也断开连接。(注:在UNP 7.5节的图7.6中,可以看到使用挑选能够检测出对方发送了鳍,再根据这条规则就可以处理关闭_等待的连接)
2.给每一个(电源)插座设置一个时间戳上次更新,每接收或者是发送成功数据,就用当前时间更新这个时间戳。定期检查所有的时间戳,如果时间戳与当前时间差值超过一定的阈值,就关闭这个插座。
3.使用一个心跳线程,定期向(电源)插座发送指定格式的心跳数据包,如果接收到对方的英特尔的快速储存技术报文,说明对方已经关闭了插座,那么我们也关闭这个插座。
4.设置所以_保持活跃选项,并修改内核参数
前提是启用(电源)插座的保持活跃机制:
//启用(电源)插座连接的保持活跃
int iKeepAlive=1;
setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,(void *)iKeepAlive,sizeof(iKeepAlive));
tcp_keepalive_intvl(整数;默认:75;从Linux 2.4开始)
传输控制协议(Transmission Control Protocol)保持活动探测之间的秒数。
tcp_keepalive_probes(整数;默认:9;从Linux 2.2开始)
如果没有从另一端获得响应,在放弃并终止连接之前要发送的传输控制协议(Transmission Control Protocol)保持活动探测的最大数量。
tcp_keepalive_time(整数;默认:7200;从Linux 2.2开始)
在传输控制协议(Transmission Control Protocol)开始发送保持活动探测之前,连接需要空闲的秒数。仅当启用了所以_保持活跃套接字选项时,才发送保持活力.默认值为7200秒(2小时)。启用保活功能后,空闲连接将在大约11分钟后终止(9次探测,间隔75秒)。
echo 120/proc/sys/net/IP v4/TCP _ keepalive _ time
echo 2/proc/sys/net/IP v4/TCP _ keepalive _ intvl
echo 1/proc/sys/net/IP v4/TCP _ keepalive _ probes
除了修改内核参数外,可以使用塞特索科普特修改(电源)插座参数,参考男人七插座。
int KeepAliveProbes=1;
int KeepAliveIntvl=2;
int KeepAliveTime=120
setsockopt(s,IPPROTO_TCP,TCP_KEEPCNT,(void *)KeepAliveProbes,sizeof(KeepAliveProbes));
setsockopt(s,IPPROTO_TCP,TCP_KEEPIDLE,(void *)KeepAliveTime,sizeof(KeepAliveTime));
setsockopt(s,IPPROTO_TCP,TCP_KEEPINTVL,(void *)KeepAliveIntvl,sizeof(KeepAliveIntvl));
到此这篇关于关闭_等待状态解决方案的文章就介绍到这了,更多相关关闭_等待状态内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!