ESFramework 开发手册(01) -- 发送和处理信息

本文介绍ESFramework 开发手册(00) -- 概述一文中提到的四大武器的第一个:发送和处理自定义信息。

使用通信框架最基础的需求就是收发信息,ESFramework底层已经为我们封装好了所有与信息收发相关的操作,我们只要调用ESPlus.Application.CustomizeInfo命名空间下的相关组件的API来发送信息,以及实现对应的处理器接口来处理收到的信息就可以了。

1.客户端发送信息

  客户端可以发送信息给服务端,也可以发送信息给其他在线用户。
  客户端通过ESPlus.Application.CustomizeInfo.Passive.ICustomizeOutter接口提供的方法来发送信息。
  我们可以从ESPlus.Rapid.IRapidPassiveEngine暴露的CustomizeOutter属性来获取ICustomizeOutter引用。

   public interface ICustomizeOutter:IOutter {     /// <summary> /// 向服务器发送信息。 /// </summary> /// <param name="informationType">自定义信息类型</param> /// <param name="info">信息</param> void Send(int informationType, byte[] info); /// <summary> /// 向在线用户targetUserID发送信息。 /// </summary> /// <param name="targetUserID">接收消息的目标用户ID</param> /// <param name="informationType">自定义信息类型</param> /// <param name="info">信息</param> void Send(string targetUserID, int informationType, byte[] info); /// <summary> /// 通过P2P通道(不一定可靠)向在线用户targetUserID发送信息。 /// </summary> /// <param name="targetUserID">接收消息的目标用户ID</param> /// <param name="informationType">自定义信息类型</param> /// <param name="info">信息</param> /// <param name="actionType">当P2P通道又不存在时,采取的操作</param> void SendByP2PChannel(string targetUserID, int informationType, byte[] info ,ActionTypeOnNoP2PChannel actionType);      /// <summary> /// 即使与目标用户之间有可靠的P2P通道存在,也要通过服务器转发信息。 /// </summary> /// <param name="targetUserID">接收消息的目标用户ID</param> /// <param name="informationType">自定义信息类型</param> /// <param name="info">信息</param> void TransferByServer(string targetUserID, int informationType, byte[] info); /// <summary> /// 向服务器发送信息,并等待服务器的ACK。当前调用线程会一直阻塞,直到收到ACK;如果超时都没有收到ACK,则将抛出Timeout异常。 /// </summary> /// <param name="informationType">自定义信息类型</param> /// <param name="info">信息</param> void SendCertainly(int informationType, byte[] info); /// <summary> /// 向在线用户targetUserID发送信息,并等待其ACK。当前调用线程会一直阻塞,直到收到ACK;如果超时都没收到ACK,则将抛出Timeout异常。 /// </summary> /// <param name="targetUserID">接收消息的目标用户ID</param> /// <param name="informationType">自定义信息类型</param> /// <param name="info">信息</param> void SendCertainly(string targetUserID, int informationType, byte[] info); /// <summary> /// 向服务器提交请求信息,并返回服务器的应答信息。如果超时没有应答则将抛出Timeout异常。 /// </summary> /// <param name="informationType">自定义请求信息的类型</param> /// <param name="info">请求信息</param> /// <returns>服务器的应答信息</returns> byte[] Query(int informationType, byte[] info); /// <summary> /// 向在线目标用户提交请求信息,并返回应答信息。如果目标用户不在线,或超时没有应答则将抛出Timeout异常。 /// </summary> /// <param name="targetUserID">接收并处理请求消息的目标用户ID</param> /// <param name="informationType">自定义请求信息的类型</param> /// <param name="info">请求信息</param> /// <returns>应答信息</returns> byte[] Query(string targetUserID, int informationType, byte[] info); /// <summary> /// 向目标组内所有在线用户广播信息。如果groupID传入null,则信息会广播给当前服务器上的所有在线用户。(服务端采用Post) /// </summary> /// <param name="groupID">接收广播信息的目标组的ID</param> /// <param name="informationType">自定义信息类型</param> /// <param name="info">信息</param> void BroadcastInGroup(string groupID, int informationType, byte[] info); }

发送信息有几种方式:

普通发送:

调用Send方法进行普通发送,即将信息写入网络流后就立即返回。

带ACK机制的发送:

调用SendCertainly方法发送信息时会启用ACK机制,即将信息发送出去后,调用并不返回,而是要等到接收方的ACK后,才返回。ACK机制是由ESPlus底层实现的,我们直接使用,不需要做任何额外的其它工作。关于带ACK机制的信息发送的更多内容可以参见自定义信息的ACK机制

信息同步调用:

调用Query方法可以发送请求信息,并返回接收方处理请求后的应答信息。就像方法调用一样 - - 使用参数调用方法并返回结果。从Query方法的重载看到,信息同步调用的对象既可以是服务端、也可以是另外一个在线客户端。关于信息同步调用的更多内容可以参见消息同步调用

使用P2P通道发送:

调用SendByP2PChannel方法可以明确指定使用P2P通道发送进行发送,这是一种普通发送。如果与接收者之间的P2P通道不存在,则由ActionTypeOnNoP2PChannel参数指示如何动作:使用服务器转发、或者丢弃信息。关于P2P通道的更多内容可看见后面的P2P通道章节。

强制经过服务器转发:

对于某些类型的P2P信息,我们可能想在服务端监控它,如果这些信息还是经过P2P通道发送的话,那么服务端将捕获不到这些信息。TransferByServer方法用于解决这一问题。如果调用TransferByServer方法发送信息,那么即使有可靠的P2P通道存在,信息仍然会经过服务器中转。

组广播:

调用BroadcastInGroup方法可以将信息发送给目标组内的在线用户或当前服务器上的所有在线用户。

2.服务端发送信息

服务端可以通过ESPlus.Application.CustomizeInfo.Server.ICustomizeController接口向客户端发送信息和广播、以及同步调用客户端。
我们可以从ESPlus.Rapid.IRapidServerEngine暴露的CustomizeController属性来获取ICustomizeController引用。

public interface ICustomizeController {      /// <summary> /// 当收到来自客户端(由ICustomizeInfoOutter发出)的任何自定义信息时,将触发该事件。 /// 该事件处理函数不能抛出异常,否则将导致后续消息处理流程中断。另外,该事件处理函数应尽快返回。 /// </summary> event CbGeneric<Information> InformationReceived; /// <summary> /// 当因为目标用户不在线而导致服务端转发自定义信息失败时,将触发该事件。参数为转发失败的信息。 /// </summary> event CbGeneric<Information> TransmitFailed; /// <summary> /// 向ID为userID的在线用户发送信息。如果用户不在线,则直接返回。 /// </summary> /// <param name="userID">接收消息的用户ID</param> /// <param name="informationType">自定义信息类型</param> /// <param name="info">信息</param> void Send(string userID, int informationType, byte[] info); /// <summary> /// 向当前AS上的在线用户发送信息,并等待其ACK。当前调用线程会一直阻塞,直到收到ACK;如果超时都没有收到ACK,则将抛出Timeout异常。 /// </summary> /// <param name="userID">接收消息的用户ID</param> /// <param name="informationType">自定义信息类型</param> /// <param name="info">信息</param> void SendCertainlyToLocalClient(string userID, int informationType, byte[] info); /// <summary> /// 向ID为userID的在线用户投递信息。如果用户不在线,则直接返回。 /// </summary> /// <param name="userID">接收消息的用户ID</param> /// <param name="informationType">自定义信息类型</param> /// <param name="info">信息</param> void Post(string userID, int informationType, byte[] info); /// <summary> /// 询问当前AS的在线用户,并返回应答信息。如果目标用户不在当前AS上,或超时没有应答则将抛出Timeout异常。 /// </summary> /// <param name="userID">接收并处理服务器询问的目标用户ID</param> /// <param name="informationType">自定义请求信息的类型</param> /// <param name="info">请求信息</param> /// <returns>客户端给出的应答信息</returns> byte[] QueryLocalClient(string userID, int informationType, byte[] info); /// <summary> /// 向目标组内的在线用户发送广播信息。如果groupID传入null,则信息会广播给当前服务器上的所有在线用户。 /// </summary> /// <param name="groupID">接收消息的组ID</param> /// <param name="informationType">自定义信息类型</param> /// <param name="info">广播信息</param> void SendBroacast(string groupID, int informationType, byte[] info); /// <summary> /// 向目标组内的在线用户投递广播信息。如果groupID传入null,则信息会广播给当前服务器上的所有在线用户。 /// </summary> /// <param name="groupID">接收消息的组ID</param> /// <param name="informationType">自定义信息类型</param> /// <param name="info">广播信息</param> void PostBroacast(string groupID, int informationType, byte[] info); }

各个方法含义几乎与客户端是一致的,但是,对于普通发送和广播,服务端可以选择是采用同步发送(Send)还是异步发送(Post)。对于同时要给众多在线用户发送信息时(比如广播),一般采用Post方式,这样可以避免因为某些用户的网路延迟而延迟对后续用户的信息发送。
ICustomizeController接口还暴露了两个事件,我们可以通过预定InformationReceived事件来监控所有来自客户端的自定义信息;通过预定TransmitFailed事件来监控那些转发失败的信息。
SendCertainlyToLocalClient方法和QueryLocalClient方法的名称都以LocalClient结尾,其在ESPlatform群集平台中就会显示其特别的含义 - - 这两个方法只能针对连接到当前服务端的客户端进行调用。而其它的几个方法,则是可以将信息发送给任何在线用户的,即使该用户位于群集中的其它服务器上。

3.客户端处理信息

客户端可以收到来自其它客户端或服务端的信息、广播、以及同步调用。那么,我们在客户端如何来处理这些信息了?只要实现ESPlus.Application.CustomizeInfo.Passive.ICustomizeHandler接口即可。

public interface ICustomizeHandler:IBusinessHandler {       /// <summary> /// 处理来自其他用户的信息。 /// </summary> /// <param name="sourceUserID">发出信息的用户ID</param> /// <param name="informationType">自定义信息类型</param> /// <param name="info">信息</param> void HandleInformation(string sourceUserID, int informationType, byte[] info); /// <summary> /// 处理来自其它在线用户的P2P请求并返回应答信息。(即处理客户端ICustomizeOutter.Query发出的请求) /// </summary> /// <param name="sourceUserID">发送请求信息的用户ID</param> /// <param name="informationType">自定义请求信息的类型</param> /// <param name="info">P2P请求信息</param> /// <returns>P2P应答信息</returns> byte[] HandleQuery(string sourceUserID, int informationType, byte[] info); /// <summary> /// 处理来自其他用户的广播信息。 /// </summary> /// <param name="broadcasterID">发出广播信息的用户ID</param> /// <param name="groupID">接收广播信息的组ID</param> /// <param name="informationType">自定义信息类型</param> /// <param name="info">广播信息</param> void HandleBroadcast(string broadcasterID, string groupID, int informationType, byte[] info); /// <summary> /// 处理来自服务端的信息。(即处理服务端ICustomizeController.Send或Post发出的请求) /// </summary> /// <param name="informationType">自定义信息类型</param> /// <param name="info">信息</param> void HandleInformationFromServer(int informationType, byte[] info); /// <summary> /// 处理来自服务端的询问信息,并给出应答信息。(即处理服务端ICustomizeController.QueryLocalClient发出的请求) /// </summary> /// <param name="informationType">自定义信息类型</param> /// <param name="info">信息</param> /// <returns>应答信息</returns> byte[] HandleQueryFromServer(int informationType, byte[] info); /// <summary> /// 处理来自服务器的广播信息。 /// </summary> /// <param name="groupID">接收广播信息的组ID</param> /// <param name="informationType">自定义信息类型</param> /// <param name="info">广播信息</param> void HandleBroadcastFromServer(string groupID, int informationType, byte[] info); }

(1)凡是以FromServer结尾的方法,都表示被处理的信息是来自服务端的;否则,表示被处理的信息是由其它在线客户端发出的。
(2)ICustomizeHandler接口的所有方法都是在后台线程中被调用的,所以如果这些方法的实现中涉及到了操作UI,一定要将调用转发到UI线程。
我们可以将ICustomizeHandler实现类的实例传递给ESPlus.Rapid.IRapidPassiveEngine的Initialize方法以挂接到框架。

4.服务端处理信息

服务端只要实现ESPlus.Application.CustomizeInfo.Server.ICustomizeHandler接口就可以处理来自客户端的信息(转发的信息除外)。

public interface ICustomizeHandler:IBusinessHandler {       /// <summary> /// 处理来自客户端的自定义信息。 /// </summary> /// <param name="sourceUserID">发送该信息的用户ID</param> /// <param name="informationType">自定义信息类型</param> /// <param name="info">信息</param> void HandleInformation(string sourceUserID, int informationType, byte[] info); /// <summary> /// 处理来自客户端的请求并返回应答信息。(即处理客户端ICustomizeOutter.Query发出的请求) /// </summary> /// <param name="sourceUserID">发送该请求信息的用户ID</param> /// <param name="informationType">自定义请求信息的类型</param> /// <param name="info">请求信息</param> /// <returns>应答信息</returns> byte[] HandleQuery(string sourceUserID, int informationType, byte[] info); }

我们可以将服务端ICustomizeHandler实现类的实例传递给ESPlus.Rapid.IRapidServerEngine的Initialize方法以挂接到框架。

5.发送与处理的对应关系  

上述的四个接口中有两个是用于发送信息,另外两个是用于处理信息。那么,用哪个方法发出的信息是由处理器的哪个对应的方法来处理的了?我们这里用箭头图示把它们匹配起来。

6.业务处理器

我们看到客户端和服务端的ICustomizeHandler都是从IBusinessHandler接口继承的,包括还有一些后面即将介绍的处理器接口(命名以“Handler”结尾的)都是从IBusinessHandler继承的,IBusinessHandler即业务处理器,表示其用于处理我们应用系统的具体业务逻辑。

(1)业务处理器将在后台线程中被调用,所以,实现业务处理器的方法中如果涉及到了UI操作,则必须将调用转发到UI线程。

(2)业务处理器的方法必须尽可能快地返回,否则,将不能及时地处理后续的消息。如果某个业务处理方法非常耗时,可以考虑使用异步方式。

最后,大家可以查看demo的源码,并运行demo,来了解信息发送和处理的流程。谢谢。

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

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

关于ESFramework的任何问题,欢迎联系我们:

电话:15807162718
Q Q:372841921

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值