0 前言

>>[前言][第1节][第2节][第3节][第4节][第5节][第6节]

在程序设计与实际应用中,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. 总体思路与架构
  2. 关键实现技术
  3. 框架使用简介
  4. 一般测试结果
  5. 总结与展望
  6. 版本与源码

1 总体思路与架构

>>[前言][第1节][第2节][第3节][第4节][第5节][第6节]

1.1 总体思路

总体构思上,主要考虑多线程、异步Socket和可扩展性三个方面。

1) 三个核心线程

在Internet环境下的Socket应用中,客户端和网络容易出现异常,此时必须释放异常退出的Socket资源。考虑到服务器的高并发能力,一般采取包接收和处理分开的策略:将接收到的包添加到包队列,然后处理队列中的数据包。当然,侦听远程客户端的连接请求可以用Socket的AcceptAsync()异步方法(IOCP,I/O完成端口由此开始)。考虑到暂停、关闭同步操作,仍然用一个线程。这样,清理资源、处理数据包、侦停客户连接请求就是组成了EMTASS架构的三个核心线程,它们由.NET线程池统一管理:

  1. 客户端连接侦听线程 StartServerListen():循环侦听远程客户端的Socket连接请求。如果存在,通过适当规范性判断后创建该Socket的客户端会话TSessionBase对象(实际上是该类的派生类对象),同时调用该会话对象的Socket异步数据接收方法BeginReceive(),接收到的数据包存放在会话对象的包队列中。当然,新增的TSessionBase对象将添加到会话队列m_sessionTable(一个Dictionary<>泛型对象)中,该队列表就是清理和处理线程的遍历对象;
  2. 数据包处理线程 CheckDatagramQueue():循环检测TSessonBase队列中的会话对象,调用该对象的相关方法完成数据包解析、判断类型、数据存储等任务;
  3. 会话表检测线程 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。

2 关键实现技术

>>[前言][第1节][第2节][第3节][第4节][第5节][第6节]

下面介绍主要类TSocketServerBase和辅助类TSessionBase、TDatabaseBase中的主要实现方法。

2.1 TSocketServerBase类

该类包括了全部的对外接口和事件,主要实现前面介绍过的三个线程。

1) 线程池与同步信号

.NET 提供了线程池方法 ThreadPool.QueueUserWorkItem() 自动将委托对象添加到系统线程池,见如下的实现代码:

    if (!ThreadPool.QueueUserWorkItem(this.StartServerListen)) return false;
if (!ThreadPool.QueueUserWorkItem(this.CheckDatagramQueue)) return false;
if (!ThreadPool.QueueUserWorkItem(this.CheckSessionTable)) return false;

其中, 客户端连接请求侦听方法StartServerListen()、数据包队列检查方法CheckDatagramQueue()和会话表检查方法CheckSessionTable()均使用循环处理方式,循环条件是m_serverClosed为false。只有该类的Close()方法可以中断这三个线程。在Close()方法中设置m_serverClosed为true终止线程的同时,还需要考虑线程退出的同步问题,此时使用手工事件信号对象ManualResetEvent。参考如下数据包队列检查线程方法的代码:

private void CheckDatagramQueue(object state)
{
m_checkDatagramQueueResetEvent.Reset(); while (!m_serverClosed)
{
lock (m_sessionDictionary)
{
// ...其它代码
}
} m_checkDatagramQueueResetEvent.Set();
}

上述代码是不安全的,一般要需要try{}finally{}保证事件信号对象Reset()与Set()匹配。但EMTASS中的三个线程方法均有自己的异常处理方式,不会抛出异常。下面是关闭服务器方法Close()的主要代码。在设置变量了m_serverCloed为true后,使用了三个事件信号等待,同步三个线程的正常终止。

