Linux TCP通信出现CLOSE_WAIT后导致服务端进程挂掉

在前文中讲述了Linux服务端TCP通信出现CLOSE_WAIT状态的原因,这篇文章主要通过一个实例演示它个一个“恶劣”影响:直接使服务端进程Down掉。

CentOS服务端建立监听端口

1 CentOS服务端建立监听端口

如上图所示,在虚拟机CentOS7服务器(192.168.1.178)中打开一个终端界面,建立8000端口的监听服务(PID:13035)。所用代码如下,和上一篇文章中的程序大体一样,只是多了一个SIGPIPE信号处理以及自动回复(Auto response from server.)部分。

 代码如下 复制代码

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <signal.h>

//Whether add a signal handle.
#define SIGNAL_HANDLE 0

void sig_handle( int signal )
{
    printf( "Receive a signal=[%d].\n", signal );
    return;
}

int main( int argc, char **argv )
{
    int server_sockfd;
    int client_sockfd;
    int len;
    int llOpt = 1;
    struct sockaddr_in my_addr;
    struct sockaddr_in remote_addr;
    int sin_size;
    char buf[BUFSIZ];
    memset( &my_addr, 0, sizeof(my_addr) );
    my_addr.sin_family = AF_INET;
    my_addr.sin_addr.s_addr = INADDR_ANY;
    my_addr.sin_port = htons(8000);

    #if SIGNAL_HANDLE
    struct sigaction new_act, old_act;
    new_act.sa_handler = sig_handle;
    new_act.sa_flags = 0;
    sigemptyset( &new_act.sa_mask );
    sigaction( SIGPIPE, &new_act, &old_act );
    sigaction( SIGINT, &new_act, &old_act );   
    #endif

    if( ( server_sockfd = socket( AF_INET, SOCK_STREAM, 0 ) ) < 0 )
    { 
        perror("socket");
        return 1;
    }

    if( setsockopt( server_sockfd, SOL_SOCKET, SO_REUSEADDR, &llOpt, sizeof(llOpt) ) ) {
        close(server_sockfd);
        return errno;
    }

    if( bind( server_sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr) ) < 0 )
    {
        perror( "bind" );
        return 1;
    }

    listen( server_sockfd, 5 );

    sin_size = sizeof( struct sockaddr_in );

    printf( "Server is listening with pid=[%d].\n", getpid() );

    while(1)
    {
        if( ( client_sockfd = accept( server_sockfd, (struct sockaddr *)&remote_addr, &sin_size ) ) < 0 )
        {
            perror( "accept" );
            return 1;
        }
        //Print the ip address and port of client.
        printf( "Accept client[%s:%u].\n", inet_ntoa(remote_addr.sin_addr), ntohs(remote_addr.sin_port) );

        send( client_sockfd, "Auto response from server.", strlen("Auto response from server."), 0 );

        memset( buf, 0x00, BUFSIZ );
        while( ( len = recv( client_sockfd ,buf, BUFSIZ, 0) ) > 0 )
        {
            buf[len]='\0';
            printf( "Message from client=[%s]\n", buf );
        }
        close( client_sockfd );
    }
    close( server_sockfd );
    return 0;
}

2 在Linux中利用telnet命令创建一个客户端

新建一个shell脚本netstat_nap.sh,里面只包含一条有效命令netstat -nap|head -n 2;netstat -nap|grep 8000。

再打开一个Linux终端界面,然后输入命令telnet 192.168.1.177 8000作为客户端建立与服务端的TCP连接。这时执行脚本./netstat_nap.sh可以看到Linux客户端(PID:13045)和服务端(PID:13035)的TCP通信已经变成ESTABLISHED状态,效果如下图所示:

3 在Windows中利用telnet命令创建一个客户端

在Windows主机(192.168.1.110)中打开一个PowerShell终端界面,然后输入命令telnet 192.168.1.177 8000作为客户端建立与Linux服务端的TCP连接。

这时执行脚本./netstat_nap.sh,可以看到Windows客户端(端口:64012)和服务端(PID:13035)的TCP通信已经变成ESTABLISHED状态。同时使用命令lsof -i:8000,可以看到进程打开的文件。

