免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
123下一页
最近访问板块 发新帖
查看: 26423 | 回复: 27
打印 上一主题 下一主题

[C] linux 多线程 TCP长连接 心跳检测 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-08-27 00:10 |只看该作者 |倒序浏览
TCP服务,长连接。
    首先一个s(服务器)和一个c(客户端)建立TCP连接,S向C发送数据。C有两个线程:一个收数据;一个将数据保存到本地,保存成功以后向S回复一个确认。
    因为是长连接,所以要加一个心跳检测,C每隔100秒向S发一个心跳数据包,S收到包以后会给C一个回复。如果C没有在10秒内收到回复,则认为连接已经断了,则需要重新connect服务器。
基本功能实现了,现在缺少心跳检测功能。心跳怎么实现呢?听起来简单,实现起来发现不容易。
想到一个办法就是用定时器,客户端心跳线程在发心跳的时候启动一个定时器,如果定时器超时则
重新连接服务器。如果接受线程收到服务器的心跳回复数据包,则删除定时器。
按我的想法
心跳线程:
while(1)
{
    sleep(100);//
    if (0 == flag)
    {
        break;
    }
    init_timer(&xxx);
    xxx.function = fun();//func为超时函数,比如超时就置标志位为0: flag = 0;
    add_timer
}
connect();//跳出while重新连接。
接收线程:
for( ; ; )
{
    read(sock, buf[], n);
    switch (buf[0])
      case data: xxx;break;
      case 心跳回复: del_timer(&xxx);break;
}   

各位大牛指点一下啊

论坛徽章:
7
天蝎座
日期:2013-09-28 10:45:42双子座
日期:2013-10-16 16:27:09射手座
日期:2013-10-23 10:21:32处女座
日期:2014-09-17 16:44:332015年亚洲杯之巴林
日期:2015-04-09 17:28:01冥斗士
日期:2015-11-26 16:19:0015-16赛季CBA联赛之山东
日期:2018-03-02 23:59:31
2 [报告]
发表于 2013-08-27 10:12 |只看该作者
心跳的作用就是检测链路是否连接,就这点功能,没其他作用了。

不建议使用定时器(个人理解的定时器,离不开alarm,信号。),信号不容易控制,就算是单线程也容易引起race condition,何况多线程。
可以使用当前时间除以一个间隔值,满足了即可发送心跳报文。
或者keepalive,默认2小时,可以修改内核参数缩小心跳时间。

心跳有益处,也有好处。益处在于能检测到网络问题,害处在于如果出现短暂性的网络波动(短暂性的,网络出现暂时性的断开,若干s后可能又好了),他可能就立马就把一个优秀的链路给断开了。

论坛徽章:
0
3 [报告]
发表于 2013-08-27 11:26 |只看该作者

   说起来容易,无非是发一个心跳包给服务器,然后根据是否收到服务器的回复来判断链路是否可用。
但是实际实现起来不容易,
比如,怎么定义10秒?尤其是怎么保证100秒和10秒同步?
这样:
while(1)
{
   if (flag == 0)
   {
       break; //跳出while,重新连接
   }
    sleep(100 - i * 1);
    time_now = now();
    global = time_now + 20; //将全局变量设的比较大。
    for (i = 0; i < 10; i++)
    {
        sleep(1);
        if (global - time_now < 10)
        {
           flag = 1; //表示收到了回复
           break;
        }
    }
    if (i == 10) flag = 0; //表示没收到回复
}
connect();

接收线程:
for( ; ; )
{
    read(sock, buf[], n);
    switch (buf[0])
      case data: xxx;break;
      case 心跳回复: global = now();break;
}   

你的意思呢?

















cxytz01 发表于 2013-08-27 10:12
心跳的作用就是检测链路是否连接,就这点功能,没其他作用了。