private void Close()
{
if (m_serverClosed)
{
return;
} m_serverClosed = true;
m_serverListenPaused = true; m_checkServerListenResetEvent.WaitOne(); // 等待3个线程
m_checkSessionTableResetEvent.WaitOne();
m_checkDatagramQueueResetEvent.WaitOne(); // ...其它代码
}

2) 分步骤的清理线程

建立会话对象后,三种情况需要终止会话:1)关闭服务器;2)会话异常;3)会话超时。第1种情况将强制终止会话,第2、3种情况需要清理线程终止会话并释放其资源。为防止立即关闭Socket引发的异常,系统分3个步骤完成:1)标记该会话为Invalid,此时停止一切与该会话的处理操作;2)调用Shutdown()方法:Shutdown会话Socket,标记会话状态为Shutdown;3)调用Close()方法:清除会话缓冲区和数据包队列,释放Socket资源,从会话表中删除该对象。具体操作可以参考TSocketServerBase类中的CheckSessionTable()方法。

3) 事件传递与发布

EMTASS框架的所有事件,包括TSessionBase类和TDatabaseBase类的事件,都通过服务器类TSocketServerBase对外发布。在创建会话对象或数据库对象时,直接传递其事件给TSocketServerBase的相同委托事件,见如下代码举例:

    session.DatagramAccepted += new EventHandler(this.OnDatagramAccepted);
session.DatagramHandled += new EventHandler(this.OnDatagramHandled);

上述代码中,将TSessionBase派生类对象session的两个事件直接绑定(使用+=方法)到当前TSocketServerBase对象上。具体实现代码可以参考TSocketServerBase的初始化方法Initiate()和添加会话对象方法AddSession()。

2.2 TSessionBase类

该抽象类包括客户端Socket、数据接收缓冲区和数据包队列等成员,封装了所有与Socket通信的方法。该类还包括数据包处理方法:数据包解析保护方法ResolveSessionBuffer()和数据包分析虚拟方法AnalyzeDatagram()。

1) 会话缓冲区和数据包队列

TSessionBase包括两个数据接收缓冲区和一个数据包队列:

  • m_receiveBuffer缓冲区:接收客户端Socket数据的缓冲区,如果数据包比该缓冲区长,Socket将自动(异步)读取几次,每次用方法CopyToDatagramBuffer暂到数据包缓冲区m_datagramBuffer中;
  • m_datagramBuffer缓冲区:如果m_receiveBuffer接收了非完整的数据包,则使用该缓冲区暂存,直到获得一个完整数据包。一般情况下,设置m_receiveBuffer大于数据包长度,则一次可以接收一个完整包,此时该缓冲区为空。此外,该缓冲区大小根据数据包的长度动态增长;
  • m_datagramQueue包队列:字节数组的队列(Queue<>泛型),保存了当前会话的数据包(字节数组),等待处理线程分析与处理。在EMTASS 1.0与1.1中,该队列结构封装在TSocketServerBase内。这种设计增加了TSessionBase类与TSocketServerBase类的偶合程度。

2) 数据包解析方法ResolveSessionBuffer()

该方法是protected的,可以根据数据包结构与具体业务逻辑重写代码。在TSessionBase类中实现的包组成规则是:开始字符是<结束字符是>。特别指出,Socket通信中有两个必须考虑的著名问题:

  • 数据包界限问题:数据包字符串(字节数组)如何界限?在交通部的Socket通信协议中,用<>分别作为一个包的开始与结束字符;
  • 数据包间断与重叠问题:由于网络或设备故障,一个数据包可能分两次接收。另一种情况就是连续接收到多个数据包。第一种情况,需要先缓存接收到的包直到一个完整的数据包。第二种情况,则需要根据包界限符号分解一个个的包。

3) 数据包分析方法AnalyzeDatagram()

该抽象方法是TSessionBase类必须重写的方法,也是EMTASS框架扩展的主要接口,应该完成如下基本任务:

  • 判断数据包的有效性与包类型;
  • 分解包中的各字段数据;
  • 校验包及其数据有效性;
  • 发送确认消息给客户端(调用方法 SendDatagram());
  • 存储包数据到数据库中;
  • 如果数据包中存在客户端名称或编号,则填写m_name字段。

2.3 TDatabaseBase类

该抽象类定义了3个数据库异常处理事件:DatabaseOpenException、DatabaseCloseExeption和DatabaseException,以及4个public方法:Open()、Close()、Clear()和Store()。其中,Open()是抽象方法,在派生类中可以增加自己的代码(见demo的实现部分),Close()方法关闭数据库连接,Clear()方法在Close()中被调用——关闭数据库前清理相关资源,虚方法Store()用于数据存储。EMTASS框架给出了该基类的两个派生类:TSqlServerBase和TOleDatabaseBase,可以满足一般的数据库应用需求。

3 架构使用简介

>>[前言][第1节][第2节][第3节][第4节][第5节][第6节]

3.1 一般步骤

EMTASS框架的使用包括如下步骤:

  • 定制满足需求的TSqlServerBase或TOleDatabaseBase派生类
    • 增加成员字段,如:DbCommand、DbDataAdapter等;
    • 重写TDatabaseBase类的虚拟方法Store(),编写保存数据包到数据库中的实现代码;
    • 在TSessionBase的AnalyzeDatagram()方法中直接或间接调用Store()方法。
  • 定制满足业务处理逻辑的TSessionBase派生类
    • 重写ResolveSessionBuffer()方法,按照数据包规则提取缓冲区中的数据包文,并存储到包队列中;
    • 重写AnalyzeDatagram()方法,按前面的要求增加功能。
  • 定制满足需求的TSocketServerBase派生类
    • 定义TSocketSErverBase泛型类的派生类(该步可省略);
    • 创建泛型类的派生类对象,在构造函数中给出数据库连接字符串和TCP通信端口;
    • 设置泛型类的派生类对象的最大数据包长度等参数;
    • 实现泛型类的派生类对象的相关事件处理方法。

3.2 TSocketServerBase的构造、属性、方法和事件

泛型类TSocketServerBase提供了EMTASS框架的所有对外接口(属性、方法和事件),包括内联TSessionBase对象和TDatabaseBase对象的对外属性和事件。

1) TSocketServerBase构造函数

有两个重载版本,默认端口3130。考虑到可扩展性,必须给出数据库连接串,见如下代码:

public TSocketServerBase(string dbConnectionString)
{
this.Initiate(dbConnectionString);
} public TSocketServerBase(int tcpPort, string dbConnectionString)
{
m_servertPort = tcpPort;
this.Initiate(dbConnectionString);
}

构造函数中的方法Initiate()完成具体的初始化任务。

2) TSocketServerBase公共属性

  • ServerPort:服务器端口号,默认值为3130
  • Closed:服务器已经关闭
  • ListenPaused:服务器暂时停止客户端连接请求
  • LoopWaitTime:Socket.Listen方法中的等待时间(ms),默认值为25ms
  • MaxDatagramSize:允许数据包的最大长度,默认值为1024K
  • MaxListenQueueLength:最大侦听队列长度,默认值为16
  • MaxReceiveBufferSize:允许数据包接收缓冲区的最大长度,默认值为16K
  • MaxSameIPCount:允许同地址IP的会话Socket个数,默认值为64
  • MaxSessionTableLength:允许最大会话表长度,默认值为1024
  • MaxSessionTimeout:允许最大的会话超时间隔(s),默认值为120s
  • ErrorDatagramCount:错误数据包个数
  • ReceivedDatagramCount:接收数据包个数
  • ServerExceptionCount:服务器异常次数
  • SessionCount:当前会话个数
  • SessionExeptionCount:会话异常个数
  • SessionCoreInfoList:当前会话表信息清单

