ESFramework 开发手册(08) -- 重登陆模式与掉线重连
ESPlus提供的Rapid引擎采用了这样一条规则:当客户端与服务器成功建立TCP连接以后,发送的第一个消息为登录消息,当登录消息中的帐号密码经过服务端的验证后,服务端就会从消息中取出UserID的值,并将其与对应的TCP连接绑定起来。这样,服务端就知道每一个TCP连接所对应的用户UserID,而当我们要求服务端向某个客户端发送消息时,服务端就知道通过哪个TCP连接进行发送了。对同一个服务端而言,TCP连接与UserID是一一对应的,一个TCP连接只能对应一个UserID;同样的,一个UserID最多存在一个TCP连接。
一. 两种重登陆模式
在现实中,经常出现这样的情况:比如我们用的QQ,当我们用一个账号在A地登录了,还未下线,而我又用此账号在B地登录,会发生什么情况了?QQ采用的策略是用新连接取代旧连接,即通知A地的客户端其已经被挤掉线了(如提示“同名的用户已在其它地方登陆”),而对于后续的通信,服务器都将与B地的客户端进行。
QQ采用的这种模式在ESFramework中称为ReplaceOld模式。但是,有的应用可能需要保留A地的连接而忽略新来的B地的连接,对于这种情况,我们可以采用另外一种模式:IgnoreNew。ESFramework通过RelogonMode枚举来定义这两种模式:
public enum RelogonMode { /// <summary> /// 忽略新的连接。 /// </summary> IgnoreNew = 0, /// <summary> /// 使用新的连接取代旧的连接。 /// </summary> ReplaceOld }
我们可以设置用户管理器IUserManager的RelogonMode属性来控制ESFramework采用哪种重登陆模式。比如:
rapidServerEngine.UserManager.RelogonMode = RelogonMode.ReplaceOld;
RapidServerEngine默认的重登陆模式是ReplaceOld。
二. ESFramework对重登陆模式的反应
1. IgnoreNew模式
如果我们采用的是IgnoreNew模式, 当服务端从另外一个新的连接上收到同名用户发来的消息时,ESFramework会触发IUserManager的NewConnectionIgnored事件来通知服务端应用程序:
///<summary> /// 如果RelogonMode为IgnoreNew,并且当从一个新连接上收到一个同名ID用户的消息时将触发此事件。 /// 注意,只有在该事件处理完毕后,才会关闭新连接。可以在该事件的处理函数中,将相关情况通知给客户端。 ///</summary> event CbGeneric<string, IPEndPoint> NewConnectionIgnored;
事件的第一个参数string是同名用户的ID,第二个参数是新连接的客户端地址。ESFramework/ESPlus在触发此事件前后所做的动作有:
- 通知新连接对应的客户端,已经有同名的用户在线了,新的连接将被关闭。新连接对应的客户端Rapid引擎的Intialize方法将返回LogonResult.HadLoggedOn。
- 关闭新的连接。
2. ReplaceOld模式
如果我们采用的是ReplaceOld模式, 当服务端从另外一个新的连接上收到同名用户发来的消息时,ESFramework会触发IUserManager的SomeOneBeingPushedOut事件来通知服务端应用程序:
///<summary> /// 如果RelogonMode为ReplaceOld,并且当从另外一个新连接上收到一个同名ID用户的消息时将触发此事件。 /// 注意,只有在该事件处理完毕后,才会真正关闭旧的连接并使用新的地址取代旧的地址。可以在该事件的处理函数中,将相关情况通知给旧连接的客户端。 ///</summary> event CbGeneric<UserData> SomeOneBeingPushedOut;
即同名的老连接对应的客户端被挤掉了,事件的参数包含了旧连接对应的相关信息。
同样的,ESFramework/ESPlus在触发此事件前后所做的动作有:
- 通知旧连接对应的客户端,有同名的用户连接上来,旧的连接将被关闭。客户端将触发IBasicOutter的BeingPushedOut事件。
- 关闭旧的连接。
三. 掉线重连
IRapidPassiveEngine在与服务端的TCP连接断开时,会尝试自动重连服务端,直到重连成功为止。
但是有两种情况的掉线,是不会自动重连的:
- 客户端被挤掉线。这种情况下,如果也自动重连,将会导致新登陆的同名客户端被挤掉线,新的客户端又会自动重连......,从而陷入“挤掉线-重连-挤掉线-......”的死循环。
- 客户端被踢出而导致的掉线。我们可以通过IBasicOutter的KickOut方法或者IBasicController的KickOut方法将某个在线用户踢掉,这种情况下,被踢掉的客户端不会再自动重连。
如果在这两种情况下掉线,应用仍然要再次连接服务端,那么我们可以手动进行--即重新调用IRapidPassiveEngine的初始化方法。
还有一种情况要特别提醒一下。由于客户端引擎在初次连接服务器并登录成功后,会记录当前登录用户的帐号和密码,等之后掉线重连时,会再次自动用记录的帐号密码进行登录。所以,如果在中间过程中,用户的密码被修改了,重连登录就会失败。IRapidPassiveEngine的重连完成事件RelogonCompleted的参数LogonResponse的值将会是LogonResult.Failed。我们可以预定RelogonCompleted事件来获取重连失败的通知。
请特别注意:对于服务端而言,是无所谓“重连”的,客户端重连上服务端与客户端初次连接上服务端采用一样的处理模式。客户端只要一掉线,服务端就会清除该客户端对应的Session以及相关的状态数据。如果有的应用需要服务端支持重连模型,也很容易做到。我们的服务端可以在接收到用户掉线的通知时,不清除该用户相关的业务状态数据,而只是将其改为不可用,等客户端重连上来后,再激活该用户对应的业务数据就可以了。
下一篇:ESFramework 开发手册(09) -- ACK机制、同步调用、回复异步调用
上一篇:ESFramework 开发手册(07) -- 掉线与心跳机制
-----------------------------------------------------------------------------------------------------------------------------------------------
Q Q:168757008