不建议使用定时器(个人理解的定时器,离不 ...

论坛徽章:
7
天蝎座
日期:2013-09-28 10:45:42双子座
日期:2013-10-16 16:27:09射手座
日期:2013-10-23 10:21:32处女座
日期:2014-09-17 16:44:332015年亚洲杯之巴林
日期:2015-04-09 17:28:01冥斗士
日期:2015-11-26 16:19:0015-16赛季CBA联赛之山东
日期:2018-03-02 23:59:31
4 [报告]
发表于 2013-08-27 12:25 |只看该作者
本帖最后由 cxytz01 于 2013-08-27 12:28 编辑

以前写的,心跳发送端。
  1. int keep_alive(int fd) {

  2.     static time_t cur_t, pre_t;
  3.     static int    send_flag;
  4.     static char   s_heart[HEART_LEN], s_send_msg[BUF_LEN];
  5.     static long   n, l_snd_len;
  6.     static fd_set alive_wset;

  7.     FD_ZERO(&alive_wset);
  8.     FD_SET(fd, &alive_wset);

  9.     cur_t = time(NULL);

  10.     if (cur_t % HEARTBEAT_T >= 0 && cur_t % HEARTBEAT_T <= (MAX_HEART - HEARTBEAT_T) && send_flag == 0) {    /* 如果在180s~300s之内>,且没有发送过一次心跳,则发送。 心跳报文的发送不依赖定时器,而是使用时间取模,HEARTBEAT_T 是一个宏:时间间隔*/

  11.         memset(s_heart, 0, HEART_LEN);
  12.         memset(s_send_msg, 0, BUF_LEN);
  13.         l_snd_len = 0L;
  14.         n = 0;

  15.         heart_beat(s_heart);  /* 构建心跳报文 */

  16.         Write_m_h(fd, s_send_msg, l_snd_len, &alive_wset);  /* 发送心跳报文 */

  17.         debug_msg(LOG_INFO, "send_simplex: sended s_heart at [%d]", cur_t);

  18.         send_flag = 1;
  19.         pre_t = cur_t;

  20.     } else if (cur_t - pre_t >= HEARTBEAT_T) send_flag = 0;

  21.     return 0;
  22. }
复制代码

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
5 [报告]
发表于 2013-08-27 12:41 |只看该作者
本帖最后由 yulihua49 于 2013-08-27 13:03 编辑
xdh0817 发表于 2013-08-27 00:10
TCP服务,长连接。
    首先一个s(服务器)和一个c(客户端)建立TCP连接,S向C发送数据。C有两个线程:一个 ...

能不能改变一下思路。不必测试连接。我现在这么干:
连接池,平时连接是断的。使用时连,长时间(300秒)不用,断。
使用中故障,归还,断。取用时重连(自愈测试)。。。。

完全无需健康检查(心跳,算是连接池健康检查的一种)的自愈式连接池,使用效果非常好,极为可靠。
单有一个监控线程每30秒检查一次连接池,发现哪个连接长时间未用就关闭之(如果不关闭就无法保证长时间闲置后,它到底好不好。我也不测它好不好了,关了算了,用时再开)。

你的思路是“保通”,这太难了。
我的思路是“保断”,这个容易。

论坛徽章:
4
水瓶座
日期:2013-09-06 12:27:30摩羯座
日期:2013-09-28 14:07:46处女座
日期:2013-10-24 14:25:01酉鸡
日期:2014-04-07 11:54:15
6 [报告]
发表于 2013-08-27 13:14 |只看该作者
心跳有什么复杂吗, 就是为了更新一下last_active时间戳而已, 定时器去踢掉超时的连接就完事了.

论坛徽章:
0
7 [报告]
发表于 2013-08-27 14:14 |只看该作者

很受启发~
这样用的吧:

客户端心跳线程:
while(1)
{
    sleep(1);
    keep_alive(fd);
}这样就可以固定某个时长发一次心跳了。

怎样接收服务器返回来的心跳回复,你的思路是什么呢?

回复 4# cxytz01


   

论坛徽章:
0
8 [报告]
发表于 2013-08-27 14:16 |只看该作者


现在的设计是建立长连接,连接上就永远不断开,所以要用心跳~
不管连接是否需要使用,只要心跳检测到连接断了,那么就重新连接~



回复 5# yulihua49


   

论坛徽章:
0
9 [报告]
发表于 2013-08-27 14:19 |只看该作者

类似于这样?:
while(1)
{
   if (flag == 0)
   {
       break; //跳出while,重新连接
   }
    sleep(100 - i * 1);
    time_now = now(); //记录时间
    global = time_now + 20; //将全局变量设的比较大。
    for (i = 0; i < 10; i++)
    {
        sleep(1);
        if (global - time_now < 10)
        {
           flag = 1; //表示收到了回复
           break;
        }
    }
    if (i == 10) flag = 0; //表示没收到回复
}
connect();

接收线程:
for( ; ; )
{
    read(sock, buf[], n);
    switch (buf[0])
      case data: xxx;break;
      case 心跳回复: global = now();break;//收到回复了,就更新全局变量为接收到回复的时间
}   













回复 6# linux_c_py_php


   

论坛徽章:
15
射手座
日期:2014-11-29 19:22:4915-16赛季CBA联赛之青岛
日期:2017-11-17 13:20:09黑曼巴
日期:2017-07-13 19:13:4715-16赛季CBA联赛之四川
日期:2017-02-07 21:08:572015年亚冠纪念徽章
日期:2015-11-06 12:31:58每日论坛发贴之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-08-04 06:20:00程序设计版块每日发帖之星
日期:2015-07-12 22:20:002015亚冠之浦和红钻
日期:2015-07-08 10:10:132015亚冠之大阪钢巴
日期:2015-06-29 11:21:122015亚冠之广州恒大
日期:2015-05-22 21:55:412015年亚洲杯之伊朗
日期:2015-04-10 16:28:25
10 [报告]
发表于 2013-08-28 13:53 |只看该作者
本帖最后由 yulihua49 于 2013-08-28 13:56 编辑
xdh0817 发表于 2013-08-27 14:16
现在的设计是建立长连接,连接上就永远不断开,所以要用心跳~
不管连接是否需要使用,只要心跳检测到 ...

我们也是长连接的,这个“长”,是指在一定时间内,密集的操作在一个连接里进行。
如果不操作了,经过一段时间就自动挂断了。
如果你一直在操作,就无需心跳。
如果没事了,就挂断。何须心跳?
心跳系统,很难稳定工作。
不是错报故障(服务器忙被错认为死),就是不报故障(心跳正常,业务不正常);
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP