本来Tcp/udp组件是系统与外界交换消息的唯一进出口,而Tcp组件或Udp组件与我们系统唯一的联系是通过消息分派器,如此一来,就相当于ESFramework规定了消息分派器是我们应用与外界交换消息的进出口。这样,才能保证接收到的每个消息和发送出去的每个消息都能被所有的Hook截获。另外,消息分派器需要验证接收到的每个消息格式是否正确、消息是否合法、消息是否符合特定规格等。下面是消息分派器组件和其它各个消息组件之间的联系图:

消息分派器ITcpStreamDispatcher定义非常简单:

1 public interface ITcpStreamDispatcher
2 {
3 /// <summary>
4 /// DealRequestData 不允许抛出异常
5 /// </summary>
6 NetMessage DealRequestData(RoundedMessage reqMsg ,ref bool closeConnection) ;
7
8 event CbackRequestRecieved RequestRecieved ;
9 }
DealRequestData方法的第一个参数是RoundedMessage类型,这个类从NetMessage继承,如下:
1 public class RoundedMessage :NetMessage
2 {
3 public RoundedMessage(){}
4
5 public RoundedMessage(NetMessage netMsg)
6 {
7 this.Header = netMsg.Header ;
8 this.Body = netMsg.Body ;
9 this.Tag = netMsg.Tag ;
10
11 this.BodyOffset = netMsg.BodyOffset;
12 }
13
14 public bool IsFirstMessage = false ;
15 public int ConnectID = 0 ; //请求所对应的Tcp连接
16 }
相对于NetMessage,RoundedMessage包含了更多的信息,ConnectID字段标志了这个消息来自于哪个Tcp连接,分派器有时候需要使用这个标志。
DealRequestData方法的第二个参数closeConnection表明消息分派器可以控制是否关闭对应的Tcp连接,因为可能存在这样情况,收到了某个消息,当消息分派器在分派这个消息的时候发现这个消息是非法的、或恶意的,则分派器可以通过通知closeConnection参数通知Tcp组件关闭掉对应的连接。
经常出现这样的情况,消息处理器自己需要直接将消息发送给客户,这就要求了IHookSender的存在:
public interface IHookSender
{
void HookAndSendNetMessage(int connectID ,NetMessage msg) ;
}
通常,消息分派器既要实现ITcpStreamDispatcher接口,又要实现IHookSender接口,因为IHookSender接口在消息分派器中实现是最佳的位置。
接下来,我们可以看看消息分派器的实现,消息分派器的实现需要引入IContractHelper接口。我们知道,框架是一个“骨架”,而骨架中的细节需要靠具体的应用来填充,IContractHelper就是用来填充这些细节的一个,它提供了关于应用中定义的协议的相关信息,比如,消息头的长度、消息头的标志等等。

其定义如下所示:
1 public interface IContractHelper :IStringEncoder
2 {
3 int MessageHeaderLength{get ;} //消息头的长度
4
5 IMessageHeader ParseMessageHeader(byte[] data ,int offset) ;
6
7 int ServerType{get ;}//当前服务器的服务器类型(如 cityCode)
8
9 //验证消息头标志
10 bool ValidateMessageToken(IMessageHeader header) ;
11
12 NetMessage GetResponseByServiceResultType(NetMessage reqMsg ,int serviceResultType) ;//根据服务失败类型获ServiceResultType取失败回复消息
13
14 ServiceType GetServiceType(int serviceKey) ;
15
16 //验证消息(在CaptureRecievedMsg之后进行),如果失败,将关闭对应的连接
17 bool VerifyFirstMessage(NetMessage msg) ;
18 bool VerifyOtherMessage(NetMessage msg ) ;
19 }
20
21 #region IStringEncoder
22 /// <summary>
23 /// StringEncoder 限定字符串编码格式
24 /// </summary>
25 public interface IStringEncoder
26 {
27 string GetStrFromStream(byte[] stream ,int offset ,int len) ;
28 byte[] GetBytesFromStr(string ss) ;
29 }
30 #endregion
万事具备,消息分派器的实现就水到渠成了。