3) TSocketServerBase公共方法

  • Start():启动服务器
  • Stop():关闭服务器
  • PauseListen():暂停侦听连接请求
  • ResumeListen():恢复侦听连接请求
  • Dispose():关闭服务器并释放系统资源
  • CloseSession():关闭一个会话
  • CloseAllSessions():关闭全部会话
  • SendToSession():给一个会话发送消息
  • SendToAllSessions():给所有会话发送消息

4) TSocketServerBase事件

  • DatabaseCloseException:数据库关闭异常
  • DatabaseException:数据库异常
  • DatabaseOpenException:数据库打开异常
  • DatagramAccepted:接受了一个完整数据包
  • DatagramDelimiterError:数据包界限符错误
  • DatagramError:数据包错误
  • DatagramHandled:处理了一个数据包
  • DatagramOversizeError:数据包超长错误
  • ServerStarted:服务器启动后
  • ServerClosed:服务器关闭后
  • ServerListenPaused:服务器暂停连接请求后
  • ServerListenResumed:服务器恢复连接请求后
  • ServerException:服务器异常
  • SessionRejected:连接请求被拒绝
  • SessionConnected:建立一个会话连接
  • SessionDisConnected:断开一个会话连接
  • SessionReceiveException:会话接收数据异常
  • SessionSendException:会话发送数据异常
  • SessionTimeout:会话超时

3.3 下载包Demo介绍

下载包中包括EMTASS框源代码和Demo。其中,VS2005的Demo解决方案文件为EMTASS.sln,包含两个项目:服务器项目和客户端项目。/bin/文件夹下的编译文件可直接运行:先启动服务器,然后运行客户端。

1) 服务器端Demo

服务器端包括两个部分:第一,接收服务器窗体程序;第二,Access数据库。服务器端窗体程序包含如下实现:

  • TSessionBase派生类TTestSession:重写了OnDatagramDelimiterError()和OnDatagramOversizeError()事件处理方法,重写了数据包分析方法AnalyzeDatagram(),增加了一个自定义方法Store();
  • TDatabaseBase派生类TAccessDatabase:增加了一个OleDbCommmand字段m_command,重写了Open()方法和Store()方法。在重写的Open()方法中,创建了m_command对象及其参数对象,给出了数据库的Insert语句SQL代码。重写的Store()方法中,将TSessionBase的IP、SessionName和数据包长度保存到Access数据库中。方法Store()将被TTestSession()的AnalyzeDatagram()方法调用;
  • TSocketServerBase<>对象:给出数据库连接字符串后,应用前面定义的两个派生类创建泛型类对象,然后注册该对象的事件实现方法。注册的事件方法功能包括:显示服务器的如果计数情况,显示服务器运行状态。

服务器端的主要代码如下:

