ESFramework 开发手册(07) -- 掉线与心跳机制

       虽然我们前面已经介绍完了ESFramework开发所需掌握的各种基础设施,但是还不够。想要更好地利用ESFramework这一利器,有些背景知识是我们必须要理解的。就像本文介绍的心跳机制,在严峻的Internet条件下,是通信系统中不可或缺的机制之一。

       在Internet上采用TCP进行通信的系统,都会遇到一个令人头疼的问题,就是“掉线”。而“TCP掉线”这个问题远比我们通常所能想象的要复杂的多 —— 网络拓扑纷繁复杂、而从始节点A到终节点B之间可能要经过N多的交换机、路由器、防火墙等等硬件设备,每个硬件设备的相关设定也不统一,再加上网络中可能出现的拥塞、延迟等,使得我们在编程时,处理掉线也非常棘手。

一. 从程序的角度看待TCP掉线

       TCP掉线的原因多种多样、不一而足,比如,客人的电脑突然断电、OS崩溃、路由器重启、网线接触不良、因为P2P下载软件而导致网络资源短缺、Internet网络的不稳定等等,但是从程序的角度来说,我们可以总结为两种情况:程序能立即感知的掉线和程序不能立即感知的掉线。

1. 程序能立即感知的掉线

       也就是说客户端一掉线,服务器端的某个读写对应的TCP连接的线程就会抛出异常,这种情况相对容易处理。而ESFramework针对这种情况,会触发IUserManager的SomeOneDisconnected事件,来通知我们的应用程序。     

   ///<summary>
  /// 客户端连接被关闭时,将触发此事件。不要远程预定该事件。
  ///</summary>
  event CbGeneric<UserData ,DisconnectedType> SomeOneDisconnected;

2. 程序不能立即感知的掉线

       我们都知道,TCP连接的建立,需要经过三次握手;而TCP连接的断开,需要经过四次挥手。掉线通常没什么大不了的,掉就掉了呗,只要四次挥手顺利完成后,服务器和客户端分别做一些善后处理就可以。

       麻烦的事情在于,连接在没有机会完成4次挥手时已经断开了(比如当客人的电脑系统死机,或客人电脑与服务器之间的某处物理网线断开),而服务端以为客户端还正常在线,而客户端也自以为还正常在线。这种程序对现实状态的错误判断有可能引发诸多悲剧。比如,在此情况下,客户端发一个指令给服务器,服务器因为没有收到而一直处于等待指令的状态;而客户端了,以为服务器已经收到了,也就一直处于等待服务端回复的状态。如果程序的其它部分需要依据当前的状态来做后续的操作,那就可能会出问题,因为程序对当前连接状态的判断是错误的。

       毫无疑问,这种对连接状态错误的判断所持续的时间越久,带来可能的危害就越大。当然,如果我们不做任何额外的处理措施,服务器到最后也能感受到客户端的掉线,但是,这个时间可能已经过去了几分钟甚至几十分钟。对于大多数应用来说,这是不可忍受的。 所以,针对这种不能立即感知掉线的情况,我们要做的补救措施,就是帮助程序尽快地获知tcp连接已断开的信息。

      首先,我们可以在Socket上通过Socket.IOControl方法设置KeepAliveValues,来控制底层TCP的保活机制,比如,设定2秒钟检测一次,超过10秒检测失败时抛出异常。  

     byte[] inOptionValues = FillKeepAliveStruct(1100002000);
    socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);

       ESFramework底层已经进行了如此处理。据我们的经验,这种设定可以解决一部分问题,但是仍然会有一些连接在断开后,远远超过10秒才被感知掉。所以,这个补救措施还是远远不够的。我们还需要在应用层加入我们自己的TCP连接状态检测机制,这种机制就是通常所说的“心跳”。

二. "心跳"机制