TcpStreamDispatcher
1 public class TcpStreamDispatcher :ITcpStreamDispatcher ,IHookSender
2 {
3 private IDataDealerFactory requestDealerFactory = null ; //必须设置
4 private IContractHelper contractHelper = null ; //必须设置
5 private ITcpClientsController tcpClientsController = null ; //必须设置
6 private INetMessageHook netMessageHook = new EsbNetMessageHook() ;
7
8 public TcpStreamDispatcher()
9 {
10
11 }
12
13 #region property
14 #region Logger
15 private IEsbLogger logger = new EmptyEsbLogger() ;
16 public IEsbLogger Logger
17 {
18 set
19 {
20 if(value != null)
21 {
22 this.logger = value ;
23 }
24 }
25 }
26 #endregion
27
28 public IDataDealerFactory RequestDealerFactory
29 {
30 set
31 {
32 this.requestDealerFactory = value ;
33 }
34 }
35
36 public INetMessageHook NetMessageHook
37 {
38 set
39 {
40 this.netMessageHook = value ;
41 }
42 }
43
44 public IContractHelper ContractHelper
45 {
46 set
47 {
48 this.contractHelper = value ;
49 }
50 }
51
52 public ITcpClientsController TcpClientsController
53 {
54 set
55 {
56 this.tcpClientsController = value ;
57 }
58 }
59
60 #endregion
61
62 #region ITcpStreamDispatcher 成员
63
64 public void HookAndSendNetMessage(int connectID ,NetMessage msg)
65 {
66 if(msg == null)
67 {
68 return ;
69 }
70
71 this.tcpClientsController.SendData(connectID ,this.netMessageHook.CaptureBeforeSendMsg(msg)) ;
72 }
73
74 public NetMessage DealRequestData(RoundedMessage reqMsg, ref bool closeConnection)
75 {
76 if(this.RequestRecieved != null)
77 {
78 this.RequestRecieved(reqMsg) ;
79 }
80
81 closeConnection = false ;
82
83 try
84 {
85 #region 验证消息
86 if(reqMsg.IsFirstMessage)
87 {
88 if(! this.contractHelper.VerifyFirstMessage(reqMsg))
89 {
90 closeConnection = true ;
91 return null ;
92 }
93 }
94 else
95 {
96 if(! this.contractHelper.VerifyOtherMessage(reqMsg))
97 {
98 closeConnection = true ;
99 return null ;
100 }
101 }
102 #endregion
103
104 NetMessage msgHooked = this.netMessageHook.CaptureRecievedMsg(reqMsg) ;
105
106 IDataDealer dealer = this.requestDealerFactory.CreateDealer(msgHooked.Header.ServiceKey ,msgHooked.Header.TypeKey) ;
107 if(dealer == null)
108 {
109 return this.contractHelper.GetResponseByServiceResultType(msgHooked ,ServiceResultType.ServiceIsNotExist) ;
110 }
111
112 NetMessage resMsg = dealer.DealRequestMessage(msgHooked) ;
113 return this.netMessageHook.CaptureBeforeSendMsg(resMsg) ;
114 }
115 catch(Exception ee)
116 {
117 this.logger.Log(ee.Message ,"TcpStreamDispatcher.DealRequestMessage" ,ErrorLevel.High) ;
118 return this.contractHelper.GetResponseByServiceResultType(reqMsg ,ServiceResultType.HandleFailure) ;
119 }
120 }
121
122 public event ESFramework.Network.Tcp.CbackRequestRecieved RequestRecieved;
123
124 #endregion
125 }
到现在位置,消息相关的部分大致就这么多,还有一些微小的细节后面会补充进来。读者可能会问,还没有讲到Tcp组件或Udp组件的实现了,关于Tcp组件的介绍,可以参考这篇文章:.NET平台下可复用的Tcp通信层实现
接下来的后续文章将讲述基于C/S的4层架构经验。感谢关注!
上一篇:ESFramework介绍之(4)――消息拦截器INetMessageHook
转到 :ESFramework 可复用的通信框架(序)