Workerman 5.2.3 自定义协议断连处理核心是提前预防假死、精准识别失联、重连策略匹配网络现实;需在decode首行无条件更新lastMessageTime,区分lastActiveTime与lastPingTime,服务端主动probe,IoT客户端重连须按网络状态退避。

Workerman 5.2.3 自定义协议中处理断连,关键不在“发现断连”,而在于**提前预防假死、精准识别失联、并让重连策略与网络现实匹配**。纯TCP下没有WebSocket的自动心跳机制,所有逻辑必须由你主动植入协议层和业务层。
必须在 input/decode 中埋入心跳时间戳更新
很多开发者只在 onMessage 里更新 lastMessageTime,但自定义协议中,onMessage 的 $data 已是 decode 后的结果——如果客户端发的是原始 ping 字节(比如 \x01\x00\x00\x00),而你的 decode 报错或跳过,$data 根本不会进 onMessage,时间戳就永远不更新。
正确做法是在协议类的 decode 方法第一行无条件刷新时间戳:
- 无论 $buffer 是否能成功 json_decode 或 unpack,先执行
$connection->lastMessageTime = time() - 若需区分心跳与业务包,建议用固定前缀(如 \x01 表示心跳、\x02 表示业务),在 decode 中按前缀分支处理,但时间戳更新不能跳过
- 匿名连接(未 bindUid)也必须初始化该字段,否则定时器遍历时会触发 Notice
服务端超时清理必须独立于 onMessage 活跃度
不能仅靠 onMessage 时间判断是否断连。客户端可能发完一条业务消息后长期静默(如传感器上报后休眠),此时 lastMessageTime 是旧的,但连接仍有效。
应建立两个时间维度:
- lastActiveTime:仅在收到非心跳包时更新(即真正业务数据)
- lastPingTime:每次 decode 到心跳包时更新
- 服务端定时器(如每 30 秒)检查:若
time() - $connection->lastPingTime > 90,则 close 连接;若仅lastActiveTime超时,可保留连接但标记为“空闲”
客户端重连必须适配模组真实生命周期
自定义协议通常用于 IoT 设备(4G/NB-IoT 模组),其网络附着耗时远超普通浏览器:
- 首次断开:立即重试(new TCP socket)
- 连续失败 2 次:退避至 4–6 秒,加 ±1 秒随机抖动
- 连续失败 5 次:暂停重连,轮询 AT 命令状态(如
+CGATT?、+CREG?),确认网络注册成功后再建连 - 禁止固定 5 秒重连——多数 4G 模组固件无法承受高频建连,易触发模块锁死或 SIM 卡被运营商限频
协议层要支持双向探测,不只是单向心跳
只靠客户端发 ping、服务端回 pong 不够。当服务端怀疑连接假死(如多次 ping 无响应),应主动发 probe 包(如 \x03\x00),并等待客户端 pong 回复。
- probe 包需带递增序列号,避免被中间设备缓存或乱序
- 客户端收到 probe 后必须立即回 \x04 + 相同序列号,且不走业务 decode 流程(防止阻塞)
- 服务端对同一连接连续 2 次 probe 无响应,直接 close 并记录日志(含连接 IP、首次建立时间、最后 ping 时间)

















