几乎所有的网游服务端都有心跳包(HeartBeat或Ping)的设计,在最近开发手游服务端时,也用到了心跳包。思考思考,心跳包是必须的吗?为什么需要心跳包?TCP没有提供断线检测的方法吗?TCP提供的KeepAlive机制可以替代HeartBeat吗?
由于连接丢失时,TCP不会立即通知应用程序。比如说,客户端程序断线了,服务端的TCP连接不会检测到断线,而是一直处于连接状态。这就带来了很大的麻烦,明明客户端已经断了,服务端还维护着客户端的连接,照常执行着该玩家的游戏逻辑……
心跳包就是用来及时检测是否断线的一种机制,通过每间隔一定时间发送心跳数据,来检测对方是否连接。是属于应用程序协议的一部分。
问题1: TCP为什么不自己提供断线检测?
首先,断线检测需要轮询发送检测报文,会消耗一定的网络带宽和暂用一定的网络资源。如果把它做成TCP的底层默认功能,那些不需要断线检测的应用程序将会浪费不必要的带宽资源。
另外,TCP不提供连接丢失及时通知的最重要原因与其主要设计目的目标之一有关:出现网络故障时维护通信的能力。TCP是美国国防部赞助研究的,一种即使发生战争或自然灾害这种严重网络损坏情况下,也能维护可靠网络通信的网络协议。通常,网络故障只是暂时的,有时路由器会在TCP临时连接丢失后默默的重新连上。所以,TCP本身并不提供那么及时的断线检测。
问题2: TCP的KeepAlive机制可以用来及时检测连接状态吗?
TCP有个KeepAlive开关,打开后可以用来检测死连接。通常默认是2小时,可以自己设置。但是注意,这是TCP的全局设置。假如为了能更及时的检测出断开的连接,把tcp_keepalive_time
和tcp_keepalive_intvl
的时间改小(参考:Link),该机器上所有应用程序的KeepAlive检测间隔都会变小,显然是不能接受的。因为不同应用程序的需求是不一样的。
(在某些平台的Socket实现已经支持为每条连接单独设置KeepAlive参数)
KeepAlive本质上来说,是用来检测长时间不活跃的连接的。所以,不适合用来及时检测连接的状态。
问题3:心跳包(HeartBeat)为什么是好的方式及时检测连接状态?
- 具有更大的灵活性,可以自己控制检测的间隔,检测的方式等等。
- 心跳包同时适用于TCP和UDP,在切换TCP和UDP时,上层的心跳包功能都适用。(其实这种情况很少遇到)
- 有些情况下,心跳包可以附带一些其他信息,定时在服务端和客户端之间同步。(比如帧数同步)
结论
需要及时检测TCP连接状态,心跳包(HeartBeat)还是必须的。