4 直接关闭Windows telnet客户端界面并使用Wireshark抓包

在直接关闭telnet界面后,继续使用netstat_nap.sh脚本和lsof命令发现刚才建立的TCP通信出现了CLOSE_WAIT的状态。

在等待2分钟后,在Windows中使用Wireshark抓包发现由于客户端发送了RST+ACK报文给Linux服务端,所以二者的TCP链路已经被复位了:

在Windows中使用Wireshark抓包

这时在Linux中再次使用netstat_nap.sh脚本和lsof命令发现CLOSE_WAIT的状态已经不存在了。

5 关闭Linux telnet客户端

在Windows关闭telnet客户端界面并发送RST+ACK报文后,关闭小节2中在Linux中打开的telnet客户端。这时Linux服务端进程会执行第90行处的close()函数,也即执行正常四次挥手关闭TCP连接。

接着Linux服务端进程继续从内核中已完成连接队列中取出已完成连接,这样之前小节3中Windows telnet建立的客户端连接被读取。如下图所示,服务端进程打印了第80行出的数据(Accept client[192.168.1.110:64012].),但是服务端进程却挂掉了。

CentOS服务端建立监听端口

这时在Linux中再次使用netstat_nap.sh脚本和lsof命令:

 


6 原因分析

由于Windows客户端的TCP链路在小节4中由于RST的缘故而关闭了,没有读端。那么当Linux服务端执行82行的send()函数时,向之前的socket描述符发送26字节的报文数据时,会收到内核发送过来的SIGPIPE信号,导致服务端进程默认关闭。

因此,如果想捕捉到这个SIGPIPE信号的话,可以将程序17行的SIGNAL_HANDLE宏定义值改成1,那么就会得到如下图所示的情况(进程能正常运行了)。

 


7 问题延伸

如果在第4小节中关闭Windows客户端界面后,又直接如第5小节所示关闭Linux telnet客户端界面,那么又会出现什么情况呢?于是又重新做了一遍测试,流程同上,下面是测试结果以及分析。

先用netstat和lsof命令查看TCP服务状态,发现监听服务正常:

 


然后分别用TCPDUMP和Wireshark抓取TCP通信包,截取如下所示。可以发现在Linux telnet客户端完成四次挥手后,服务端进程继续向之前Windows telnet客户端建立的socket描述符发送26字节的报文数据。

因为Windows客户端此时处于FIN_WAIT2状态(Linux服务端处于CLOSE_WAIT状态),所以服务端能继续发其发送数据(即图中的PUSH+ACK报文),接着Windows客户端回应RST+ACK报文,从而两者的TCP链路复位。

在Linux中使用TCPDUMP抓包

在Windows中使用Wireshark抓包

这样Linux服务端进程还是能够正常执行监听任务:

 


8 其它

网上有人把这种客户端或者服务端异常关闭的连接叫做TCP半关闭(Half-Close),例如网线拔掉、突然断电等,此时对端连接仍认为双方连接处于打开中。

时间: 2024-10-15 04:06:34

Linux TCP通信出现CLOSE_WAIT后导致服务端进程挂掉的相关文章

协议-Android软件使用TCP进行通信,连接不到服务端,在同一子网,代码跟异常日志都有,请大神指教