public partial class SocketServerDemo : Form
{
TSocketServerBase<ttestsession, ttestaccessdatabase=""> m_socketServer; public SocketServerDemo()
{
InitializeComponent();
} private void SocketServerDemo_Load(object sender, EventArgs e)
{
cb_maxDatagramSize.SelectedIndex = 1; // 数据库连接字符串
string connStr = "Provider=Microsoft.Jet.OLEDB.4.0; Data Source = DemoAccessDatabase.mdb;"; m_socketServer = new TSocketServerBase<ttestsession, ttestaccessdatabase="">(connStr); // 服务器对象
m_socketServer.MaxDatagramSize = 1024 * int.Parse(cb_maxDatagramSize.Text); // 包最大长度 this.AttachServerEvent(); // 附加服务器全部事件
} private void SocketServerDemo_FormClosing(object sender, FormClosingEventArgs e)
{
m_socketServer.Dispose(); // 关闭服务器进程
} private void AttachServerEvent()
{
m_socketServer.ServerStarted += this.SocketServer_Started;
m_socketServer.ServerClosed += this.SocketServer_Stoped;
m_socketServer.ServerListenPaused += this.SocketServer_Paused;
m_socketServer.ServerListenResumed += this.SocketServer_Resumed;
m_socketServer.ServerException += this.SocketServer_Exception; m_socketServer.SessionRejected += this.SocketServer_SessionRejected;
m_socketServer.SessionConnected += this.SocketServer_SessionConnected;
m_socketServer.SessionDisconnected += this.SocketServer_SessionDisconnected;
m_socketServer.SessionReceiveException += this.SocketServer_SessionReceiveException;
m_socketServer.SessionSendException += this.SocketServer_SessionSendException; m_socketServer.DatagramDelimiterError += this.SocketServer_DatagramDelimiterError;
m_socketServer.DatagramOversizeError += this.SocketServer_DatagramOversizeError;
m_socketServer.DatagramAccepted += this.SocketServer_DatagramReceived;
m_socketServer.DatagramError += this.SocketServer_DatagramrError;
m_socketServer.DatagramHandled += this.SocketServer_DatagramHandled; m_socketServer.DatabaseOpenException += this.SocketServer_DatabaseOpenException;
m_socketServer.DatabaseCloseExcpetion += this.SocketServer_DatabaseCloseException;
m_socketServer.DatabaseExcpetion += this.SocketServer_DatabaseException; m_socketServer.ShowDebugMessage += this.SocketServer_ShowDebugMessage;
}
//...其它代码
}

下面是服务器端Demo运行图片

2) 客户端Demo

创建一个TcpClient对象,模拟远程客户端与服务器通信。下面是客户端Demo运行图片:

4 一般测试结果

>>[前言][第1节][第2节][第3节][第4节][第5节][第6节]
  • 机器配置:双核CPU E2140,主频1.6G,RAM是1G(含显卡内存)。
  • 正确性测试
    • 测试方法:客户端的数据包含有长度串,在服务器端检测比较,以此判断数据包传输的正确性;
    • 测试情况:单机启动服务器,运行7个客户端程序,其中3个是干扰源——连续做连接/断开操作,另4个连续发送数据包。运行约80分钟检查,服务器平均每秒接收15个包,没有发生异常,也没有错误包,数据包队列稳定在3以下。
  • 速度测试
    • 测试方法:单机运行服务器,然后运行20个客户端程序,用10-50ms的速度发送数据包;
    • 测试情况:运行30分钟检查,服务器平均每秒接收60个包,没有出现数据包队列显著增长等情况。
  • 稳定性测试:客户端Demo中包含一个连接后马上断开的连续操作,在这种情况下服务器端没有发现异常。此外,笔者在30个客户端会话情况下运行服务器1个小时,没有发生服务器端异常,但有个别客户端异常退出。
  • 并发性测试:同时运行了30个客户端Demo,没有发生服务器异常等现象,数据包队列上限也不超过5。
  • 测试结论: 在一台机器上做测试, EMTASS 2.0接收与处理正确,每秒可以处理60以上的数据包,运行稳定可靠,有较好的并发处理能力。测试中发现的主要问题如下:
    • CPU占用率很大:显然是三个线程的循环操作所引起的,在EMTASS1.0、1.1中,使用Thread.Sleep(m_waitTime)等待一段时间。本框架的设计目的是专用Socket服务器,为提高吞吐能力省略了这个操作。如果必要,在以后版本中添加该功能;
    • 服务器启动后拒绝连接请求:特别是关闭后再启动容易发生这种现象,多关闭启动几次后却又恢复正常。笔者估计是服务器m_serverSocket对象释放资源不同步的原因;
    • 客户端连接请求被拒绝:有时客户端发生错误后,再连接时被拒绝。有两种可能,第一种是如前所讲的服务器对象的问题;第二种是客户端Socket接收、发送和断开时被阻塞,具体原因待分析。出现这种情况后,做多次连接/断开操作还是可以连接服务器。

显然,这种测试环境和结果有待进一步验证,但存在较大的改进空间。特别,数据包队列最大值一般不超过5,表明服务器接收到包后立即处理,并发性能比较好。

