TCP/IP协议分为4层,分别为应用层、传输层、网络层、数据链路层,TCP协议在TCP/IP协议族的传输层,它提供一种面向连接的、可靠的字节流服务,服务器和客户端交换数据之前必须先建立一个连接,在此基础之上双方进行通信。但实际中会存在各种问题导致掉线(如有的防火墙会关闭长时间没有数据交换的网络、中间路由器断掉等),对于TCP长连接而言,服务器和客户端在很长一段时间内可能不会进行数据交互,但客户端会随时上传数据,因此该连接必须保持。那么如何检测连接的通断情况呢?本文提供两种思路方法,希望还有其他方法的朋友提出来分享交流。
1、 TCP机制本身提供了一种方法——keepalive机制,在网络空闲一长段时间后底层发出报文,若网络通畅,会收到对端回复的报文,则说明网络通畅,则继续等待空闲一长段时间再发报文,若未收到对端报文则说明网络对端掉线,则每隔一小段时间发送报文,若几次都没有收到对端的报文,则说明对端彻底掉线,本地应用层recv()函数会返回0,则将该套接字关闭。该机制在TCP/IP协议的传输层实现,可以通过应用层设置keepalive的相关参数,代码如下。该方法适合并发服务器检测网络通畅性问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34int SetTcpKeepalive(int fd, unsigned int start, unsigned int interval, unsigned int count) { int keepAlive = 1; if (fd < 0 || start < 0 || interval < 0 || count < 0) return -1; //启用心跳机制,如果您想关闭,将keepAlive置零即可 if(setsockopt(fd,SOL_SOCKET,SO_KEEPALIVE,(void*)&keepAlive,sizeof(keepAlive)) == -1) { perror("setsockopt"); return -1; } //启用心跳机制开始到首次心跳侦测包发送之间的空闲时间 if(setsockopt(fd,SOL_TCP,TCP_KEEPIDLE,(void *)&start,sizeof(start)) == -1) { perror("setsockopt"); return -1; } //两次心跳侦测包之间的间隔时间 if(setsockopt(fd,SOL_TCP,TCP_KEEPINTVL,(void *)&interval,sizeof(interval)) == -1) { perror("setsockopt"); return -1; } //探测次数,即将几次探测失败判定为TCP断开 if(setsockopt(fd,SOL_TCP,TCP_KEEPCNT,(void *)&count,sizeof(count)) == -1) { perror("setsockopt"); return -1; } return 0; }
2、应用层发送心跳包:每隔一段时间向对端发送一个较小的数据包,通知对方自己在线,并传输一些可能必要的数据(如告诉服务器该数据包为心跳包),并且定时检测对端返回的数据,若连续几次在规定时间内均未收到回复,则判断对端掉线,可以做下一步处理。该方法适合用于客户端处理,在应用层开一个线程发送心跳包,本例中只有发送数据包,未做接收数据处理。数据报文如下:
1
2
3
4
5
6
7
8
9
10
11
12typedef char Data; typedef enum { HEART = 0, //心跳报文头部 MSG //数据报文头部 }Type; typedef struct datapack{ Type type; //若为心跳报文,buf和len为0 Data buf[32]; int len; }DataPack;
处理心跳报文的线程函数如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33void *HeartBeat(void *argc) { int ret = 0; int cunt = 0; int i = 0; DataPack heart_data; bzero(&heart_data, sizeof(heart_data)); heart_data.type = HEART; heart_data.len = 0; printf("start send heartbeat data n"); cunt = 0; while(1){ ret = send(cli_fd, &heart_data, sizeof(heart_data), 0); if(ret < 0){ cunt ++; if(errno == EPIPE){ //服务器关闭,发送数据导致管道破裂 //处理流程 } if(cunt > 5){ printf("server is offline n"); printf("the client is going down n"); exit(0); } close(cli_fd); } delay(3000); } }
在服务器崩溃以后客户端还调用send()函数会导致管道破裂,该信号的默认处理方式为结束进程, 为了避免进程结束可以调用signal函数忽略掉该信号,如下:
signal(SIGPIPE, SIG_IGN);
本文中若有不足的地方请各位看官提出。
最后
以上就是灵巧洋葱最近收集整理的关于TCP 应用层 心跳包的全部内容,更多相关TCP内容请搜索靠谱客的其他文章。
发表评论 取消回复