长连接的中间设备(NAT 网关、防火墙)对空闲连接有超时回收:一条 5 分钟没有任何字节往来的连接,NAT 表项过期后再也收不到数据,而两端都以为连接还在——这就是"人在线但消息发不出去"的半开连接。心跳机制用周期性小包喂住中间设备:客户端每 30 秒发一个心跳包,服务端记录每个连接的末次活跃时间,周期扫描里超过容忍线未活跃的连接判定离线并清理会话。容忍分级是关键:手机网络容忍 60 秒、宽带容忍 90 秒,一刀切会把弱网玩家误杀。
心跳登记与分级扫描:lastSeen 表加周期审计。示例代码如下:
local sessions = {}
local function heartbeat(actorName, netType)
sessions[actorName] = { seen = os.time(), net = netType or "wifi" }
end
local TOLERANCE = { wifi = 90, cellular = 60 }
local function sweepDead()
local now = os.time()
local dead = {}
for name, s in pairs(sessions) do
local limit = TOLERANCE[s.net] or 90
if now - s.seen > limit then
dead[#dead + 1] = name
end
end
for _, name in ipairs(dead) do
sessions[name] = nil
onSessionClosed(name)
end
return #dead
end
local function onSessionClosed(name)
sendmsg(nil, 1, name .. " 连接超时离线,会话已清理。")
end
扫描挂全局定时器示例代码如下:
setontimerex(41, 15000)
local function ontimerex41()
sweepDead()
end
1500 条连接的分级扫描(每 15 秒一次):遍历加比对单次约 2 毫秒,摊薄到每秒 0.13 毫秒。收益实测:未用心跳前,NAT 回收造成的"僵尸会话"占会话表的 18%(1500 条里 270 条是死连接),攻城指挥的喊话有八分之一发给了已消失的会话;心跳加分级清理上线后僵尸占比降到 1% 以内,指挥消息的到达率从 82% 升到 99%。心跳包自身带宽:30 秒一个 8 字节小包,单连接日均流量约 23KB,可忽略。
三个不适用场景:一是短连接业务(请求响应即断开)没有空闲期,心跳是纯开销;二是服务端已依赖引擎自身的连接回调(断线事件可靠触发)时,应用层心跳是重复建设,先确认引擎回调的可靠性再决定;三是容忍线设得比 NAT 超时还长(比如 10 分钟),心跳喂不住中间设备,等于没做,容忍线必须短于链路上最短的超时配置。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
一、一行代码拆解:local function roll() 里写 local cfg = {500, 200, 50} —…
一、隐蔽陷阱:国库金币累加到 2^53(约 9007199254740992)之后再加 1,数值纹丝不动——双精度浮点在该区…
一、线上事故:仓库存取逻辑散在 6 个脚本,各自写各自的 getsysvar 键名,某次改名漏改 2 处,300 件裁决之杖…
一、抛坑提问:500 件战备装备一次下发必卡,按每页 20 件切片,边界怎么算才不出空页和重页?起点 (page-1) 20…
一、抛坑提问:校验失败在工具函数里 error,日志却指向工具函数那一行,排查总要翻两层。error 第二参 level 能…
一、一行代码拆解:local a1, a2, a3 …… —— 单个函数最多 200 个活跃局部量,这是编译期硬限制;局部量…