概述
TCP/IP协议分为4层,分别为应用层、传输层、网络层、数据链路层,TCP协议在TCP/IP协议族的传输层,它提供一种面向连接的、可靠的字节流服务,服务器和客户端交换数据之前必须先建立一个连接,在此基础之上双方进行通信。但实际中会存在各种问题导致掉线(如有的防火墙会关闭长时间没有数据交换的网络、中间路由器断掉等),对于TCP长连接而言,服务器和客户端在很长一段时间内可能不会进行数据交互,但客户端会随时上传数据,因此该连接必须保持。那么如何检测连接的通断情况呢?本文提供两种思路方法,希望还有其他方法的朋友提出来分享交流。
1、 TCP机制本身提供了一种方法——keepalive机制,在网络空闲一长段时间后底层发出报文,若网络通畅,会收到对端回复的报文,则说明网络通畅,则继续等待空闲一长段时间再发报文,若未收到对端报文则说明网络对端掉线,则每隔一小段时间发送报文,若几次都没有收到对端的报文,则说明对端彻底掉线,本地应用层recv()函数会返回0,则将该套接字关闭。该机制在TCP/IP协议的传输层实现,可以通过应用层设置keepalive的相关参数,代码如下。该方法适合并发服务器检测网络通畅性问题。
int 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、应用层发送心跳包:每隔一段时间向对端发送一个较小的数据包,通知对方自己在线,并传输一些可能必要的数据(如告诉服务器该数据包为心跳包),并且定时检测对端返回的数据,若连续几次在规定时间内均未收到回复,则判断对端掉线,可以做下一步处理。该方法适合用于客户端处理,在应用层开一个线程发送心跳包,本例中只有发送数据包,未做接收数据处理。数据报文如下:
typedef char Data;
typedef enum {
HEART = 0, //心跳报文头部
MSG //数据报文头部
}Type;
typedef struct datapack{
Type type; //若为心跳报文,buf和len为0
Data buf[32];
int len;
}DataPack;
处理心跳报文的线程函数如下:
void *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 应用层 心跳包所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复