1.原理

       心跳机制的原理很简单:客户端每隔N秒向服务端发送一个心跳消息,服务端收到心跳消息后,回复同样的心跳消息给客户端。如果服务端或客户端在M秒(M>N)内都没有收到包括心跳消息在内的任何消息,即心跳超时,我们就认为目标TCP连接已经断开了。

       由于不同的应用程序对感知TCP掉线的灵敏度不一样,所以,N和M的值就可以设定的不一样。灵敏度要求越高,N和M就要越小;灵敏度要求越低,N和M就可以越大。而要求灵敏度越高,也是有代价的,那就是需要更频繁地发送心跳消息,如果有几千个连接同时频繁地发送心跳消息,那么其所消耗的资源也是不能忽略的。

       当然,网络环境(如延迟的大小)的好坏,也对会对N和M的值的设定产生影响,比如,网络延迟较大,那么N与M之间的差值也应该越大(比如,M是N的3倍)。否则,可能会产生误判 -- 即TCP连接没有断开,只是因为网络延迟大才及时没收到心跳消息,我们却认为连接已经断开了。

       ESFramework内置了心跳机制:

       在服务端,可通过IRapidServerEngine的HeartbeatTimeoutInSecs属性来设置上面描述的M值。

       在客户端,则通过IRapidPassiveEngine的HeartBeatSpanInSecs属性设置N值。

       当心跳超时时,服务端会触发IUserManager的SomeOneTimeOuted事件,来通知我们的应用程序。     

2.必须关闭掉线的TCP连接

       无论是普通掉线(立即感知)还是心跳超时掉线(非立即感知),都需要关闭对应的TCP连接以释放系统资源。ESFramework将会自动帮我们关闭掉线的TCP连接。

       另外要提醒一点,当TCP连接超时掉线时,服务端会引擎首先会触发IUserManager的SomeOneTimeOuted事件,接着再触发IUserManager的SomeOneDisconnected事件。

3.关闭心跳机制

       比如,在LAN中进行通信的分布式系统,由于网络延迟和意外掉线的几率微乎其微,所以,可以考虑关闭心跳机制。再比如,当我们断点调试客户端程序时,由于断点时间太久,服务端会判断为客户端已经心跳超时掉线了,在这种情况下,也可以关闭心跳机制。那么如何关闭心跳机制了?可以这样做:

  • 将IRapidPassiveEngine的HeartBeatSpanInSecs属性设置为0。这样客户端就不会发送定时的心跳消息了。
  • 将IRapidServerEngine的HeartbeatTimeoutInSecs属性设置为小于等于0。这表示服务端将不再做心跳超时检查。

三. 客户端如何快速感知自己掉线?

      在某些客户端电脑上,比如拔掉网线,或断开wifi,程序可能需要几秒到几分钟才能感知到自己掉线,不同的电脑这个感受的时间不一样。那么如何才能让客户端尽可能快地得到掉线通知了?

      可以利用socket写超时的机制,像下面这样做:

(1)将Socket发送缓冲区的大小设置为0。       对应IRapidPassiveEngine的Advanced属性的SocketSendBuffSize属性。

(2)设置写超时为一个较小的值,如30秒。      对应IRapidPassiveEngine的Advanced属性的WriteTimeoutInSecs属性。

       这样,结合上面的心跳发送机制(如每隔5秒发送一个心跳),则当网络断开后,在发送心跳消息,最多再过30秒,程序就会得到掉线通知了。 

 

 

下一篇:ESFramework 开发手册(08) -- 重登陆模式与掉线重连

上一篇:ESFramework 开发手册(06) -- Rapid通信引擎

-----------------------------------------------------------------------------------------------------------------------------------------------   

下载免费版本的ESFramework 以及 demo源码  

阅读 更多ESFramework开发手册系列文章

Q Q:168757008

官网:www.oraycn.com

导航

首页

官方网站

联系我们

站内搜索

OrayTalk 企业即时通讯系统

傲瑞通官网

详细说明

客户端下载

OrayMeeting 视频会议系统

详细说明

客户端下载

ESFramework 通信框架

详细说明

SDK与Demo下载

ESFramework FAQ

版本变更记录

OMCS 语音视频框架

详细说明

SDK与Demo下载

OMCS FAQ

版本变更记录

OVCS 视频会议Demo

详细说明

源码下载

傲瑞实用组件

SDK下载

H5Media 纯网页音视频交互

NPusher 推流组件

MCapture 语音视频采集组件

MFile 语音视频录制组件

MPlayer 语音视频播放组件

OAUS 自动升级系统

StriveEngine 轻量级的通信引擎

傲瑞组件 FAQ

授权

授权流程

产品选购指南

授权方案说明

授权SDK使用说明

其它

支持信创国产化

SDK使用技巧

联系我们