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

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

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

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

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

   event CbGeneric<UserData> SomeOneDisconnected;

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

我们都知道,TCP连接的建立,需要经过三次握手;而TCP连接的断开,需要经过四次挥手。     

掉线通常没什么大不了的,掉就掉了呗,只要四次挥手顺利完成后,服务器和客户端分别做一些善后处理就可以。

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

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

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

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

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

二.“心跳”机制

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

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

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

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

UserManager通过ESBasic.Threading.Application.HeartBeatChecker来对心跳进行检测,而HeartBeatChecker的SurviveSpanInSecs属性可以用于设置我们所描述的M值。

客户端通过ESPlus.Application.Basic.Passive.HeartBeater来向服务器定时发送心跳消息,而HeartBeater的DetectSpanInSecs属性可以用于设置N值。

当你在使用Rapid引擎时,关于心跳机制的组件已经自动为你组装好了。由于RapidServerEngine和RapidPassiveEngine没有暴露出HeartBeatChecker和HeartBeater,所以,我们不能直接通过HeartBeatChecker和HeartBeater设定M和N的值,但是,RapidServerEngine和RapidPassiveEngine分别提供了TimeoutSpanInSecs属性和HeartBeatSpanInSecs属性来间接地设定M和N。

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

无论是普通掉线(立即感知)还是心跳超时掉线(非立即感知),都需要关闭对应的TCP连接以释放系统资源。ITcpServerEngine接口提供了CloseOneConnection方法以关闭目标连接。

   void CloseOneConnection(IUserAddress adderss);

当普通掉线时,ITcpServerEngine会自动关闭了TCP连接;但是,当心跳超时掉线时,我们需要自己手动关闭对应的连接。幸运的是,如果我们使用了ESPlus.Application.Basic命名空间下的组件,ESPlus也会自动帮我们关闭超时掉线的连接。Rapid引擎使用了ESPlus.Application.Basic下的组件,所以,使用Rapid引擎的朋友也不用再自己手动关闭连接了。

另外要提醒一点,如果使用Rapid引擎,那么当连接超时掉线时,服务端会首先触发IUserManager的SomeOneTimeOuted事件,接着再触发IUserManager的SomeOneDisconnected事件(由于ESPlus调用CloseOneConnection方法时触发)。

四.UDP与“心跳”

前面介绍的都是关于TCP的掉线的问题,下面我们看看UDP。

由于UDP是无连接的协议,所以,当我们在使用ESFramework的UDP引擎的时候,几乎肯定是需要配备心跳机制的,使用心跳消息确认客户端还在线,以保证服务端不会过早释放对应的Session或长期保留已失效的Session。

ESFramework中的心跳机制相关的组件是与协议无关的,所以既可以用于TCP应用,也可用于UDP应用。但是,由于对UDP没有进行类似Rapid引擎的封装,所以,如果使用ESFramework的UDP引擎开发应用,则需要手动组装与心跳机制相关的组件了。

最新文章

  1. ASP.NET MVC Controller的激活
  2. Windows Phone 八、文件读写IO
  3. 需求:输入一个年份和月份 ,显示当前月日情况 ,星期数要对应准确 * 1.1900年1月1号开始 * 2.星期 : 直接用总天数对7求余数 31 28 59 / 7 = 5 * 3.以\t来个开
  4. 在本地创建angular-ui/bootstrap项目
  5. 预处理语句--#define、#error和#warning
  6. QTP基础学习(二)启动与设置
  7. iOS-代理
  8. android 防止多次点击,它会导致事件侦听响应于其他接口
  9. 【费用流】BZOJ1061: [Noi2008]志愿者招募(这题超好)
  10. React create-react-app Build fails after eject: Cannot find module &#39;@babel/plugin-transform-react-jsx&#39;
  11. three.js全景漫游实践
  12. Windows 10 替换 cmd 的命令行工具
  13. 【转】CLR和JIT的理解、.NET反汇编学习
  14. 【转载】vim 中如何替换选中行或指定几行内的文本
  15. vue 点击一个div,使input获得焦点
  16. October 26th, 2017 Week 43rd Thursday
  17. SQL Server 安装后改动计算机名带来的问题以及解决方法
  18. vue 表单校验(二)
  19. Banana
  20. ng的点滴记录

热门文章

  1. 观察者模式 C++11
  2. windows自带的线程池
  3. UNIX基础--磁盘组织
  4. [Centos] mod_wsgi 安装流程以及遇到问题解决办法。apxs: command not found 或 Sorry, Python developer package does not appear to be installed.
  5. old linkedin profile
  6. percentiles of live data capture
  7. [笔记]The Linux command line
  8. python基础---pymsql
  9. Angularjs directive全面解读(1.4.5)
  10. 精简CSS