5 总结与展望

>>[前言][第1节][第2节][第3节][第4节][第5节][第6节]

本文介绍的EMTASS 2.0 是笔者一段工作的总结,也是学习基于.NET的类设计、组件设计、模式设计等的一个小结。笔者的目标就是不断修改和完善,设计与实现一个可靠与稳定的、有良好可扩展性的和易于使用的Socket数据包接收服务器框架。由于最初的代码和思路均来自他人的开源架构和设计构思,EMTASS也仿效一般开源做法:公布源码和设计思路。

基于EMTASS 1.0的服务器有连续运行30天完全正常的记录,而框架EMTASS 2.0 虽然具有可扩展性,也进行了一般的测试,但没有投入实际运行,需要时间检验和实践验证。当前,.NET 3.0及3.5 Framwwork提供的IOCP(完成端口)具有更好的异步并发处理能力,笔者将结合新的运行平台,完善与升级EMTASS,并公布完善与升级计划。如果有读者使用EMTASS 2.0时发现问题,或有更好的建议或想法,请不吝指正。

6 版本与源码

>>[前言][第1节][第2节][第3节][第4节][第5节][第6节]
  • EMTASS 1.0, 2005年12月
  • EMTASS 1.1, 2008年09月
  • EMTASS 2.0, 2008年10月27日
    • 重设计了类名及类关系,增加了框架的可扩展性,重构了大部分代码
    • TSessionBase的数据包队列取代TSocketServerBase的数据包队列
    • TSessionBase中封装了全部通信方法
  • EMTASS 2.1, 2008年11月9日
    • 增加了缓冲区管理类BufferManager,管理两个可重复使用的发送/接收缓冲区。
    • TSocketServerBase增加了如下属性:
      • ReceiveBufferSize:接收缓冲区大小(默认16K)
      • SendBufferSize:发送缓冲区大小(默认16K)
      • CheckDatagramQueueTimeInterval:数据包处理线程Sleep时间间隔(默认:100ms)
      • CheckSessionTableTimeInterval:会话资源清理线程Sleep时间间隔(默认:100ms)
    • TSocketServerBase增加了若干构造函数,包括最大任务数和缓冲区大小
    • TSocketServerBase使用了Mutex互斥类,防止同机器创建两个服务器
    • TSessionBase在异步接收/发送完成后,调用IAsyncResult.AsyncWaitHandle.Close()方法
    • TSessionBase接收数据时,使用BufferManger公共缓冲区ReceivevBuffer
    • TSessionBase发送数据时,如果据长度小于发送缓冲区,则使用SendBuffer,否则申请字节数组发送
    • 测试结果:10个干扰客户端(100ms连续连开)与15个数据客户端(3个100K/100ms、3个100K/50ms、4个100K/20ms、2个1M/1s、2个1M/500ms、1个1M/10s),CPU占用率为70-90%,速度为40/s,无错误包,直接显示的连接数为30-50之间

