您的位置:首页 > 大数据 > 人工智能

CLOSE_WAIT状态分析

2010-03-08 14:38 225 查看
先给出如下C程序.在此代码中,我们故意屏蔽了发送端的close socket函数.运行后我们会发现发送端有大量的CLOSE_WAIT状态的连接,此时接收端是大量的FIN_WAIT_2状态的连接.

main.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <netinet/tcp.h>
#include <netinet/in.h>

int socket_connect(const char *remoteip, int remoteport){
int socketfd = -1;
unsigned long uladdr = 0;
struct sockaddr_in sa = {0};

int bReuseaddr = 1;
socklen_t optlen = sizeof(bReuseaddr);
/*
int keepAlive = 1;
int keepIdle = 1;
int keepInterval = 1;
int keepCount = 2;
*/
socketfd = socket(AF_INET, SOCK_STREAM, 0);
if(socketfd < 0){
printf("Error: create socket error/n");
return -1;
}
/*
if(setsockopt(socketfd, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(keepAlive)) < 0) {
printf("Error: set socket [%d] opt keep alive error/n", socketfd);
return -1;
}
if(setsockopt(socketfd, SOL_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(keepIdle)) < 0) {
printf("Error: set socket [%d] opt keep idle error/n", socketfd);
return -1;
}
if(setsockopt(socketfd, SOL_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(keepInterval)) < 0) {
printf("Error: set socket [%d] opt keep alive error/n", socketfd);
return -1;
}
if(setsockopt(socketfd, SOL_TCP, TCP_KEEPCNT, &keepCount, sizeof(keepCount)) < 0) {
printf("Error: set socket [%d] opt keep alive error/n", socketfd);
return -1;
}
*/

uladdr = inet_addr(remoteip);
memset(&sa,0,sizeof(sa));
sa.sin_family=AF_INET;
sa.sin_port=htons(remoteport);
sa.sin_addr.s_addr=uladdr;

if( connect(socketfd, (struct sockaddr *)&sa, sizeof(struct sockaddr)) < 0) {
printf("Error: connect exception/n");
return -1;
}
return socketfd;
}

int socket_listen(int port, int listen_queue_size){
int ret = 0;
int s = 0;
struct sockaddr_in addr;

int bReuseaddr = 1;
socklen_t optlen = sizeof(bReuseaddr);

s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if(s < 0) {
return -1;
}
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);

ret = bind(s,(struct sockaddr *)&addr, sizeof(struct sockaddr_in));
if(ret < 0) {
printf("Info: bind port %d failed/n",port);
return -1;
}
ret = listen(s, listen_queue_size);
if(ret < 0){
return -1;
}
return s;
}

int socket_accept(int listen_socket,char* remoteip){
int len = 0;
struct sockaddr_in remote_addr;
int s = -1;
len = sizeof(struct sockaddr_in);
memset((char *)&remote_addr, 0, len);
s = accept(listen_socket, (struct sockaddr *)&remote_addr, (socklen_t *)&len);
if(s <= 0){
printf("accept ret = %d/n",errno);
return -1;
}
return s;
}

int main(int argc, char* argv[]){
int sender = 0;
int ret = 0;
int listen_sock = 0;
if(argc !=2){
printf("usage: ./test 1/n");
return -1;
}
sender = atoi(argv[1]);
if(sender){
while(1){
printf("new round connect/n");
sleep(4);
ret = socket_connect("10.224.55.145",8765);
if(ret < 0){
printf("socket connect failed/n");
return -1;
}else{
printf("socket connect success, sock = %d/n",ret);
// close(ret);
}
}
}else{
listen_sock = socket_listen(8765,100);
if(listen_sock < 0){
printf("socket listen failed/n");
return -1;
}else{
while(1){
printf("before accept socket/n");
ret = socket_accept(listen_sock,NULL);
if(ret < 0){
printf("socket accept failed/n");
return -1;
}else{
printf("socket accept success, sock = %d/n",ret);
close(ret);
}
}
}
}
return 0;
}

上面程序相当于接收端主动断掉当前连接,那么双方关闭此TCP连接需要的四次挥手如下:

SERVER --- FIN --- CLINET

CLINET --- ACK --- SERVER

CLINET --- FIN --- SERVER

SERVER --- ACK --- CLINET

接受端关闭连接,步骤I,II没问题.由于发送端没有关闭连接,所以步骤III不会继续执行.此时,发送端等待自己关闭连接,而接收端等待发送断发送FIN报文.所以,此时,发送端处于CLOSE_WAIT状态,接收端处于FIN_WAIT_2状态.

同理,发送端主动关闭连接,而屏蔽接收端的close socket函数.则发送端会有大量FIN_WAIT_2状态的连接,而接收端会有大量的CLOSE_WAIT状态的连接.当然,造成这种问题也可能是在关闭连接前要处理大量事情.

如何解决此问题?通过设置SO_KEEPALIVE选项能保证此连接会在2个小时后被检测到有问题,此选项一般用于对端以一种非优雅的方式断开的情况.如果我们不能接受如此之长的等待时间,可以通过设置TCP_KEEPIDLE,TCP_KEEPINTVL,TCP_KEEPCNT三个选项来缩短这个时间,从而使CLOSE_WAIT状态的连接尽快释放,而不会越来越多. 如程序socket_connect函数中屏蔽代码实现的.

TCP_KEEPIDL:开始首次KeepAlive探测前的TCP空闭时间.

TCP_KEEPINTVL:两次KeepAlive探测间的时间间隔.

TCP_KEEPCNT:判定断开前的KeepAlive探测次数.

注意:这些属性都必须在客户端(发送端)设置.如果在服务器端(接收端)accepte后设置,只能释放接收端未能释放的连接.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: