TcpSock的心跳问题

来源:互联网 发布:苹果软件不可用 编辑:程序博客网 时间:2024/04/29 05:34

  • 前言
  • 原理
  • 解决办法
    • tcp socket的心跳属性设置
    • 操作系统间的差异
        • macos的特殊情形
  • 总结

前言

tcp socket建立连接之后,突然拔掉网线,或者把路由器断电。tcp socket并不会立即报错断开。

原理

tcp其实有这个属性,但是默认没有打开。
本文只是介绍如何解决问题;至于问题背后的原理啥的,我就不说了。

解决办法

1,自己定义心跳消息。
这个还好,自己控制。但是不建议自己实现。
不知道为什么,现在越来越懒了。已经存在的轮子就不要再重复制造了,再说了,自己实现的未必有别人的好,面临的问题一大把。
2,使用tcp socket的属性。

tcp socket的心跳属性设置

相关的参数:
1,是否打开心跳属性
2,当没有数据交换时,等待多久才发送心跳
3,心跳的发送周期。两次心跳的时间间隔
4,心跳的发送次数。发送几次之后,socket就会产生断开连接的信号。

操作系统间的差异

虽然说都是tcp socket,但是不同操作系统之间的还真不一样。
android跟linux是一样的。
macos不行,貌似必须要用管理员权限。

#ifdef Q_OS_WIN#include "WinSock2.h"#include <winsock2.h>#include <ws2tcpip.h>#include "mstcpip.h"#endif#ifdef Q_OS_ANDROID#include <sys/socket.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include "netinet/tcp.h"#endifQTcpSocket *socket ;//你的socket指针int fd = socket->socketDescriptor();#ifdef Q_OS_WIN    int keepAlive = 1;   // 开启keepalive属性. 缺省值: 0(关闭)//    int keepIdle = 60;   // 如果在60秒内没有任何数据交互,则进行探测. 缺省值:7200(s)//    int keepInterval = 5;   // 探测时发探测包的时间间隔为5秒. 缺省值:75(s)//    int keepCount = 2;   // 探测重试的次数. 全部超时则认定连接失效..缺省值:9(次)    int nRet = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, (char*)(&keepAlive), sizeof(keepAlive));    if(nRet == SOCKET_ERROR)    {        qDebug()<<"robin:setSocketopt failed";    }    struct tcp_keepalive   alive;    alive.keepalivetime = 5000;                // 开始首次KeepAlive探测前的TCP空闭时间    alive.keepaliveinterval  = 1000;                // 两次KeepAlive探测间的时间间隔    alive.onoff = TRUE;    DWORD dwBytesRet=0;    if (WSAIoctl(fd, SIO_KEEPALIVE_VALS, &alive, sizeof(alive),            NULL, 0, &dwBytesRet, NULL, NULL) == SOCKET_ERROR)    {        qDebug()<<"robin:WSAIoctl failed";        return ;    }#elif defined Q_OS_ANDROID    int keepAlive = 1;   // 开启keepalive属性. 缺省值: 0(关闭)    int keepIdle = 5;   // 如果在60秒内没有任何数据交互,则进行探测. 缺省值:7200(s)    int keepInterval = 1;   // 探测时发探测包的时间间隔为5秒. 缺省值:75(s)    int keepCount = 5;   // 探测重试的次数. 全部超时则认定连接失效..缺省值:9(次)    setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));//确定打开tcp的这个属性    setsockopt(s, IPPROTO_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));    setsockopt(s, IPPROTO_TCP, TCP_KEEPINTVL, (void*)&keepInterval, sizeof(keepInterval));//每次发送间隔时间    setsockopt(s, IPPROTO_TCP, TCP_KEEPCNT, (void*)&keepCount, sizeof(keepCount));//探测重试的次数#elif defined Q_OS_MACOS//mac还没有找到合适的办法,//主要设置的有三个值//    int keepIdle = 5;   // 如果在60秒内没有任何数据交互,则进行探测. 缺省值:7200(s)//    int keepInterval = 1;   // 探测时发探测包的时间间隔为5秒. 缺省值:75(s)//    int keepCount = 5;   // 探测重试的次数. 全部超时则认定连接失效..缺省值:9(次)    qDebug()<<"robin:waring, macos not set tcpsocket heartbeta";#else    qDebug()<<"robin:waring,not set tcpsocket heartbeta";#endif

macos的特殊情形

用控制台查看当前tcpsocket属性

#sysctl -A | grep net.inet.tcp.*keep

输出

robindeMBP:~ robin$ sysctl -A | grep net.inet.tcp.*keepnet.inet.tcp.keepidle: 7200000net.inet.tcp.keepintvl: 75000net.inet.tcp.keepinit: 75000net.inet.tcp.always_keepalive: 0net.inet.tcp.keepcnt: 8

用命令更改

sudo sysctl -w net.inet.tcp.keepidle=20000sudo sysctl -w net.inet.tcp.keepintvl=20000sudo sysctl -w net.inet.tcp.keepinit=20000sudo sysctl -w net.inet.tcp.always_keepalive=1

如果用代码,会发现有的值设置不了。

#ifdef Q_OS_MACOS#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include "netinet/tcp.h"#endif    setsockopt(s,SOL_SOCKET,SO_KEEPALIVE,(void*)&keepAlive, sizeof(keepAlive));//确定打开tcp的这个属性 //   setsockopt(s, IPPROTO_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));    setsockopt(s, IPPROTO_TCP, TCP_KEEPINTVL, (void*)&keepInterval, sizeof(keepInterval));//每次发送间隔时间     setsockopt(s, IPPROTO_TCP, TCP_KEEPCNT, (void*)&keepCount, sizeof(keepCount));//探测重试的次数

也就是说,心跳初始等待的时间没有找到对应的宏,二者值默认是7200s。如果不设置这个值的话,基本没什么用了。

总结

windows、linux和android都找到了对应的实现方式。
macos没有找到,放弃了。

原创粉丝点击