上个月中下旬有一个同事突然从公司离职,而他负责的部分是整个项目里的网络编程模块;这也是我们整个项目里最难,BUG最多的模块。目前这个模块涉及难点主要有以下问题:
- 由于程序不严谨导致偶现的异常崩溃,进而导致白屏、卡死等现象
- 在网络通讯过程中,掉线频率非常高
- 程序结构臃肿,无框架思想
由于公司嵌入式软件方面缺人,而我又是做过嵌入式Linux相关的,于是,这个项目就只能让我来接手了,但由于项目十分紧急,开始我是没有什么把握的,直到后来静下心来调试,慢慢就掌握了整个设备与云端的业务通讯流程。针对与云端联调的问题,最首要的是解决连接的稳定性部分,也就是"在网络通讯过程中,掉线频率非常高"这一项,这样才能确保与云端的同事能够将业务流程顺利进行下去。
针对这个问题,我找了很久,也尝试对程序的逻辑、框架进行优化,但始终定位不到问题点。最后只能使出最常用的招,直接到程序里去打LOG Debug,最终发现在网络发送数据的时候出现了"Broken pipe"这个字段;后来经过复现,发现只要是断线,则百分百出现该字段。因此,我断定这个问题就是"Broken pipe"引起的。
1、在什么场景下会产生SIGPIPE信号?
如果一个socket在接收到了RST packet之后,程序仍然向这个socket写入数据,那么就会产生SIGPIPE信号。
这种现象是很常见的,譬如说,当client连接到server之后,这时候server准备向 client 发送多条消息,但在发送消息之前,client进程意外崩溃了,那么接下来server在发送多条消息的过程中,就会出现SIGPIPE信号。
对一个已经收到FIN包的socket调用read方法, 如果接收缓冲已空, 则返回0, 这就是常说的表示连接关闭. 但第一次对其调用write方法时, 如果发送缓冲没问题, 会返回正确写入(发送). 但发送的报文会导致对端发送RST报文, 因为对端的socket已经调用了close, 完全关闭, 既不发送, 也不接收数据. 所以, 第二次调用write方法(假设在收到RST之后), 会生成SIGPIPE信号, 导致进程退出。
参考1:https://blog.csdn.net/u014752451/article/details/103354574
参考2:https://blog.csdn.net/u013246898/article/details/52934628
参考3:https://blog.csdn.net/u010821666/article/details/81841755
2、产生SIGPIPE问题的解决方案
在程序的最开始加入以下代码:
代码语言:javascript复制sigset_t set;
sigemptyset(&set);
sigaddset(&set, SIGPIPE);
sigprocmask(SIG_BLOCK, &set, NULL);
这样就可以避免Program received signal SIGPIPE, Broken pipe。