(转自:http://blog.csdn.net/hulihui)

可扩展多线程异步Socket服务器框架EMTASS 2.0的更多相关文章

  1. 可扩展多线程异步Socket服务器框架EMTASS 2.0 (转自:http://blog.csdn.net/hulihui)

    可扩展多线程异步Socket服务器框架EMTASS 2.0 (转自:http://blog.csdn.net/hulihui) 0 前言 >>[前言].[第1节].[第2节].[第3节]. ...

  2. 可扩展多线程异步Socket服务器框架EMTASS 2.0 续

    转载自Csdn:http://blog.csdn.net/hulihui/article/details/3158613 (原创文章,转载请注明来源:http://blog.csdn.net/huli ...

  3. 《Unity 3D游戏客户端基础框架》多线程异步 Socket 框架构建

    引言: 之前写过一个 demo 案例大致讲解了 Socket 通信的过程,并和自建的服务器完成连接和简单的数据通信,详细的内容可以查看 Unity3D -- Socket通信(C#).但是在实际项目应 ...

  4. C# 实现的多线程异步Socket数据包接收器框架

    转载自Csdn : http://blog.csdn.net/jubao_liang/article/details/4005438 几天前在博问中看到一个C# Socket问题,就想到笔者2004年 ...

  5. workerman是一个高性能的PHP socket服务器框架

    workerman-chatorkerman是一款纯PHP开发的开源高性能的PHP socket服务器框架.被广泛的用于手机app.手游服务端.网络游戏服务器.聊天室服务器.硬件通讯服务器.智能家居. ...

  6. PHP socket 服务器框架集

    1.Swoole:重新定义PHP PHP语言的高性能网络通信框架,提供了PHP语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySQL,数据库连接池,AsyncTask,消息队列,毫秒定时 ...

  7. 异步Socket服务器与客户端

      本文灵感来自Andre Azevedo 在CodeProject上面的一片文章,An Asynchronous Socket Server and Client,讲的是异步的Socket通信. S ...

  8. C#实现的异步Socket服务器

    介绍 我最近需要为一个.net项目准备一个内部线程通信机制. 项目有多个使用ASP.NET,Windows 表单和控制台应用程序的服务器和客户端构成. 考虑到实现的可能性,我下定决心要使用原生的soc ...

  9. C# 实现的异步 Socket 服务器

    介绍 我最近需要为一个.net项目准备一个内部线程通信机制. 项目有多个使用ASP.NET,Windows 表单和控制台应用程序的服务器和客户端构成. 考虑到实现的可能性,我下定决心要使用原生的soc ...

随机推荐

  1. MVC Linq动态排序

    在nuget 中searh System.Linq.Dynamic 安装对应的版本, 这样都可以使用了 var orderExpression = string.Format("{0} {1 ...

  2. hosts.allow hosts.deny

    简单控制访问ip,hosts.allow/hsots.deny  a simple access control language that is based on client (host name ...

  3. 【LeetCode】166. Fraction to Recurring Decimal

    Fraction to Recurring Decimal Given two integers representing the numerator and denominator of a fra ...

  4. 制作IOS 后台极光推送时,遇到的小问题

    推送广义上分为两种, 一种是  程序在前台的时候,不想在任务栏里面显示通知,直接在app中进行某种操作.这个叫做自定义消息.这个是在前台时,app与极光后台建立了一个长链接. 另一种是  程序处于前. ...

  5. 开源 java CMS - FreeCMS2.2 单位管理

    项目地址:http://www.freeteam.cn/ 单位管理 FreeCMS支持多单位同一时候使用,并支持无限树级管理. 1. 加入根单位 从左側管理菜单点击单位管理进入. 点击"加入 ...

  6. POJ 1789:Truck History(prim&amp;&amp;最小生成树)

    id=1789">Truck History Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 17610   ...

  7. Excel操作类库最常用到的4种开源项目与MS Excel类库写操作对比分析性能

    4种开源Excel读写类库与MS Excel类库写操作对比 软件开发过程中,经常需要将数据保存为.xls或.xlsx文件.之前发现微软提供的Microsoft.Office.Interop.Excel ...

  8. 【转】input file accept属性可以限制的文件类型

    来源:http://blog.sina.com.cn/s/blog_6c9d65a10101a8yh.html 在上传文件的时候,需要限制指定的文件类型. <input type="f ...

  9. gcc自有的define语法,解决变量多次自加的问题

    如果定义一个这样的宏: #define MAX(a,b) ((a)>(b)?(a):(b)) int main(void){ int a=5,b=10; MAX(a++,b++); printf ...

  10. 使用cat命令添加或附加多行文本

    覆盖原有文本: cat>test<<EOF the 1 line the 2 line the 3 line EOF 追加的原有文本尾部: cat>>test<&l ...