我们在编写网络通信程序的时候,不论作为服务端还是客户端,常常需要确保实时检测与对方的连接状态,调用select函数监控文件描述符(套接字)的连接状态是最普遍的方法,但是当遇到对方断电或者网络线路突然断开的情况,select函数将接收不到来自对方的断开消息,所以维护着这一连接的文件描述符(套接字)已经没有存在的意义,这个时候往该文件描述符(套接字)内写内容依然不会返回错误,read也会堵塞着并不返回错误。因此也无法断定该套接字是否还处于连接状态,我们可以通过开启TCP的keepAlive选项来让TCP连接自身维护自己的连接状态,详见《Linux下回收异常断开的TCP连接》,但是倘若要及时检测网络连通性,不得不频繁地发心跳包,势必会影响通信带宽,不是很理想。
另外一种办法是在select超时或者指定时间去connect一次对方,因为connect涉及到传输层跟应用层,为了消耗更少的带宽,减少对于应用层的干扰,可以考虑调用Ping命令走ICMP协议封装Linux下网络连通性检测函数。下面是我用C语言封装的一个检测实例。
#include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <string.h> int checkConnect(char *dst, int cnt) { int i = 0; FILE *stream; char recvBuf[16] = {0}; char cmdBuf[256] = {0}; if (NULL == dst || cnt <= 0) return -1; sprintf(cmdBuf, "ping %s -c %d -i 0.2 | grep time= | wc -l", dst, cnt); stream = popen(cmdBuf, "r"); fread(recvBuf, sizeof(char), sizeof(recvBuf)-1, stream); pclose(stream); if (atoi(recvBuf) > 0) return 0; return -1; } int main(int argc, char *argv[]) { if (argc != 3) { printf("Please set the arguments as follow:\n"); printf("%s <destination> <count>\n", argv[0]); return -1; } if (checkConnect(argv[1], atoi(argv[2]))) printf("Network is not up to %s\n", argv[1]); else printf("Network is up to %s\n", argv[1]); return 0; }
上面提到的connect跟Ping封装虽然比keepLive更灵活,但仍然存在不足,对于服务器来说很难实现,因为通常客户端都处于内网,外网的服务器无法主动connect到内网客户端,所以也不够通用,只能在应用层上下功夫了,可以考虑通信双方协商一个专门用于检测连接状态的命令字,其中一方收到来自另一方的网络检测命令字后,回应一个确认命令字告诉对方自己当前在线,如果一方发送检测命令字后得不到回应,则作对方掉线处理,回收套接字并释放相关资源。
除非注明,文章均为CppLive 编程在线原创,转载请注明出处,谢谢。