前言
当客户端与服务器之间使用 TCP 协议进行通信时,建立这条连接时需要三次握手,销毁这条连接时需要四次挥手。其中,TCP 协议中有长连接和短连接之分。
长短连接
- TCP 长连接:当客户端与服务器建立连接后,一直处于连接状态,直到最后不再需要服务的时才断开本次连接。在长时间内无数据交互时,客户端可能发生了断电、崩溃、重启、断网等意外情况,但服务器并不知道对端的情况,它会一直维护这个已经断开的“死链”,长时间的积累会造成系统资源的消耗和浪费。
- TCP 短连接:当客户端与服务器建立连接后,通信 1 次 / N 次结束之后就断开本次连接,当下次再次通信时,再次建立 TCP 连接。虽说不会长期占用服务器的内存,但是频繁的建立连接和释放连接会浪费大量的 CPU 和带宽资源,服务器需要处理更多的连接数量。
针对以上的问题,只要客户端定时向服务端发送一个数据包,服务端判断后返回响应数据,这种双向心跳的 PING-PONG 模式可检测连接是否正常,这类数据包我们也称之为”心跳包”(HeartBeat),这种操作我们称之为“心跳检测”。
心跳检测
虽然 TCP/IP 协议已经提供了相关的 keepalive 选项,但是启用这个选项需要为每个连接中的 socket 开启,这不一定是必须的,可能会产生大量无意义的带宽浪费,且 keepalive 选项不能与应用层很好地交互。所以,在对实时性要求比较高的游戏/交互场景中,开发者们通常会在应用层实现的一种机制,模仿 TCP 的 keepalive 机制检测。
实现思路
心跳包其实就是一个预先规定好格式的数据包,客户端定时发送,服务端做检测并响应即可。
心跳间隔过小,心跳包发送频繁会造成电量、流量、网络等资源的浪费;心跳间隔过大会影响系统的稳定性,不能保证通信的实时性。对此,可以自定义一个心跳间隔的区间。
- 服务端当前的 Unix 时间戳:NowTime
- 上一次接收到心跳包的时间戳:LastTime
- 最快的心跳间隔:FastSpan
- 最慢的心跳间隔:SlowSpan
心跳过快
在接收到客户端主动发送的心跳包后,服务端用 NowTime
与 LastTime
做对比,如果时间间隔 Diff
大于设置的差值(自定义),就把 Diff
存进一个错误队列 ErrList
中。
网络通信永远要考虑到最坏的情况,一次心跳过快并不能代表什么,需要容错处理。
当这个错误队列的长度达到设置的值(自定义),截取这个错误队列得到 ErrList2
,对其排序,剔除 N 个最小值后,再求剩余的平均值 AvgDiff
,如果 AvgDiff
小于设置的 FastSpan
,那么错误次数 ErrNum
累加。
当错误次数达到设置的上限(自定义),那么可以判定为这个用户的心跳包不正常,强制下线。
心跳过慢
服务端设置定时器(自定义)作为兜底机制,用 NowTime
与 LastTime
做对比,如果时间间隔 Diff
大于设置的 SlowSpan
,那么可以判定这个用户可能已经断开连接了,强制下线。
当由于过快/过慢原因强制下线时,返回的心跳应答可以携带上特定的操作错误码或者说明文本反馈给用户。
服务端根据实际需要记录心跳包日志,在开发环境为了方便调试都记录,或者写入配置文件,对于特定的服务器 Id 才记录。
2023年3月27日 13:22
表评论2717
2023年3月23日 06:50
rj29bx