ESFramework 使用技巧 -- 实现监控

    有些使用ESFramework/ESPlus进行二次开发的朋友,经常来询问要如何做才能在服务端监控客户端发送的所有或部分重要的自定义信息了?鉴于这是一个比较常见的需求,所以,这里我们就简单介绍一下如何在基于ESFramework/ESPlus开发的系统上增加信息监控的功能。

 

一.深入ICustomizeInfoOutter接口

  正如ESFramework 开发手册(01) -- 发送和处理信息一文所介绍的,客户端是使用ESPlus.Application.CustomizeInfo.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);    
            } 

      该接口的所有方法可以分为两类:一类方法是接收自定义信息的目标用户是服务端;另一类是接收自定义信息的目标用户是其他在线用户。无论是哪种情况,信息都会到达服务端,或由服务端处理、或经服务端转发。(如果客户端之间开启了P2P通道,则稍后再谈。)所以,在服务端是可以监控到客户端通过ICustomizeInfoOutter接口发出的所有自定义信息的。

      另外,关于同步调用服务端或同步调用其它客户端(Query方法)以及组广播信息(BroadcastInGroup)时,所发出的自定义信息也是如此,而且同步调用其它客户端时的返回信息也是经过服务器转发的,所以,也可以被监控到。

      如果客户端之间开启了P2P通道,它们之间的信息将不再经过服务器中转,而是直接经过P2P通道传送,所以这时,服务器就不能监控到这样的信息了。难道就没有办法了吗?不,我们还有一招。对于那些必须要被监控到的信息,客户端可以采用TransferByServer方法进行发送,该方法会强制信息必须通过服务器中转,即使有P2P通道存在也是这样。所以,这里总结的规则是:如果在客户端之间传递的某个信息必须要被服务端监控到,那么,客户端就应该调用TransferByServer方法来发送该信息。

 

二.截获自定义信息

      在服务端,我们可以预定ICustomizeController接口的InformationReceived事件,来截获客户端发出的所有自定义信息。

 event CbGeneric<Information> InformationReceived; 

(1)当收到来自客户端的任何自定义信息时,将触发该事件。要特别注意的是,该事件的处理函数不能抛出异常,否则将导致后续消息处理流程中断。所以,最好在该事件的处理函数中catch所有的异常

(2)该事件处理函数应尽快返回。因为服务端只有在所有的事件函数执行完毕后,才会继续后续的消息处理流程,所以该事件处理函数返回得越快越好。如果有些处理函数的业务逻辑复杂,比较费时,可以考虑使用异步的方式。

(3)ICustomizeOutter接口发出的所有自定义信息都包含了相同几个方面的内容:自定义信息的类型、自定义信息的数据、信息的接收者。ESPlus使用Information类来封装这些内容,并且增加了SourceID属性以记录发送者的UserID。

      InformationReceived事件只有一个Information类型参数。

    [Serializable]    
    public class Information    
    {        
        #region Ctor        
        public Information() { }        
        public Information(string _sourceID, string _destID, int _infoType, byte[] _content ,bool broadcast)        
        {            
            this.sourceID = _sourceID;            
            this.destID = _destID;            
            this.informationType = _infoType;            
            this.content = _content;            
            this.isBroadcast = broadcast;        
        }         
        #endregion        

        #region SourceID        
        private string sourceID = "";        
        ///<summary>        
        /// 信息的发送者。可以为UserID或者NetServer.SystemUserID。        
        ///</summary>        
        public string SourceID        
        {            
            get { return sourceID; }            
            set { sourceID = value; }        
        }         
        #endregion        

        #region DestID        
        private string destID = "";        
        ///<summary>        
        /// 信息的接收者。可以为UserID或者NetServer.SystemUserID或GroupID(广播消息)。        
        ///</summary>        
        public string DestID        
        {            
            get { return destID; }            
            set { destID = value; }        
        }         
        #endregion        

        #region InformationType        
        private int informationType = 0;        
        ///<summary>        
        /// 自定义信息类型        
        ///</summary>        
        public int InformationType        
        {            
            get { return informationType; }            
            set { informationType = value; }        
        }         
        #endregion        

        #region Content        
        private byte[] content = null;        
        ///<summary>        
        /// 信息的内容        
        ///</summary>        
        public byte[] Content        
        {            
            get { return content; }            
            set { content = value; }        
        }        
        #endregion        

        #region IsBroadcast        
        private bool isBroadcast = false;        
        ///<summary>        
        /// 是否为广播消息。如果是,则DestID为groupID。        
        ///</summary>        
        public bool IsBroadcast        
        {            
            get { return isBroadcast; }            
            set { isBroadcast = value; }        
        }         
        #endregion    
    }

        注意,如果自定义信息的接收者为服务端,则DestID属性的值是NetServer.SystemUserID(即 "_0")。

(4)当预定了InformationReceived事件后,我们可以根据InformationType来筛选那些我们感兴趣的要监控的自定义信息,并对它们做一些记录或其它业务处理。 

 

三.为系统增加监控端

      有些系统不仅仅需要在服务端监控重要的自定义信息,它还可能有这样的需求:需要一个特殊的客户端,并且只有类似管理员这样的高级用户才能登录这个特殊的客户端,通过该客户端,管理员可以监控到其它普通客户端发送的自定义信息。我们称这个特殊的客户端为监控端。

      在有了上面介绍的原理的基础上,实现监控端也相当容易。当服务端在处理ICustomizeController的InformationReceived事件时,将需要被监控的信息转发给在线的监控端就可以了。服务端转发信息给监控端仍然可以使用ICustomizeController的Send方法:

            ///<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);

(1)Send方法的userID参数传入登录监控端的用户的ID。

(2)将监控到的要转发的信息(InformationReceived事件的参数,即上面提到的被封装成Information对象)通过序列化后,作为一个新的自定义信息,分别新的将信息类型和序列化结果传入Send方法的第二和第三个参数。

(3)在监控端处理服务器转发过来的被监控到的信息(可反序列化为Information对象)以实现监控的业务逻辑即可。

 

四.更多说明

     请注意,服务端通过预定ICustomizeController接口的InformationReceived事件,只能监控到客户端由ICustomizeOutter接口发出的自定义信息,而不能监控到其它命名空间下的Outter接口发送的消息(注意这里的用词是消息,“自定义信息”特用于ESPlus.Application.CustomizeInfo空间),比如客户端通过IBasicOutter接口发出的消息,在服务端是不会触发InformationReceived事件的,它们属于不同的命名空间,是相互独立的。

 

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

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

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

电话:027-87638960

Q Q:372841921

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值