Linux中tcp服务器检测客户端断开连接的方法

时间:2024-04-07 22:02:41

现在是在一个树莓派上执行编写好的tcp服务器的程序,一旦有客户端连接上来,服务器就会不断的向客户端发送类似于心跳包的数据。现在的需求是,当客户端软件关闭,服务器不需要从新启动服务器上的tcp进程。

 

涉及到的主要是客户端断开连接后,服务器端继续send时,进程会退出,这是会涉及到信号SIGPIPE的知识,以及使用send中的最后参数或者sigaction的方法,避免进程被杀死。

 

根据上面的使用场景,首先想到的思路就是:1.检测客户端断开连接 -> 2.服务端从新监听客户端的连接 -> 3.更新服务端的发送和接收的socket

 

这里比较重要的就是第一步:检测客户端的连接。

比较简单常规的方法就是,当服务端调用send接口后,如果返回小于0,就直接判断客户端断开连接。这里使用window下的<TCP/UDP socket 调试工具>(如下图1)作为数据收发的客户端。

Linux中tcp服务器检测客户端断开连接的方法

图1

 

通过这种方法测试得出:如果客户端是直接点击客户端的断开按钮(如下图2),服务器端就直接退出,并且很奇怪的是从服务端的打印来看,完全不清楚实在哪一个系统调用出的问题。这样的话对于检测断开是个麻烦事。

 

如果是点击右上角的客户端软件关闭按钮(如下图3),服务端就会按照设想的在send的时候返回小于0,并且从新监听。

Linux中tcp服务器检测客户端断开连接的方法

图2

 

Linux中tcp服务器检测客户端断开连接的方法

图3

 

在发送数据的各个代码数据间加上打印信息,发现每次服务端代码的退出的时候的最后打印都是send函数之前的那条打印。也就是如图4的红色方框中的打印。也就是说代码运行到send的时候就退出。

Linux中tcp服务器检测客户端断开连接的方法

图4

 

打印信息如下(图5):

Linux中tcp服务器检测客户端断开连接的方法

图5

参考如下解决:

https://blog.csdn.net/luxgang/article/details/81217289

 

Linux系统中当客户端断开连接后,服务端再次调用send接口时,底层会抛出SIGPIPE的信号,这个信号的缺省操作是进程直接退出。所以需要在代码中忽略这个信号,这里有两种方法:

方法一:

直接在服务端的send接口的参数进行设置:

ret = send(client_socket, buf, 10, MSG_NOSIGNAL);

客户端断开后返回的错误码:Broken pipe

附加描述:

函数原型:ssize_t send(int s, const void *buf, size_t len, ini flags);

flags参数:

         0:此时同write

         MSG_OOB:发送带外数据

         MSG_DONTROUTE:告诉ip协议,目的主机在本地网络,不需要查找路由表

         MSG_DONTWAIT:设置为非阻塞操作

         MSG_NOSIGNAL:表示发送动作不被SIGPIPE信号中断

 

方法二:

#include <sys/signal.h>

...

struct sigaction sa;

sa.sa_handler = SIG_IGN;

sigaction(SIGPIPE, &sa, 0);

 

sigaction的操作是检查或者修改与指定信号相关联的处理动作。

sigaction的用法参考:https://blog.csdn.net/weibo1230123/article/details/81411827