• 【转载】消息拦截器INetMessageHook


    ESFramework介绍之(4)――消息拦截器INetMessageHook

        网络上传输的消息经常是经过加密和压缩,有的特定类型的消息可能还需要进行其它变形,ESFramework通过INetMessageHook对这些功能提供支持。需要说明的是,ESFramework对消息进行截获(Hook)处理有两种方式,一是仅仅Hook处理消息主体(Body),而不对消息头作任何变换;另一种方式是对整个消息(包括消息头和主体)都进行Hook处理。通常,第一种方式已经能够满足我们的大多数应用,并且效率也更高,如果应用有更特殊的要求,可以采用第二种方式。本文先介绍第一种方式,后面的文章中会对第二种给予讲解。

    INetMessageHook定义如下:
       

    1 public interface INetMessageHook
    2     {
    3         //转换消息
    4         NetMessage CaptureRecievedMsg(NetMessage msg) ;//在交给处理器之前
    5         NetMessage CaptureBeforeSendMsg(NetMessage msg) ;//在发送出去之前    
    6     
    7         bool Enabled{get ;set ;} //是否启用本Hook
    8     }

        通过INetMessageHook接口可以看到,当消息进入系统时回被CaptureRecievedMsg方法截获(比如,进行解密处理),当把消息发送到网络之前,将被CaptureBeforeSendMsg方法截获(比如,进行加密处理)。

        如果有多个INetMessageHook,则可以形成一个HookList,消息经过HookList时,分别按顺序被每个INetMessageHook处理,ESFramework中的HookList的参考实现是EsbNetMessageHook,并且它也实现了INetMessageHook接口,这样就可以把一个Hook链当作一个Hook了。
       
    其源码如下:


     1 public class EsbNetMessageHook : INetMessageHook
     2     {
     3         #region property
     4         private IList hookList = new ArrayList() ;
     5         public IList HookList
     6         {
     7             set
     8             {
     9                 this.hookList = value ;
    10             }
    11         }
    12
    13         #region Enabled
    14         private bool enabled = true ;
    15         public bool Enabled
    16         {
    17             get
    18             {
    19                 return this.enabled ;
    20             }
    21             set
    22             {
    23                 this.enabled = value ;
    24             }
    25         }
    26         #endregion
    27
    28         #endregion
    29
    30         #region INetMessageHook 成员
    31         public NetMessage CaptureRecievedMsg(NetMessage msg)
    32         {   
    33             if(! this.enabled)
    34             {
    35                 return msg ;
    36             }
    37    
    38             if(msg == null)
    39             {
    40                 return null ;
    41             }
    42
    43             NetMessage result = msg ;
    44             for(int i=0 ;i<this.hookList.Count ;i++)
    45             {
    46                 INetMessageHook hook = (INetMessageHook)this.hookList[i] ;
    47                 result = hook.CaptureRecievedMsg(result) ;
    48             }
    49
    50             return result ;
    51         }
    52
    53         public NetMessage CaptureBeforeSendMsg(NetMessage msg)
    54         {
    55             if(! this.enabled)
    56             {
    57                 return msg ;
    58             }
    59
    60             if(msg == null)
    61             {
    62                 return null ;
    63             }
    64
    65             NetMessage result = msg ;
    66             for(int i=this.hookList.Count-1 ;i>=0 ;i-- )
    67             {
    68                 INetMessageHook hook = (INetMessageHook)this.hookList[i] ;
    69                 result = hook.CaptureBeforeSendMsg(result) ;
    70             }
    71
    72             return result ;
    73         }       
    74
    75         #endregion
    76     }

        需要特别注意的是,HookList的实现中,逐个调用HookCaptureRecievedMsg方法的顺序必须与逐个调用CaptureBeforeSendMsg方法的顺序相反,这就是为什么EsbNetMessageHook实现CaptureBeforeSendMsg方法时,出现下列代码的原因: 

    for(int i=this.hookList.Count-1 ;i>=0 ;i-- )

        为了说明为什么需要颠倒Hook链的情况,可以举个例子,假设有一个“原始消息”从系统1发送到系统2,其间经过了两个Hook,一个加密Hook,一个是压缩Hook,则可表示如下:
    系统1=》原始消息=》加密=》压缩=》发送
    系统2=》接收消息=》解压缩=》解密=》原始消息 

        还要提醒的是,在具体的Hook实现中,截获处理经常改变Body的大小,如果Body的大小真的发生了变化,一定要更新消息头(IMessageHeader)中的MessageBodyLength字段。
        
        通常,采用Hook时,服务端与客户端是对称的,所以你可以把所有的Hook实现放在一个Dll中,这样服务端和客户端可以共同使用这个Hook Dll了。

        由于对网络消息进行压缩是常见的需求,所以我把
    BaseZipHook纳入到了ESFramework中,并且,这也是IMessageHeader存在ZipMe属性的原因。ZipMe属性用于通知BaseZipHook是否对接收到的本条消息进行解压缩,是否在发送本消息之前进行压缩。
       
    BaseZipHook
    实现如下:

    BaseZipHook
      1 public abstract class BaseZipHook :INetMessageHook
      2     {
      3         public BaseZipHook()
      4         {           
      5         }
      6
      7         protected abstract byte[] Zip(byte[] data ,int offset ,int size) ;
      8         protected abstract byte[] Unzip(byte[] data ,int offset ,int size) ;
      9         protected abstract bool   IsP2PMessage(NetMessage msg) ;
     10
     11         #region INetMessageHook 成员
     12
     13         #region CaptureRecievedMsg
     14         public NetMessage CaptureRecievedMsg(NetMessage msg)
     15         {
     16             if(!this.enabled)
     17             {
     18                 return msg ;
     19             }
     20
     21             if(! msg.Header.ZipMe)
     22             {
     23                 return msg ;
     24             }
     25
     26             if(this.IsP2PMessage(msg))
     27             {
     28                 if(! this.zipP2pMessage)
     29                 {
     30                     return msg ;
     31                 }
     32             }
     33
     34             if(msg.Body == null)
     35             {
     36                 return msg ;
     37             }
     38
     39             msg.Body = this.Unzip(msg.Body ,0 ,msg.Header.MessageBodyLength) ;
     40             msg.Header.MessageBodyLength = msg.Body.Length ;
     41
     42             return msg ;
     43         }
     44         #endregion
     45
     46         #region CaptureBeforeSendMsg
     47         public NetMessage CaptureBeforeSendMsg(NetMessage msg)
     48         {
     49             if(!this.enabled)
     50             {
     51                 return msg ;
     52             }
     53
     54             if(! msg.Header.ZipMe)
     55             {
     56                 return msg ;
     57             }
     58
     59             if(this.IsP2PMessage(msg))
     60             {
     61                 if(! this.zipP2pMessage)
     62                 {
     63                     return msg ;
     64                 }
     65             }
     66
     67             if(msg.Body == null)
     68             {
     69                 return msg ;
     70             }
     71
     72             msg.Body = this.Zip(msg.Body ,0 ,msg.Header.MessageBodyLength) ;
     73             msg.Header.MessageBodyLength = msg.Body.Length ;
     74
     75             return msg ;
     76         }
     77         #endregion
     78
     79         #region Enabled
     80         private bool enabled = true ;
     81         public bool Enabled
     82         {
     83             get
     84             {
     85                 return this.enabled ;
     86             }
     87             set
     88             {
     89                 this.enabled = value ;
     90             }
     91         }
     92         #endregion
     93
     94         #region ZipP2pMessage 通常服务端设置为false ,而客户端设置为true
     95         private bool zipP2pMessage = true ;
     96         public bool ZipP2pMessage
     97         {
     98             get
     99             {
    100                 return this.zipP2pMessage ;
    101             }
    102             set
    103             {
    104                 this.zipP2pMessage = value ;
    105             }
    106         }
    107         #endregion
    108
    109         #endregion
    110     }

        BaseZipHook是一个抽象类,具体实现的压缩算法由派生类通过覆写ZipUnzip方法提供。如果要实现一个具体的ZipHook,你可以从BaseZipHook继承,并采用ZipHelper提供的压缩/解压缩功能。 

        到现在为止,我们已经讨论了关于消息的比较多的内容了,但还有一个非常重要的组件没有讲到,那就是消息分派器ITcpStreamDispatcher,消息分派器直接与Tcp组件或Udp组件联系,并且所有消息的进出都要经过ITcpStreamDispatcher,所以分派器是一个对消息进行Hook的理想位置,并且消息分派器会把具体的消息分派到对应的消息处理器上。欲知道ITcpStreamDispatcher的原理与实现,请继续关注下文:

    ESFramework介绍之(5――消息分派器ITcpStreamDispatcher

  • 相关阅读:
    fopen C++
    Jungle Roads(最小生成树+并查集)
    Magic Number(dp)
    error: macro names must be identifiers
    <errors>'MessageBoxA' : function does not take 1 parameter
    归并排序模板
    MFC程序出现“Debug Assertion Failed! File:afx.inl Line:177”错误
    构造函数初始化列表
    (Codeforces Round #136 (Div. 2))A. Little Elephant and Function(递归分析,简单)
    Argus(ZOJ Problem Set 2212)(优先队列)
  • 原文地址:https://www.cnblogs.com/fx2008/p/2263123.html
Copyright © 2020-2023  润新知