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

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

INetMessageHook 定义如下:
ESFramework介绍之(4)――消息拦截器INetMessageHook

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

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

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

EsbNetMessageHook
<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--> 1 public class EsbNetMessageHook:INetMessageHook
2 {
3 #region property
4 private IListhookList = new ArrayList();
5 public IListHookList
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 NetMessageCaptureRecievedMsg(NetMessagemsg)
32 {
33 if ( ! this .enabled)
34 {
35 return msg;
36 }
37
38 if (msg == null )
39 {
40 return null ;
41 }
42
43 NetMessageresult = msg;
44 for ( int i = 0 ;i < this .hookList.Count;i ++ )
45 {
46 INetMessageHookhook = (INetMessageHook) this .hookList[i];
47 result = hook.CaptureRecievedMsg(result);
48 }
49
50 return result;
51 }
52
53 public NetMessageCaptureBeforeSendMsg(NetMessagemsg)
54 {
55 if ( ! this .enabled)
56 {
57 return msg;
58 }
59
60 if (msg == null )
61 {
62 return null ;
63 }
64
65 NetMessageresult = msg;
66 for ( int i = this .hookList.Count - 1 ;i >= 0 ;i -- )
67 {
68 INetMessageHookhook = (INetMessageHook) this .hookList[i];
69 result = hook.CaptureBeforeSendMsg(result);
70 }
71
72 return result;
73 }
74
75 #endregion
76 }

需要特别注意的是, HookList 的实现中,逐个调用 Hook CaptureRecievedMsg 方法的顺序必须与逐个调用 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 是否对接收到的本条消息进行解压缩,是否在发送本消息之前进行压缩。
ESFramework介绍之(4)――消息拦截器INetMessageHook
BaseZipHook
实现如下:

BaseZipHook
<!--<br><br>Code highlighting produced by Actipro CodeHighlighter (freeware)<br>http://www.CodeHighlighter.com/<br><br>--> 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(NetMessagemsg);
10
11 #region INetMessageHook成员
12
13 #region CaptureRecievedMsg
14 public NetMessageCaptureRecievedMsg(NetMessagemsg)
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 NetMessageCaptureBeforeSendMsg(NetMessagemsg)
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 是一个抽象类,具体实现的压缩算法由派生类通过覆写 Zip Unzip 方法提供。如果要实现一个具体的 ZipHook ,你可以从 BaseZipHook 继承,并采用 ZipHelper 提供的压缩 / 解压缩功能。

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

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


上一篇: ESFramework介绍之(3)――消息处理器和处理器工厂

转到: ESFramework 可复用的通信框架(序)



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


更多文章、技术交流、商务合作、联系博主

微信扫码或搜索:z360901061

微信扫一扫加我为好友

QQ号联系: 360901061

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。

【本文对您有帮助就好】

您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描上面二维码支持博主2元、5元、10元、自定义金额等您想捐的金额吧,站长会非常 感谢您的哦!!!

发表我的评论
最新评论 总共0条评论