问题描述 Android软件使用TCP进行通信,连接不到服务端,在同一子网,代码跟异常日志都有,请大神指教 客户端线程:class BB1 extends Thread{ public void run() { try { System.out.println(""hahahha""); Socket client=new Socket(ipadressPORT); System.out.println(client.getPort()); mingling=&qu

udp-UDP跨路由器通信,客户端无法收到服务端发送的UDP包

问题描述 UDP跨路由器通信,客户端无法收到服务端发送的UDP包 本人新手,正做一个UDP跨路由通信项目,遇如下问题:服务端(192.168.0.85)与路由器在同一网段,客户端(192.168.1.103)在该路由器组成的局域网内,现服务端可以接收到客户端发送的UDP包,但客户端无法收到服务端的UDP包.请问客户端发送的UDP包需怎样设置(IP/端口)?(PS:服务端与客服端在同一局域网内是可以UDP通信的) 以下是我的UDP设置客户端--目标IP:192.168.0.85----目标端口10

网络编程-客户端发送消息给服务端后,服务端应答无效

问题描述 客户端发送消息给服务端后,服务端应答无效 连接建立了,语言C++.前端是用cocos2d封装好的. 目前是:客户端向服务端send:服务端能接到:服务端向客户端send:客户端能接到. 但是:客户端向服务端send,服务端接到后立即应答向客户端send,客户端就接不到. 代码如下: 客户端:(在一个按钮中触发) gksock->connect_(5500,"127.0.0.1"); struct gksakura { int a; char b[32]; }; stru

在线等。Socket从客户端连接后,服务端无法返回内容,报错。

问题描述 情况如下:我从客户端连接server端.连接创建成功,然后,客户端发送一个注册码,server端也能够接收到.然后,server端返回一个成功提示,这时候,报错了.At2014/9/3011:23:03,执行命令内容时出现错误,原因来自于:远程主机强迫关闭了一个现有的连接.在System.Net.Sockets.Socket.Send(Byte[]buffer,Int32offset,Int32size,SocketFlagssocketFlags)在DEC.SocketCommon.

scoket tcp ip 报文-Java 的 Socket服务端客户端以tcp/ip协议发送接收报文

问题描述 Java 的 Socket服务端客户端以tcp/ip协议发送接收报文 代码谁有模板,比如给你一个login报文,logout报文!!急急急!!! 解决方案 Android Socket 编程(WIFI 和 ADB) 有了发送数据的功能,再发送文件还需要分包等协议. 解决方案二: 这个不知道是不是你要的答案, 解决方案三: http://blog.csdn.net/u013301192/article/details/46336719 解决方案四: 网页上的1楼回复应该对你有用,祝你好运

如何使用TLS连接+SSL证书加密通信连接Qlnmp的FTP服务端

这里我以http://www.aliyun.com/zixun/aggregation/12592.html">Flashfxp这个FTP客户端软件为例: 首先,打开你的Flashfxp客户端. 然后在菜单栏选择"站点"->"站点管理器",并打开"站点管理器". 在打开的"站点管理器"的左下角点击"新建站点",然后在弹出的窗口中为你新建的站点取一个名字,然后点击"确定&quo

关于C# Socket通信服务端

问题描述 想请教一下:我是用C#Socket异步通信编程.现在想实现这样的功能:在服务端与客户端连接的情况下,客户端断开时,服务端能够在界面上显示提醒(类似:客户端192.168.1.100:6300已断开).不知该如何实现,求教. 解决方案 解决方案二:客户端可以在退出终端程序时向服务器发送一个"关闭"消息,而服务器端也可以每隔30秒向各个客户端发送一个"心跳"消息.解决方案三:引用楼主richardcrick的回复: 想请教一下:我是用C#Socket异步通信编

C#TCP通信时模拟客户端断开后服务端的CPU使用率从45%上升到80%-90%

问题描述 我使用C#的TCPlistener写了一个TCP服务端的程序,用来向连接到服务端的客户端发送数据的,客户端发送不同的请求就发送相应的数据,没有请求数据就什么也不发送:发送的数据是从另一个端口接收到的UDP的报文解析后的数据服务端程序包括接收及解析UDP报文,和TCPlistener监听(有连接请求就建立一个线程为这个连接服务,连接断开就终止线程)现在当服务端程序启动,就开始接收UDP报文并解析,如果有连接请求,就创建线程并为连接服务,当模拟器客户端断开连接,CPU使用率就会迅速增长到8

通讯-C# 客户端和服务端连接时出现服务端运行一段时间后切断连接

问题描述 C# 客户端和服务端连接时出现服务端运行一段时间后切断连接 各位大神,现在遇到一个问题: 我的客户端程序与服务端进行数据通讯,用到的通讯协议为Modbus tcp和FTP, 但现在在运行的过程中出现一个问题,就是我的客户端程序运行一段时间后,服务端直接切断了与我客户端程序的连接(此时我的客户端程序也没有报错,可是我使用了try,catch语句进行报错的),与此同时,我通过输入FTP地址直接登录也不行,提示的错误为与服务器的连接被重置,这个问题是处在我的客户端程序还是服务端?我查了两天的