服务器向设备发送心跳检测,好像未关闭通道

追梦随想

问题描述

目前在做设备物联网,分成了客户端client_gateway和设备端device_gateway,因为设备电池比较小,硬件大佬要做低功耗,就说服老板取消了定时发送心跳,搞的现在设备在线状态都不准确了。

今天想起可以反向发送心跳检测,就试了下,确实发出去了,但是也出现了新问题:
设备不再发送心跳,为了省电(我也不知道究竟能省多少电...)
客户端(小程序)模式不变定时发送心跳给服务器
device_gateway增加了服务端发送心跳

        // 服务端定时向客户端(设备)发送的数据(在设备不发心跳的情况下)
        if (empty($GatewayDevicePingCloseLimit)) {
            $gateway->pingData = 'ping';
            $gateway->pingInterval = 20;
        }

经过测试设备可以收到ping,但是当我把设备断电后等了好久,似乎并没有执行关闭通道操作

    /**
     * 当断开连接时触发
     * @param int $connect_id 连接id
     */
    public static function onClose($connect_id)
    {
        $target = 'client';

        //设备下线
        if (isset($_SESSION['device_info'])) {
            $target = 'device';

            //出现 not_log 时不进行下线处理,一般用于重复连接时关闭旧连接的情况
            if (!isset($_SESSION['not_log'])) {
                $device_info = $_SESSION['device_info'];
                //生成设备下线日志,同时改变设备状态
                self::$service->create_device_log($device_info['device_id'], 4, '设备下线了');

                //发送给客户端(用户)
                self::send_client_message_by_user_id($device_info['user_id'], 'device_online_state', 0, $device_info);
            } else {
                unset($_SESSION['not_log']);
            }

            unset($_SESSION['device_info']);
        }
        //这里只是打印消息日志
        self::print_client_message($connect_id, 'onClose', $target, 'down');
    }

加个not_log标识是为了解决踢掉旧链接时把状态改为离线的bug
求大佬看看哪里的问题

375 1 1
1个回答

latin

TCP机制就是这样,断电这种无法及时检测到,要等数据包不断重传超时后才能发现连接断开,这个时间很久。
修改linux内核,开启keepalive,修改keepalive时间和间隔好像可以缓解这种情况

  • 追梦随想 2023-12-11

    可是官方文档是这么说的由于心跳是周期性检测,实际执行onClose的时间一般会大于pingInterval*pingNotResponseLimit=55,误差在pingInterval内。 https://www.workerman.net/doc/gateway-worker/heartbeat.html

  • latin 2023-12-11

    官方说的没问题啊,官方说的这个前提是客户端有发心跳或响应心跳,并且服务端设置了pingNotResponseLimit。你的客户端不发心跳,也不响应心跳,也没办法设置pingNotResponseLimit,根本不符合这块文档的条件

  • 追梦随想 2023-12-11

    那不是没法解决了,哎,愁死了,感谢大佬耐心解答

  • rbb 2023-12-11

    这个断电问题,你看下能不能在服务端加个定时器,隔一段时间扫描一下在线设备。

  • 追梦随想 2023-12-12

    定时全部扫描一遍?还是每个连接单独一个定时器,这样确实能解决,我也想过,就是怕影响稳定性

  • rbb 2023-12-12

    就写在一个进程就好了。

  • 追梦随想 2023-12-12

    那应该如何判断设备是否在线呢?没有执行关闭 connect_id 就一直存在,没办法判断啊,难道判断最后一次发送数据的时间么

  • rbb 2023-12-12

    在登录的时候还是要入一下库,例如redis集合吧。然后扫描在线列表(扫到心跳T掉客户端的时候)求差集,就是你想要得异常下线的吧?

  • rbb 2023-12-12

    或者在客户端ping的时候,用session记录ping的时间,然后下次扫的时候,用和服务器心跳一样的算法判断他是否掉线。

  • 追梦随想 2023-12-12

    设备取消心跳了,只能记录最后一次上报数据的时间,然后定时做超时对比了,只是时间太短还不如用心跳,时间太长状态也不准确,硬件大佬真是坑爹啊

  • rbb 2023-12-12

    极端情况只能这样了,百分之98的场景能通就行呀

  • q736979353 2024-03-18

    最后怎么解决的呀

  • JackDx 2024-03-18

    定时器上报吧 未上报的就异常

🔝