ESFramework 开发手册(06) -- Rapid通信引擎
ESPlus.Rapid命名空间提供了我们可以直接使用的客户端Rapid引擎和服务端Rapid引擎。Rapid引擎将ESFramework和ESPlus提供的各种组件装配成一个整体,将结构的复杂性隐藏在引擎的内部,而提供给我们一个简单易用的接口。在引擎对象初始化成功之后,我们就可以使用引擎对象暴露出的四大武器和两个可选功能了。Rapid引擎底层使用的核心是ESFramework的StreamTcpEngine,其采用TCP协议、并使用了紧凑的二进制消息格式。
一. 客户端Rapid引擎
ESPlus.Rapid. IRapidPassiveEngine是客户端Rapid引擎的接口。观察IRapidPassiveEngine接口的定义,我们就可以了解客户端Rapid引擎的使用方法了。
public interface IRapidPassiveEngine { /// <summary> /// 当客户端与服务器的TCP连接断开时,将触发此事件。 /// </summary> event CbGeneric ConnectionInterrupted; /// <summary> /// 自动重连开始时,触发此事件。如果重连成功则将重新登录,并触发RelogonCompleted事件。 /// </summary> event CbGeneric ConnectionRebuildStart; /// <summary> /// 当断线重连成功时,会自动登录服务器验证用户账号密码,并触发此事件。如果验证失败,则与服务器的连接将会断开,且后续不会再自动重连。事件参数表明了登录验证的结果。 /// </summary> event CbGeneric<LogonResponse> RelogonCompleted; /// <summary> /// 当前引擎所连接的服务器的地址。 /// </summary> AgileIPE ServerAddress { get; } /// <summary> /// 当前是否处于连接状态。 /// </summary> bool Connected { get; } /// <summary> /// 与服务器之间的通道是否处于繁忙状态? /// </summary> bool ChannelIsBusy { get; } /// <summary> /// 系统标志。引擎在初始化时会提交给服务器验证客户端是否是正确的系统。(也可以被借用于登陆增强验证)。(如果要set该属性,则必须在调用Initialize方法之前设置才有效。) /// </summary> string SystemToken { get; set; }
/// <summary> /// 当前登录的用户UserID。(只有Initialize方法调用成功之后,该属性才可正常使用) /// </summary> string CurrentUserID { get; } /// <summary> /// 每隔多长时间(秒)发送一次心跳消息。如果小于等于0,表示不发送定时心跳。默认值为10秒。(如果要set该属性,则必须在调用Initialize方法之前设置才有效。) /// </summary> int HeartBeatSpanInSecs { get; set; } /// <summary> /// 当通过ICustomizeOutter进行同步调用时,等待回复的最长时间。如果小于等于0,表示一直阻塞调用线程直到等到回复为止。默认值为30秒。(如果要set该属性,则必须在调用Initialize方法之前设置才有效。) /// </summary> int WaitResponseTimeoutInSecs { get; set; } /// <summary> /// 如果需要支持WSS(SSL加密的WebSocket),则必须设置该属性。默认值为null,表示不启用WSS。(如果要set该属性,则必须在调用Initialize方法之前设置才有效。) /// </summary> WssOptions WssOptions{ get; set; } /// <summary> /// Sock5代理服务器信息。如果不需要代理,则设置为null。(如果要set该属性,则必须在调用Initialize方法之前设置才有效。) /// </summary> Sock5ProxyInfo Sock5ProxyInfo { get; set; } /// <summary> /// P2P服务器的地址。默认值为null。(如果要set该属性,则必须在调用Initialize方法之前设置才有效。) /// 如果服务端引擎开启了UseAsP2PServer,则可以不设置该属性。如果同时又设置该属性为非null值,则以设置值为准。 /// </summary> AgileIPE P2PServerAddress { get; set; } /// <summary> /// 该接口用于向服务器发送基本的请求,如获取自己的IP、获取所有在线用户列表等等。(只有Initialize方法调用成功之后,该属性才可正常使用) /// </summary> IBasicOutter BasicOutter { get; } /// <summary> /// 该接口用于向服务器或其它在线用户发送自定义信息。(只有Initialize方法调用成功之后,该属性才可正常使用) /// </summary> ICustomizeOutter CustomizeOutter { get; } /// <summary> /// 该接口用于向服务器或其它在线用户发送文件。(只有Initialize方法调用成功之后,该属性才可正常使用) /// </summary> IFileOutter FileOutter { get; } /// <summary> /// 该接口用于控制和管理所有的P2P通道。(只有Initialize方法调用成功之后,该属性才可正常使用) /// </summary> IP2PController P2PController { get; } /// <summary> /// 该接口用于获取好友列表及接收好友状态改变通知。(只有Initialize方法调用成功之后,该属性才可正常使用) /// </summary> IFriendsOutter FriendsOutter { get; } /// <summary> /// 该接口用于客户端发送与组操作相关的信息和广播信息。(只有Initialize方法调用成功之后,该属性才可正常使用) /// </summary> IGroupOutter GroupOutter { get; } /// <summary> /// 完成客户端引擎的初始化,与服务器建立TCP连接,连接成功后立即验证用户密码。如果连接失败,则抛出异常。 /// </summary> /// <param name="userID">当前登录的用户ID,由数字和字母组成,最大长度为10</param> /// <param name="logonPassword">用户登陆密码。</param> /// <param name="serverIP">服务器的IP地址。</param> /// <param name="serverPort">服务器的端口。</param> /// <param name="customizeHandler">自定义处理器,用于处理服务器或其它用户发送过来的消息</param> LogonResponse Initialize(string userID, string logonPassword, string serverIP, int serverPort, ICustomizeHandler customizeHandler);
/// <summary> /// 关闭并释放客户端通信引擎。 /// </summary> void Close(); }
1. 属性
- 请注意,如果需要通过引擎的setter属性设置某些参数(如HeartBeatSpanInSecs等),必须在调用Initialize方法之前设置才能生效。如果要通过引擎的getter属性使用某些组件(如BasicOutter),必须在引擎初始化成功后,才能正常使用。
- 当客户端是通过代理服务器上Internet时,可以通过Sock5ProxyInfo属性来设置Sock5代理的相关信息。
- SystemToken属性的值用于作为参数传递到服务端IBasicBusinessHandler接口的VerifyUser方法,以进行登陆验证。如果有的系统需要验证除了密码之外更多的信息,那么也可以通过systemToken传递这些额外信息。
- WaitResponseTimeoutInSecs属性用于设置同步调用(以及ACK机制的信息发送)时等待回复的超时时间。
- 当独立部署的P2P服务器时,我们可以通过P2PServerAddress属性设定P2P服务器的地址信息。关于部署P2P服务器的更多内容,可以参见ESFramework 使用技巧 -- 部署P2P服务器。
- ChannelIsBusy:指当前客户端是否有消息正在发往服务器。另外几个只读属性:ServerAddress、Connected、CurrentUserID,注释已经很清楚了。
- BasicOutter、CustomizeOutter、FileOutter、P2PController、FriendsOutter、GroupOutter等属性,我们已经在前面章节进行了详细的描述,不再赘述。
- WssOptions 属性用于开启对 WebSocket 的 SSL 加密(即wss)支持。可按如下设置:
WssOptions wssOptions = new WssOptions( new X509Certificate2("D:\\test.pfx", "password") ,SslProtocols.Default ,false); rapidServerEngine.WssOptions = wssOptions;
2. 方法
- 参数设置好后,就可以调用Initialize方法进行初始化。Initialize方法需要传入自定义信息处理器ICustomizeHandler。
- Initialize方法的返回值 LogonResponse 的 LogonResult 属性是个枚举值,说明了登录的结果。LogonResult 的定义如下:
- 正如在ESFramework 开发手册(02) -- 基础功能与状态通知讲到的 ,当TCP重连成功时,Rapid客户端引擎会自动登录服务器重新验证用户账号和密码,并触发RelogonCompleted事件。开发人员应该依据IRapidPassiveEngine的RelogonCompleted事件来作为重连成功/失败的依据。
- 当不再需要使用引擎实例时,请调用Close方法以释放资源。
public enum LogonResult { /// <summary> /// 登录成功 /// </summary> Succeed = 0, /// <summary> /// 登录失败 /// </summary> Failed, /// <summary> /// 已在其它地方登陆 /// </summary> HadLoggedOn, /// <summary> /// 客户端与服务端的框架不匹配(比如版本不一致或授权帐号不一致)。 /// </summary> VersionMismatched }
二. 服务端Rapid引擎
ESPlus.Rapid. IRapidServerEngine是服务端Rapid引擎的接口。我们来看看IRapidServerEngine接口的定义。
public interface IRapidServerEngine { IServiceTypeNameMatcher ServiceTypeNameMatcher { get; } /// <summary> /// 【可选】如果需要好友关系支持,则必须设置该属性。(如果要set该属性,则必须在调用Initialize方法之前设置才有效。) /// </summary> IFriendsManager FriendsManager { set; } /// <summary> /// 【可选】如果需要组关系支持,则必须设置该属性。可以使用内置的动态组DynamicGroupManager。(如果要set该属性,则必须在调用Initialize方法之前设置才有效。) /// </summary> IGroupManager GroupManager { set; } /// <summary> /// 通过哪个IP地址提供服务。如果设为null,则表示绑定本地所有IP。默认值为null。(如果要set该属性,则必须在调用Initialize方法之前设置才有效。) /// </summary> string IPAddressBinding { get; set; } /// <summary> /// 心跳超时间隔(秒)。即服务端多久没有收到客户端的心跳消息,就视客户端为超时掉线。默认值为30秒。如果设置小于等于0,则表示不做心跳检查。(如果要set该属性,则必须在调用Initialize方法之前设置才有效。) /// </summary> int HeartbeatTimeoutInSecs { get; set; } /// <summary> /// 当通过ICustomizeController.QueryClient方法进行同步调用时,等待回复的最长时间。如果小于等于0,表示一直阻塞调用线程直到等到回复为止。默认值为30秒。(如果要set该属性,则必须在调用Initialize方法之前设置才有效。) /// </summary> int WaitResponseTimeoutInSecs { get; set; } /// <summary> /// 是否自动回复客户端发过来的心跳消息(将心跳消息原封不动地发回客户端)。默认值为true。(如果要set该属性,则必须在调用Initialize方法之前设置才有效。) /// </summary> bool AutoRespondHeartBeat { get; set; }
/// <summary> /// 是否同时作为P2P服务器运行。默认值为true(P2P服务器将使用UDP端口,端口号为当前引擎监听的端口号加1)。(如果要set该属性,则必须要在调用Initialize方法之前设置才有效。) /// </summary> bool UseAsP2PServer { get; set; }
/// <summary> /// 当前在线连接的数量。 /// </summary> int ConnectionCount { get; } /// <summary> /// 服务器允许最大的同时连接数。 /// </summary> int MaxConnectionCount { get; } /// <summary> /// 当前监听的端口。 /// </summary> int Port { get; } /// <summary> /// 服务端实例的唯一编号。 /// </summary> int ServerID { get; } /// <summary> /// 通过此接口,可以获取用户的相关信息以及用户上/下线的事件通知。(只有在RapidServerEngine初始化成功后,才能正常使用。) /// </summary> IUserManager UserManager { get; } /// <summary> /// 平台用户管理器(用于ESPlatform)。可以获取群集中所有在线用户信息。(只有在RapidServerEngine初始化成功后,才能正常使用。) /// </summary> IPlatformUserManager PlatformUserManager { get; } /// <summary> /// 通过此接口,服务端可以将目标用户从服务器中踢出,并关闭其对应的tcp连接。(只有在RapidServerEngine初始化成功后,才能正常使用。) /// </summary> IBasicController BasicController { get; set; } /// <summary> /// 通过此接口,服务端可以主动向在线用户发送/投递自定义信息。(只有在RapidServerEngine初始化成功后,才能正常使用。) /// </summary> ICustomizeController CustomizeController { get; } /// <summary> /// 通过此接口,服务端可以主动向在线用户发送文件。(只有在RapidServerEngine初始化成功后,才能正常使用。) /// </summary> IFileController FileController { get; } /// <summary> /// 通过此接口,服务端可以控制好友通知。(只有在RapidServerEngine初始化成功后,才能正常使用。) /// </summary> IFriendsController FriendsController { get; } /// <summary> /// 通过此接口,服务端可以控制组友通知和主动向某个组广播信息。(只有在RapidServerEngine初始化成功后,才能正常使用。) /// </summary> IGroupController GroupController { get; } /// <summary> /// 完成服务端引擎的初始化,并启动服务端引擎。 /// </summary> /// <param name="port">用于提供tcp通信服务的端口</param> /// <param name="customizeHandler">服务器通过此接口来处理客户端提交给服务端自定义信息。</param> void Initialize(int port, ICustomizeHandler customizeHandler); /// <summary> /// 完成服务端引擎的初始化,并启动服务端引擎。 /// </summary> /// <param name="port">用于提供tcp通信服务的端口</param> /// <param name="customizeHandler">服务器通过此接口来处理客户端提交给服务端自定义信息。</param> /// <param name="basicHandler">用于验证客户端登陆密码。如果不需要验证,则直接传入null。</param> void Initialize(int port, ICustomizeHandler customizeHandler, IBasicHandler basicHandler);
/// <summary> /// 关闭服务端引擎。 /// </summary> void Close(); }
1. 属性
- 请注意,同客户端一样,如果需要通过引擎的setter属性设置某些参数(如FriendsManager、GroupManager等),必须在调用Initialize方法之前设置才能生效。如果要通过引擎的getter属性使用某些组件(如UserManager、FriendsController等),必须在Initialize方法执行成功后,才能正常使用。
- IPAddressBinding 用于服务器有多个IP的情况,通过设定IPAddressBinding 的值可以指定服务端要监听哪个IP,如果设为null,表示监听服务器绑定的所有IP。
- UseAsP2PServer用于指示是否在服务端中集成部署P2P服务器。关于部署P2P服务器的更多内容,可以参见ESFramework 使用技巧 -- 部署P2P服务器。
- PlatformUserManager 用于ESPlatform,通过它可以获取群集中所有在线用户信息。当没有群集存在时,PlatformUserManager等同于UserManager。
- FriendManager和GroupManager属性分别用于管理好友和组,这两个是可选功能,如果不需要好友和组的支持,可以忽略它们。其详细介绍请参见好友与组。
- BasicController、CustomizeController、FileController、UserManager、FriendsController、GroupController的作用我们前面已经详细介绍了,这里不再赘述。
2. 方法
- 属性及参数设置好后,就可以调用Initialize方法进行初始化。Initialize方法需要的Handler参数已经在前面详细介绍过了。
- 当不再需要使用引擎实例时,调用Close方法以释放资源。
3. 默认主窗体
我们的所有Demo使用的都是 ESFramework.Boost(源码开放) 提供了默认的传主窗体ESFramework.Boost.Controls.MainServerForm。 一般,只有在调试和测试阶段,我们才会使用该窗体来实时显示每个在线用户的状态。在正式发布时,则建议不要使用该窗体了。特别是在高性能的应用中,由于该窗体会实时刷新每个用户的状态数据,其消耗CPU是不可忽视的。
当服务端Rapid引擎初始化成功之后,就可以构造MainServerForm并将其显示出来了,其截图如下:
简单提一下,图中显示的设备类型可以是.NET客户端、Silverlight客户端、ios客户端等等。不同平台的客户端引擎可以连接上同一个服务器,并相互通信。
如果具体的应用需要在主窗体中做更多的事情,则需要自己来实现。可以从这里下载 ESFramework.Boost的源码,大家可以在其基础上添加所需的功能。
当然,我们也可以在windows服务中使用服务端Rapid引擎,只要在windows服务启动的时候初始化Rapid引擎就可以了,这样就不需要窗体了,而且还节省了UI刷新所消耗的性能。
三. RapidEngineFactory
我们不能通过new来创建服务端或客户端Rapid引擎的实例,必须通过RapidEngineFactory的静态方法来创建它们。
/// <summary> /// Rapid引擎工厂。 /// </summary> public static class RapidEngineFactory { /// <summary> /// 创建一个Rapid客户端引擎实例。 /// </summary> public static IRapidPassiveEngine CreatePassiveEngine();
/// <summary> /// 创建一个Rapid客户端引擎实例。 /// </summary> public static IRapidServerEngine CreateServerEngine();
/// <summary> /// 创建一个P2P服务器实例。 /// </summary> public static IP2PServer CreateP2PServer(); }
我们甚至可以通过 RapidEngineFactory的CreateP2PServer来创建P2P服务器实例,P2P服务器用于协助客户端之间创建P2P通道(P2P“打洞”)。
到此为止,我们已经将基于ESFramework进行二次开发所需要掌握的所有基础设施都已介绍完毕,大家基本可以上手ESFramework开发了。
关于二次开发的步骤,可以参考这篇总结:ESFramework 开发手册(13) -- ESFramework 二次开发说明
如果大家结合阅读我们demo的源码,应该就更清楚明白了。后续我们将会写一些高阶一点的以及ESFramework使用技巧方面的文章,以帮助大家更好地理解和使用ESFramework。
下一篇:ESFramework 开发手册(07) -- 掉线与心跳机制
上一篇:ESFramework 开发手册(05) -- 好友与组
-----------------------------------------------------------------------------------------------------------------------------------------------
Q Q:168757008