现在是在一个树莓派上执行编写好的tcp服务器的程序,一旦有客户端连接上来,服务器就会不断的向客户端发送类似于心跳包的数据。现在的需求是,当客户端软件关闭,服务器不需要从新启动服务器上的tcp进程。
涉及到的主要是客户端断开连接后,服务器端继续send时,进程会退出,这是会涉及到信号SIGPIPE的知识,以及使用send中的最后参数或者sigaction的方法,避免进程被杀死。
根据上面的使用场景,首先想到的思路就是:1.检测客户端断开连接 -> 2.服务端从新监听客户端的连接 -> 3.更新服务端的发送和接收的socket
这里比较重要的就是第一步:检测客户端的连接。
比较简单常规的方法就是,当服务端调用send接口后,如果返回小于0,就直接判断客户端断开连接。这里使用window下的<TCP/UDP socket 调试工具>(如下图1)作为数据收发的客户端。
图1
通过这种方法测试得出:如果客户端是直接点击客户端的断开按钮(如下图2),服务器端就直接退出,并且很奇怪的是从服务端的打印来看,完全不清楚实在哪一个系统调用出的问题。这样的话对于检测断开是个麻烦事。
如果是点击右上角的客户端软件关闭按钮(如下图3),服务端就会按照设想的在send的时候返回小于0,并且从新监听。
图2
图3
在发送数据的各个代码数据间加上打印信息,发现每次服务端代码的退出的时候的最后打印都是send函数之前的那条打印。也就是如图4的红色方框中的打印。也就是说代码运行到send的时候就退出。
图4
打印信息如下(图5):
图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