写了这么多篇介绍ESFramework的文章才想起来还有一些很基础的内容没有介绍,前面介绍的一些组件、框架基本上是与协议无关的(比如无论是Tcp还是Udp甚至是Remoting、WebService都可以通用),然而到了应用的最底层,我们总需要选择一种通信协议,.net Framework对Remoting和WebService已经封装的足够好了,而对Tcp和Udp提供的API还是很低级,所以ESFramework对这两种协议进行了高层的封装,而且这些封装与ESFramework的其它组件协调一致,合作起来天衣无缝!
本文就先从封装的Tcp组件开始介绍(Tcp组件的前身可以参见.NET平台下可复用的Tcp通信层实现 和 .NET平台下可复用的Tcp通信层实现(续) )。
支持ESFramework的Tcp组件都需要实现ITcp接口:
上面接口定义中的注释已经解释了大多数内容,额外地,我需要解释一下几件事情:
(1)为什么ITcp没有从一个更基础的接口比如INet继承,这样ITcp和IEsbUdp(后面将要介绍的Udp组件的基础接口)就有了共同的根源?
早在EnterpriseServerBase中确实是这样设计的,但是在ESFramework中却将它们严格的隔离开来,原因在于,Tcp与Udp是如此的不同,如果要它们相互迁就共同遵守同一个“有意义”的基础接口并不是一件愉快的事情。这里说的“有意义”,指的是,我们可以通过这个接口来几乎完整的操纵不同的Tcp组件和Udp组件,而不需要进行向下转换。我们曾在一个早期的应用中,使之即支持Tcp协议,有支持Udp协议,只需简单修改一下配置文件,就可以简单的从一个协议切换到另一个。我们实现了这个目标,但是程序的实现中掺杂了太多的if...else和向下转换,有人一定会建议,使用多态可以避免if...else,让我告诉你这样做的难处在哪里。如果使用多态替换if...else,就需要将更多的东西抽象到形式一致的接口中,但是,Tcp和Udp组件的很多方法的签名是无法达成一致的,除非都使用Object类型,可是,如果都使用Object类型的参数,在方法的实现中,仍然需要向下进行转换。可见这样做并没有任何好处。首先是使得逻辑更加复杂含混,而且对效率也没有任何帮助。
所以太不相同的事物,就没有必要给它们安排一个共同的祖先。也许,我们可以给予一个没有多大意义的基础接口,比如像
(2)通过MaxMessageSize属性可以设置应用中所允许的单个消息的最大长度,如果从某连接上接收到的消息的长度大于此值,则Tcp组件会关闭对应的连接。如果对消息长度没有限制,则可以设置为int.MaxValue。
(3)Dispatcher属性是为Tcp组件设置消息分配器,每当Tcp组件接收到一个完整的消息,就会把它交给分配器进行分派处理。消息分配器是ESFramework的核心组件,前面已经做了详细介绍。
(4)BufferPool是缓冲区池,如果tcp发现即将接收的消息的长度大于RecieveBuffSize属性给定的值,则会从BufferPool申请更大的缓冲区来接收消息,为了避免大缓冲区的重复创建/销毁的开销,并增加复用,所以使用BufferPool来管理较大的缓冲区。
(5)ServiceCommitted事件,当服务器处理完一个用户请求并把回复发送出去后,将触发该事件。如果ServiceCommitted事件被引发,表示回复数据已经被发送出去了。
(6)我们看到了ITcp从ITcpClientsController继承,ITcpClientsController接口用于服务器主动控制TCP客户的连接,比如,在消息分配器组件中可能需要控制ITcp组件来直接向某个用户发送Active数据,这时只需要引用ITcpClientsController就可以了。
关于ITcp的介绍就是这些,然而如何实现ITcp却有多种方法,不同的实现方法所得到的并发量和效率可能千差万别。所以ITcp的实现对与系统的效率和并发量有着非常关键的影响。ESFramework中ITcp的参考实现是AgileTcp(将在后文中讲述),如果你有更好的实现,完全可以在采用ESFramework框架的同时使用自己实现的Tcp组件,ESFramework为你保留了这种权利--ESFramework所有的重要组件都是可以替换的,只要遵循相同的接口即可:)
感谢关注!
转到 :ESFramework 可复用的通信框架(序)
本文就先从封装的Tcp组件开始介绍(Tcp组件的前身可以参见.NET平台下可复用的Tcp通信层实现 和 .NET平台下可复用的Tcp通信层实现(续) )。
支持ESFramework的Tcp组件都需要实现ITcp接口:
1
public
interface
ITcp : ITcpClientsController ,IDisposable
2 {
3 void Initialize() ;
4 void Start() ;
5 void Stop() ; // 释放所有连接
6
7 int Port{ get ; set ;}
8 int ConnectionCount{ get ;} // 当前连接的数量
9 int RecieveBuffSize{ get ; set ;}
10 int MaxMessageSize{ set ;} // 当发现的消息长度大于MaxMessageSize,将关闭对应的连接
11
12 ITcpStreamDispatcher Dispatcher{ set ;} // 支持依赖注入
13 IContractHelper ContractHelper{ set ;}
14 IBufferPool BufferPool{ set ;}
15 IEsbLogger EsbLogger{ set ;} // 记录运行日志
16
17 event CbSimpleInt SomeOneConnected ; // 上线 ,ConnectID
18 event CbSimpleInt ConnectionCountChanged ; // 在线人数变化
19
20 event CallBackDisconnect SomeOneDisConnected ; // 掉线 ,ConnectID
21 event CallBackRespond ServiceCommitted ; // 用户请求的服务的回复信息
22 event CallBackRespond ServiceDirectCommitted ; // 对应ITcpClientsController.SendData ,此时无法确定ServiceKey
23 }
24
25 public delegate void CallBackRespond( int connectID ,NetMessage msg) ;
26 public delegate void CallBackDisconnect( int connectID ,DisconnectedCause cause) ;
2 {
3 void Initialize() ;
4 void Start() ;
5 void Stop() ; // 释放所有连接
6
7 int Port{ get ; set ;}
8 int ConnectionCount{ get ;} // 当前连接的数量
9 int RecieveBuffSize{ get ; set ;}
10 int MaxMessageSize{ set ;} // 当发现的消息长度大于MaxMessageSize,将关闭对应的连接
11
12 ITcpStreamDispatcher Dispatcher{ set ;} // 支持依赖注入
13 IContractHelper ContractHelper{ set ;}
14 IBufferPool BufferPool{ set ;}
15 IEsbLogger EsbLogger{ set ;} // 记录运行日志
16
17 event CbSimpleInt SomeOneConnected ; // 上线 ,ConnectID
18 event CbSimpleInt ConnectionCountChanged ; // 在线人数变化
19
20 event CallBackDisconnect SomeOneDisConnected ; // 掉线 ,ConnectID
21 event CallBackRespond ServiceCommitted ; // 用户请求的服务的回复信息
22 event CallBackRespond ServiceDirectCommitted ; // 对应ITcpClientsController.SendData ,此时无法确定ServiceKey
23 }
24
25 public delegate void CallBackRespond( int connectID ,NetMessage msg) ;
26 public delegate void CallBackDisconnect( int connectID ,DisconnectedCause cause) ;
上面接口定义中的注释已经解释了大多数内容,额外地,我需要解释一下几件事情:
(1)为什么ITcp没有从一个更基础的接口比如INet继承,这样ITcp和IEsbUdp(后面将要介绍的Udp组件的基础接口)就有了共同的根源?
早在EnterpriseServerBase中确实是这样设计的,但是在ESFramework中却将它们严格的隔离开来,原因在于,Tcp与Udp是如此的不同,如果要它们相互迁就共同遵守同一个“有意义”的基础接口并不是一件愉快的事情。这里说的“有意义”,指的是,我们可以通过这个接口来几乎完整的操纵不同的Tcp组件和Udp组件,而不需要进行向下转换。我们曾在一个早期的应用中,使之即支持Tcp协议,有支持Udp协议,只需简单修改一下配置文件,就可以简单的从一个协议切换到另一个。我们实现了这个目标,但是程序的实现中掺杂了太多的if...else和向下转换,有人一定会建议,使用多态可以避免if...else,让我告诉你这样做的难处在哪里。如果使用多态替换if...else,就需要将更多的东西抽象到形式一致的接口中,但是,Tcp和Udp组件的很多方法的签名是无法达成一致的,除非都使用Object类型,可是,如果都使用Object类型的参数,在方法的实现中,仍然需要向下进行转换。可见这样做并没有任何好处。首先是使得逻辑更加复杂含混,而且对效率也没有任何帮助。
所以太不相同的事物,就没有必要给它们安排一个共同的祖先。也许,我们可以给予一个没有多大意义的基础接口,比如像
public
interface
INet
{
void Initialize() ;
void Start() ;
void Stop() ;
int Port{ get ; set ;}
}
这没有多大的用处。如果有一天,ESFramework发现了可以从ITcp和IEsbUdp中抽象出一个有意义的基础接口的时候,会重构来得到这个接口,这也是很简单的。{
void Initialize() ;
void Start() ;
void Stop() ;
int Port{ get ; set ;}
}
(2)通过MaxMessageSize属性可以设置应用中所允许的单个消息的最大长度,如果从某连接上接收到的消息的长度大于此值,则Tcp组件会关闭对应的连接。如果对消息长度没有限制,则可以设置为int.MaxValue。
(3)Dispatcher属性是为Tcp组件设置消息分配器,每当Tcp组件接收到一个完整的消息,就会把它交给分配器进行分派处理。消息分配器是ESFramework的核心组件,前面已经做了详细介绍。
(4)BufferPool是缓冲区池,如果tcp发现即将接收的消息的长度大于RecieveBuffSize属性给定的值,则会从BufferPool申请更大的缓冲区来接收消息,为了避免大缓冲区的重复创建/销毁的开销,并增加复用,所以使用BufferPool来管理较大的缓冲区。
public
interface
IBufferPool
{
byte [] RentBuffer( int minSize) ;
void GivebackBuffer( byte [] buffer) ;
}
{
byte [] RentBuffer( int minSize) ;
void GivebackBuffer( byte [] buffer) ;
}
(5)ServiceCommitted事件,当服务器处理完一个用户请求并把回复发送出去后,将触发该事件。如果ServiceCommitted事件被引发,表示回复数据已经被发送出去了。
(6)我们看到了ITcp从ITcpClientsController继承,ITcpClientsController接口用于服务器主动控制TCP客户的连接,比如,在消息分配器组件中可能需要控制ITcp组件来直接向某个用户发送Active数据,这时只需要引用ITcpClientsController就可以了。
public
interface
ITcpClientsController
{
// 主动给某个客户同步发信息
void SendData( int ConnectID ,NetMessage msg) ;
// 主动关闭连接
void DisposeOneConnection( int connectID ,DisconnectedCause cause) ;
}
{
// 主动给某个客户同步发信息
void SendData( int ConnectID ,NetMessage msg) ;
// 主动关闭连接
void DisposeOneConnection( int connectID ,DisconnectedCause cause) ;
}
关于ITcp的介绍就是这些,然而如何实现ITcp却有多种方法,不同的实现方法所得到的并发量和效率可能千差万别。所以ITcp的实现对与系统的效率和并发量有着非常关键的影响。ESFramework中ITcp的参考实现是AgileTcp(将在后文中讲述),如果你有更好的实现,完全可以在采用ESFramework框架的同时使用自己实现的Tcp组件,ESFramework为你保留了这种权利--ESFramework所有的重要组件都是可以替换的,只要遵循相同的接口即可:)
感谢关注!
转到 :ESFramework 可复用的通信框架(序)