< type="text/javascript">
(来源:http://blog.csdn.net/hulihui
)
在程序设计与实际应用中,Socket数据包接收服务器够得上一个经典问题了:需要计算机与网络编程知识(主要是Socket),与业务处理逻辑密切
(如:包组成规则),同时还要兼顾系统运行的稳定、效率、安全与管理等。具体应用时,在满足业务处理逻辑要求的基础上,存在侧重点:有些需要考虑并发与效
率,有些需要强调稳定与可靠等等。虽然.NET 2.0
Framework上的IOCP(I/O完成端口)异步技术可以有效解决并发等问题,但完全的异步模式也缺乏一些控制上的灵活性,例如:Socket暂停
操作等。
本文介绍的是一个传统Socket数据包服务器解决方案,该方案改自笔者2005年底的一个交通部省级公路交通流量数据服务器中心
(DSC)项目。当时.NET Framework 2.0 与 Visual Studio 2005
发布没多久,笔者接触C#的时间不长。于是Google了国内国外网,希望找点应用C#解决Socket通信问题的思路和代码。最后,找到了两篇帮助最大
的文章:一篇是国人2005年3月写的Socket接收器框架——
在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)
(分
(一)、(二)两篇),该文应用了客户端Socket会话(Session)概念;另一篇是美国人写的,提出了多线程、分段接收数据包的技术方案,描述了
多线程、异步Socket的许多实现细节,该文坚定了笔者采用多线程和异步方式处理Socket接收器的技术路线。第一个版本EMTASS
1.0(EMTASS,Extensible Multi-Thread Asynchronous Socket
Server)于2006年初完成并投入使用。
今年暑假,笔者修改了原Socket接收服务器代码,即EMTASS 1.1。最近,又按框架的可扩展性、可重用性等要求重新构思和设计了EMTASS,即EMTASS 2.0。下面的介绍共分六个部分:
- 总体思路与架构
- 关键实现技术
- 框架使用简介
- 一般测试结果
- 总结与展望
- 版本与源码
1.1 总体思路
总体构思上,主要考虑多线程、异步Socket和可扩展性三个方面。
1) 三个核心线程
在Internet环境下的Socket应用中,客户端和网络容易出现异常,此时必须释放异常退出的Socket资源。考虑到服务器的高并
发能力,一般采取包接收和处理分开的策略:将接收到的包添加到包队列,然后处理队列中的数据包。当然,侦听远程客户端的连接请求可以用Socket的
AcceptAsync()异步方法(IOCP,I/O完成端口由此开始)。考虑到暂停、关闭同步操作,仍然用一个线程。这样,清理资源、处理数据包、侦
停客户连接请求就是组成了EMTASS架构的三个核心线程,它们由.NET线程池统一管理:
-
客户端连接侦听线程 StartServerListen():
循环侦听远程客户端的Socket连接请求。如果存在,通过适当规范性判断
后创建该Socket的客户端会话TSessionBase对象(实际上是该类的派生类对象),同时调用该会话对象的Socket异步数据接收方法
BeginReceive(),接收到的数据包存放在会话对象的包队列中。当然,新增的TSessionBase对象将添加到会话队列
m_sessionTable(一个Dictionary<>泛型对象)中,该队列表就是清理和处理线程的遍历对象;
-
数据包处理线程 CheckDatagramQueue():
循环检测TSessonBase队列中的会话对象,调用该对象的相关方法完成数据包解析、判断类型、数据存储等任务;
- 会话表检测线程 CheckSessionTable():
循环检查会话表m_sessionTable中的各个会话对象,分步骤清理已经超时、无效或异常的会话对象,清理会话对象的缓冲区,释放其Socket资源。
2) 异步处理模式
.NET
Framework中的Socket具有完整的异步处理能力:侦听后异步接收(AcceptAsync())、数据异步接收
(BeginReceive())、数据异步发送(BeginSend())等。EMTASS框架采取了异步接收和发送方式,并封装在
TSessionBase类中。在EMTASS的版本1.0、1.1中,这些方法在主类TSocketServerBase中实现,显然不符合类封装原
则。
3) 系统可扩展性
可扩展性主要考虑不同的业务处理逻辑和应用场景,即:数据包格式、数据存储方法、数据库服务器等。框架EMTASS的可扩展性体现在类的泛型与抽象设计、方法虚拟和保护等方面:
- 抽象类:
会话基类TSessionBase、数据库基类TDatabaseBase均是抽象类,分别提供了数据包分析与判断、数据存储的虚拟方法;
- 泛型类:
主要基类TSocketServerBase有TSessionBase、TDatabaseBase两个泛型约束参数,可以根据这两个抽象类的派生类产生具体的服务器类型;
- 方法抽象(abstract):
TSessionBase的数据包分析方法AnalyzeDatagram()、TDatabaseBase的数据库打开方法Open()均是抽象的,必须在派生类中根据业务处理逻辑和数据库类型重写;
- 方法保护(protected):
与事件处理有关的方法、与业务处理逻辑相关的方法全部是protected方法,可以根据实际情况重写。
1.2 类架构
1)主要类层次结构
(图1 主要类层次关系)
按应用类别分,EMTASS主要有四组类:Socket服务器类、Session会话类、Database数据库类和枚举类型。
- Socket服务器类
- 服务器泛型类 TSocketServerBase:
该类包括了一个服务器Socket对象、一个TDatabaseBase派生
类对象、一个会话TSessionBase类派生对象的列表(Dictionary<>泛型对象),封装了TDatabaseBase、
TSessionBase的全部事件,提供统一的对外公开接口和事件;
- 泛型参数:
服务器基类TSocketServerBase有两个泛型参数:TSessionBase类和TDatabaseBase类,定制它们的派生类后可以确定泛型类的具体版本。
- 客户端会话类
- 会话核心成员类 TSessionCoreInfo:
是TSessionBase的基类,包括会话的核心字段:登录时间、最近会话时间、IP地址、客户端名、对象状态等,是会话列表清单和事件参数的基类之一。该类的成员字段全部是protected的,需要在派生类中赋予具体值;
- 抽象会话类 TSessionBase:
封装了客户端Socket、数据接收缓冲区、数据包缓冲区、数据包队列等数据结构,包括了与客户端通信相关的全部方法:数据接收与发送、数据包处理等。
- 数据库类
- 抽象数据库基类 TDatabaseBase:
封装了数据库打开与关闭、异常事件处理等方法,其中数据连接属性DbConnection是虚属性、数据库打开方法Open()是抽象方法,需要在派生类中重写;
- 基类TSqlServerBase:
派生自TDatabaseBase,应用System.Data.SqlClient名称空间中SqlServer相关类型重定义了数据库连接属性DbConnection、重写了Open()方法;
- 基类TOleDbDatabaseBase:
派生自TDatabaseBase,应用System.Data.OleDb名称空间中OleDb数据访问相关类型重定义了基类的连接属性DbConnection、重写了Open()方法
- 枚举类型
- 会话状态类型 TSessioinState:
取4个值:Valid、Invalid、Shutdown和Closed。为Valid时表示会话是有效的,为Invalid时表示会话将被清理,为Shutdown时表示会话Socket正在卸载,为Closed时表示会话已经关闭、资源已经清理;
- 会话断开类型 TDisconnectType:
取3个值:Normal、Timeout和Exception,分别表示正常连接、超时断开、异常断开。其中,超时表示最近两次会话接收数据的时间超过约定的时限,防止某些会话长时间占有资源。
2)事件参数类型结构图
(图2 事件参数类层次关系)
EMTASS框架的事件包括三类:第一,普通事件,如:服务器启动与停止;第二,异常事件,接收与发送数据异常、数据库连接或数据存储异常
等;第三,与会话相关事件,如:增加会话对象、接收到一个合法数据包等。异常与会话结合即是会话异常事件。通过泛型委托EventHandler可以定义类事件,其中的事件参数类型如下:
- 异常事件参数类 TExceptionEventArgs:
封装了异常Exception对象的Message值;
- 会话事件参数类 TSessionEventArgs:
封装了一个TSessionCoreInfo对象;
- 会话异常参数类 TSessionExceptionEventArgs:
派生自TSessionEventArgs,包括异常消息字段Message。