在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分

//////////////////////////////////////////////////////////////////////////////////////////
/*

标题:在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分

当看到.NET中TcpListener和TcpClient的时候,我非常高兴,那就是我想要的通讯模式
但是使用之后发现它们的力量太单薄了,我们需要一个更好的类库来替代它们.

下面提供了一些类,可以很好的完成Tcp的C/S通讯模式.在本文的第二部分,我将为大家介绍怎么使用它们

主要通过事件来现实整个的功能:
服务器的事件包括:

服务器满
新客户端连接
客户端关闭
接收到数据

客户端使用的事件包括:

已连接服务器
接收到数据
连接关闭

另外为了灵活的处理需求变化,还提供了编码器和报文解析器的实现方法.
注意:该类库没有经过严格的测试,如出现Bug,请发送给我,我会觉得你的整个行为是对我的鼓励和支持.

*/
//////////////////////////////////////////////////////////////////////////////////////////

///

 1<summary>   
 2/// (C)2003-2005 C2217 Studio   
 3/// 保留所有权利   
 4///   
 5/// 文件名称: TcpCSFramework.cs   
 6/// 文件ID:   
 7/// 编程语言: C#   
 8/// 文件说明: 提供TCP网络服务的C/S的通讯构架基础类   
 9/// (使用异步Socket编程实现)   
10///   
11/// 当前版本: 1.1   
12/// 替换版本: 1.0   
13///   
14/// 作者: 邓杨均   
15/// EMail: [email protected]   
16/// 创建日期: 2005-3-9   
17/// 最后修改日期: 2005-3-17   
18///   
19/// 历史修改记录:   
20///   
21/// 时间: 2005-3-14   
22/// 修改内容:   
23/// 1.创建Ibms.Net.TcpCSFramework命名空间和添加Session对象.   
24/// 2.修改NetEventArgs类,以适应新添加对象.   
25/// 3.添加了会话退出类型,更适合实际的情况.   
26/// 注意:   
27/// * 强制退出类型是应用程序直接结束,比如通过任务管理器结束   
28/// 程序或者程序异常退出等,没有执行正常的退出方法而产生的.   
29/// * 正常的退出类型是应用程序执行正常的退出的方法关键在于   
30/// 需要调用Socket.Shutdown( SocketShutdown.Both )后才调用   
31/// Socket.Close()方法,而不是直接的调用Socket.Close()方法,   
32/// 如果那样调用将产生强制退出类型.   
33///   
34/// 时间: 2005-3-16   
35/// 修改内容:   
36/// 1.创建TcpCli,Coder,DatagramResover对象,把抽象和实现部分分离   
37/// 2.文件版本修改为1.1,1.0版本仍然保留,更名为:   
38/// TcpCSFramework_v1.0.cs   
39/// 3.在TcpServer中修改自定义的hashtable为系统Hashtable类型   
40///   
41/// </summary>

using System;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Diagnostics;
using System.Collections;

namespace Ibms.Net.TcpCSFramework
{

///

1<summary>   
2/// 网络通讯事件模型委托   
3/// </summary>

public delegate void NetEvent(object sender, NetEventArgs e);

///

 1<summary>   
 2/// 提供TCP连接服务的服务器类   
 3///   
 4/// 版本: 1.1   
 5/// 替换版本: 1.0   
 6///   
 7/// 特点:   
 8/// 1.使用hash表保存所有已连接客户端的状态,收到数据时能实现快速查找.每当   
 9/// 有一个新的客户端连接就会产生一个新的会话(Session).该Session代表了客   
10/// 户端对象.   
11/// 2.使用异步的Socket事件作为基础,完成网络通讯功能.   
12/// 3.支持带标记的数据报文格式的识别,以完成大数据报文的传输和适应恶劣的网   
13/// 络环境.初步规定该类支持的最大数据报文为640K(即一个数据包的大小不能大于   
14/// 640K,否则服务器程序会自动删除报文数据,认为是非法数据),防止因为数据报文   
15/// 无限制的增长而倒是服务器崩溃   
16/// 4.通讯格式默认使用Encoding.Default格式这样就可以和以前32位程序的客户端   
17/// 通讯.也可以使用U-16和U-8的的通讯方式进行.可以在该DatagramResolver类的   
18/// 继承类中重载编码和解码函数,自定义加密格式进行通讯.总之确保客户端与服务   
19/// 器端使用相同的通讯格式   
20/// 5.使用C# native code,将来出于效率的考虑可以将C++代码写成的32位dll来代替   
21/// C#核心代码, 但这样做缺乏可移植性,而且是Unsafe代码(该类的C++代码也存在)   
22/// 6.可以限制服务器的最大登陆客户端数目   
23/// 7.比使用TcpListener提供更加精细的控制和更加强大异步数据传输的功能,可作为   
24/// TcpListener的替代类   
25/// 8.使用异步通讯模式,完全不用担心通讯阻塞和线程问题,无须考虑通讯的细节   
26///   
27/// 注意:   
28/// 1.部分的代码由Rational XDE生成,可能与编码规范不符   
29///   
30/// 原理:   
31///   
32///   
33/// 使用用法:   
34///   
35/// 例子:   
36///   
37/// </summary>

public class TcpSvr
{
#region 定义字段

///

1<summary>   
2/// 默认的服务器最大连接客户端端数据   
3/// </summary>

public const int DefaultMaxClient=100;

///

1<summary>   
2/// 接收数据缓冲区大小64K   
3/// </summary>

public const int DefaultBufferSize = 64*1024;

///

1<summary>   
2/// 最大数据报文大小   
3/// </summary>

public const int MaxDatagramSize = 640*1024;

///

1<summary>   
2/// 报文解析器   
3/// </summary>

private DatagramResolver _resolver;

///

1<summary>   
2/// 通讯格式编码解码器   
3/// </summary>

private Coder _coder;

///

1<summary>   
2/// 服务器程序使用的端口   
3/// </summary>

private ushort _port;

///

1<summary>   
2/// 服务器程序允许的最大客户端连接数   
3/// </summary>

private ushort _maxClient;

///

1<summary>   
2/// 服务器的运行状态   
3/// </summary>

private bool _isRun;

///

1<summary>   
2/// 接收数据缓冲区   
3/// </summary>

private byte[] _recvDataBuffer;

///

1<summary>   
2/// 服务器使用的异步Socket类,   
3/// </summary>

private Socket _svrSock;

///

1<summary>   
2/// 保存所有客户端会话的哈希表   
3/// </summary>

private Hashtable _sessionTable;

///

1<summary>   
2/// 当前的连接的客户端数   
3/// </summary>

private ushort _clientCount;

#endregion

#region 事件定义

///

1<summary>   
2/// 客户端建立连接事件   
3/// </summary>

public event NetEvent ClientConn;

///

1<summary>   
2/// 客户端关闭事件   
3/// </summary>

public event NetEvent ClientClose;

///

1<summary>   
2/// 服务器已经满事件   
3/// </summary>

public event NetEvent ServerFull;

///

1<summary>   
2/// 服务器接收到数据事件   
3/// </summary>

public event NetEvent RecvData;

#endregion

#region 构造函数

///

1<summary>   
2/// 构造函数   
3/// </summary>

///

1<param name="port"/>

服务器端监听的端口号
///

1<param name="maxClient"/>

服务器能容纳客户端的最大能力
///

1<param name="encodingMothord"/>

通讯的编码方式
public TcpSvr( ushort port,ushort maxClient, Coder coder)
{
_port = port;
_maxClient = maxClient;
_coder = coder;
}

///

1<summary>   
2/// 构造函数(默认使用Default编码方式)   
3/// </summary>

///

1<param name="port"/>

服务器端监听的端口号
///

1<param name="maxClient"/>

服务器能容纳客户端的最大能力
public TcpSvr( ushort port,ushort maxClient)
{
_port = port;
_maxClient = maxClient;
_coder = new Coder(Coder.EncodingMothord.Default);
}

//

1<summary>   
2/// 构造函数(默认使用Default编码方式和DefaultMaxClient(100)个客户端的容量)   
3/// </summary>

///

1<param name="port"/>

服务器端监听的端口号
public TcpSvr( ushort port):this( port, DefaultMaxClient)
{
}

#endregion

#region 属性

///

1<summary>   
2/// 服务器的Socket对象   
3/// </summary>

public Socket ServerSocket
{
get
{
return _svrSock;
}
}

///

1<summary>   
2/// 数据报文分析器   
3/// </summary>

public DatagramResolver Resovlver
{
get
{
return _resolver;
}
set
{
_resolver = value;
}
}

///

1<summary>   
2/// 客户端会话数组,保存所有的客户端,不允许对该数组的内容进行修改   
3/// </summary>

public Hashtable SessionTable
{
get
{
return _sessionTable;
}
}

///

1<summary>   
2/// 服务器可以容纳客户端的最大能力   
3/// </summary>

public int Capacity
{
get
{
return _maxClient;
}
}

///

1<summary>   
2/// 当前的客户端连接数   
3/// </summary>

public int SessionCount
{
get
{
return _clientCount;
}
}

///

1<summary>   
2/// 服务器运行状态   
3/// </summary>

public bool IsRun
{
get
{
return _isRun;
}

}

#endregion

#region 公有方法

///

1<summary>   
2/// 启动服务器程序,开始监听客户端请求   
3/// </summary>

public virtual void Start()
{
if( _isRun )
{
throw (new ApplicationException("TcpSvr已经在运行."));
}

_sessionTable = new Hashtable(53);

_recvDataBuffer = new byte[DefaultBufferSize];

//初始化socket
_svrSock = new Socket( AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp );

//绑定端口
IPEndPoint iep = new IPEndPoint( IPAddress.Any, _port);
_svrSock.Bind(iep);

//开始监听
_svrSock.Listen(5);

//设置异步方法接受客户端连接
_svrSock.BeginAccept(new AsyncCallback( AcceptConn ), _svrSock);

_isRun = true;

}

///

1<summary>   
2/// 停止服务器程序,所有与客户端的连接将关闭   
3/// </summary>

public virtual void Stop()
{
if( !_isRun )
{
throw (new ApplicationException("TcpSvr已经停止"));
}

//这个条件语句,一定要在关闭所有客户端以前调用
//否则在EndConn会出现错误
_isRun = false;

//关闭数据连接,负责客户端会认为是强制关闭连接
if( _svrSock.Connected )
{
_svrSock.Shutdown( SocketShutdown.Both );
}

CloseAllClient();

//清理资源
_svrSock.Close();

_sessionTable = null;

}

///

1<summary>   
2/// 关闭所有的客户端会话,与所有的客户端连接会断开   
3/// </summary>

public virtual void CloseAllClient()
{
foreach(Session client in _sessionTable.Values)
{
client.Close();
}

_sessionTable.Clear();
}

///

1<summary>   
2/// 关闭一个与客户端之间的会话   
3/// </summary>

///

1<param name="closeClient"/>

需要关闭的客户端会话对象
public virtual void CloseSession(Session closeClient)
{
Debug.Assert( closeClient !=null);

if( closeClient !=null )
{

closeClient.Datagram =null;

_sessionTable.Remove(closeClient.ID);

_clientCount--;

//客户端强制关闭链接
if( ClientClose != null )
{
ClientClose(this, new NetEventArgs( closeClient ));
}

closeClient.Close();
}
}

///

1<summary>   
2/// 发送数据   
3/// </summary>

///

1<param name="recvDataClient"/>

接收数据的客户端会话
///

1<param name="datagram"/>

数据报文
public virtual void Send( Session recvDataClient, string datagram )
{
//获得数据编码
byte [] data = _coder.GetEncodingBytes(datagram);

recvDataClient.ClientSocket.BeginSend( data, 0, data.Length, SocketFlags.None,
new AsyncCallback( SendDataEnd ), recvDataClient.ClientSocket );

}

#endregion

#region 受保护方法
///

1<summary>   
2/// 关闭一个客户端Socket,首先需要关闭Session   
3/// </summary>

///

1<param name="client"/>

目标Socket对象
///

1<param name="exitType"/>

客户端退出的类型
protected virtual void CloseClient( Socket client, Session.ExitType exitType)
{
Debug.Assert ( client !=null);

//查找该客户端是否存在,如果不存在,抛出异常
Session closeClient = FindSession(client);

closeClient.TypeOfExit = exitType;

if(closeClient!=null)
{
CloseSession(closeClient);
}
else
{
throw( new ApplicationException("需要关闭的Socket对象不存在"));
}
}

///

1<summary>   
2/// 客户端连接处理函数   
3/// </summary>

///

1<param name="iar"/>

欲建立服务器连接的Socket对象
protected virtual void AcceptConn(IAsyncResult iar)
{
//如果服务器停止了服务,就不能再接收新的客户端
if( !_isRun)
{
return;
}

//接受一个客户端的连接请求
Socket oldserver = ( Socket ) iar.AsyncState;

Socket client = oldserver.EndAccept(iar);

//检查是否达到最大的允许的客户端数目
if( _clientCount == _maxClient )
{
//服务器已满,发出通知
if( ServerFull != null )
{
ServerFull(this, new NetEventArgs( new Session(client)));
}

}
else
{

Session newSession = new Session( client );

_sessionTable.Add(newSession.ID, newSession);

//客户端引用计数+1
_clientCount ++;

//开始接受来自该客户端的数据
client.BeginReceive( _recvDataBuffer,0 , _recvDataBuffer.Length, SocketFlags.None,
new AsyncCallback(ReceiveData), client);

//新的客户段连接,发出通知
if( ClientConn != null )
{
ClientConn(this, new NetEventArgs(newSession ) );
}
}

//继续接受客户端
_svrSock.BeginAccept(new AsyncCallback( AcceptConn ), _svrSock);
}

///

1<summary>   
2/// 通过Socket对象查找Session对象   
3/// </summary>

///

1<param name="client"/>

///

1<returns>找到的Session对象,如果为null,说明并不存在该回话</returns>

private Session FindSession( Socket client )
{
SessionId id = new SessionId((int)client.Handle);

return (Session)_sessionTable[id];
}

///

1<summary>   
2/// 接受数据完成处理函数,异步的特性就体现在这个函数中,   
3/// 收到数据后,会自动解析为字符串报文   
4/// </summary>

///

1<param name="iar"/>

目标客户端Socket
protected virtual void ReceiveData(IAsyncResult iar)
{
Socket client = (Socket)iar.AsyncState;

try
{
//如果两次开始了异步的接收,所以当客户端退出的时候
//会两次执行EndReceive

int recv = client.EndReceive(iar);

if( recv == 0 )
{
//正常的关闭
CloseClient(client, Session.ExitType.NormalExit);
return;
}

string receivedData = _coder.GetEncodingString( _recvDataBuffer, recv );

//发布收到数据的事件
if(RecvData!=null)
{
Session sendDataSession= FindSession(client);

Debug.Assert( sendDataSession!=null );

//如果定义了报文的尾标记,需要处理报文的多种情况
if(_resolver != null)
{
if( sendDataSession.Datagram !=null &&
sendDataSession.Datagram.Length !=0)
{
//加上最后一次通讯剩余的报文片断
receivedData= sendDataSession.Datagram + receivedData ;
}

string [] recvDatagrams = _resolver.Resolve(ref receivedData);

foreach(string newDatagram in recvDatagrams)
{
//深拷贝,为了保持Datagram的对立性
ICloneable copySession = (ICloneable)sendDataSession;

Session clientSession = (Session)copySession.Clone();

clientSession.Datagram = newDatagram;
//发布一个报文消息
RecvData(this,new NetEventArgs( clientSession ));
}

//剩余的代码片断,下次接收的时候使用
sendDataSession.Datagram = receivedData;

if( sendDataSession.Datagram.Length > MaxDatagramSize )
{
sendDataSession.Datagram = null;
}

}
//没有定义报文的尾标记,直接交给消息订阅者使用
else
{
ICloneable copySession = (ICloneable)sendDataSession;

Session clientSession = (Session)copySession.Clone();

clientSession.Datagram = receivedData;

RecvData(this,new NetEventArgs( clientSession ));
}

}//end of if(RecvData!=null)

//继续接收来自来客户端的数据
client.BeginReceive( _recvDataBuffer, 0, _recvDataBuffer.Length , SocketFlags.None,
new AsyncCallback( ReceiveData ), client);

}
catch(SocketException ex)
{
//客户端退出
if( 10054 == ex.ErrorCode )
{
//客户端强制关闭
CloseClient(client, Session.ExitType.ExceptionExit);
}

}
catch(ObjectDisposedException ex)
{
//这里的实现不够优雅
//当调用CloseSession()时,会结束数据接收,但是数据接收
//处理中会调用int recv = client.EndReceive(iar);
//就访问了CloseSession()已经处置的对象
//我想这样的实现方法也是无伤大雅的.
if(ex!=null)
{
ex=null;
//DoNothing;
}
}

}

///

1<summary>   
2/// 发送数据完成处理函数   
3/// </summary>

///

1<param name="iar"/>

目标客户端Socket
protected virtual void SendDataEnd(IAsyncResult iar)
{
Socket client = (Socket)iar.AsyncState;

int sent = client.EndSend(iar);
}

#endregion

}

///

 1<summary>   
 2/// 提供Tcp网络连接服务的客户端类   
 3///   
 4/// 版本: 1.0   
 5/// 替换版本:   
 6///   
 7/// 特征:   
 8/// 原理:   
 9/// 1.使用异步Socket通讯与服务器按照一定的通讯格式通讯,请注意与服务器的通   
10/// 讯格式一定要一致,否则可能造成服务器程序崩溃,整个问题没有克服,怎么从byte[]   
11/// 判断它的编码格式   
12/// 2.支持带标记的数据报文格式的识别,以完成大数据报文的传输和适应恶劣的网   
13/// 络环境.   
14/// 用法:   
15/// 注意:   
16/// </summary>

public class TcpCli
{
#region 字段

///

1<summary>   
2/// 客户端与服务器之间的会话类   
3/// </summary>

private Session _session;

///

1<summary>   
2/// 客户端是否已经连接服务器   
3/// </summary>

private bool _isConnected = false;

///

1<summary>   
2/// 接收数据缓冲区大小64K   
3/// </summary>

public const int DefaultBufferSize = 64*1024;

///

1<summary>   
2/// 报文解析器   
3/// </summary>

private DatagramResolver _resolver;

///

1<summary>   
2/// 通讯格式编码解码器   
3/// </summary>

private Coder _coder;

///

1<summary>   
2/// 接收数据缓冲区   
3/// </summary>

private byte[] _recvDataBuffer = new byte[DefaultBufferSize];

#endregion

#region 事件定义

//需要订阅事件才能收到事件的通知,如果订阅者退出,必须取消订阅

///

1<summary>   
2/// 已经连接服务器事件   
3/// </summary>

public event NetEvent ConnectedServer;

///

1<summary>   
2/// 接收到数据报文事件   
3/// </summary>

public event NetEvent ReceivedDatagram;

///

1<summary>   
2/// 连接断开事件   
3/// </summary>

public event NetEvent DisConnectedServer;
#endregion

#region 属性

///

1<summary>   
2/// 返回客户端与服务器之间的会话对象   
3/// </summary>

public Session ClientSession
{
get
{
return _session;
}
}

///

1<summary>   
2/// 返回客户端与服务器之间的连接状态   
3/// </summary>

public bool IsConnected
{
get
{
return _isConnected;
}
}

///

1<summary>   
2/// 数据报文分析器   
3/// </summary>

public DatagramResolver Resovlver
{
get
{
return _resolver;
}
set
{
_resolver = value;
}
}

///

1<summary>   
2/// 编码解码器   
3/// </summary>

public Coder ServerCoder
{
get
{
return _coder;
}
}

#endregion

#region 公有方法

///

1<summary>   
2/// 默认构造函数,使用默认的编码格式   
3/// </summary>

public TcpCli()
{
_coder = new Coder( Coder.EncodingMothord.Default );
}

///

1<summary>   
2/// 构造函数,使用一个特定的编码器来初始化   
3/// </summary>

///

1<param name="_coder"/>

报文编码器
public TcpCli( Coder coder )
{
_coder = coder;
}

///

1<summary>   
2/// 连接服务器   
3/// </summary>

///

1<param name="ip"/>

服务器IP地址
///

1<param name="port"/>

服务器端口
public virtual void Connect( string ip, int port)
{
if(IsConnected)
{
//重新连接
Debug.Assert( _session !=null);

Close();
}

Socket newsock= new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);

IPEndPoint iep = new IPEndPoint( IPAddress.Parse(ip), port);
newsock.BeginConnect(iep, new AsyncCallback(Connected), newsock);

}

///

1<summary>   
2/// 发送数据报文   
3/// </summary>

///

1<param name="datagram"/>

public virtual void Send( string datagram)
{
if(datagram.Length ==0 )
{
return;
}

if( !_isConnected )
{
throw (new ApplicationException("没有连接服务器,不能发送数据") );
}

//获得报文的编码字节
byte [] data = _coder.GetEncodingBytes(datagram);

_session.ClientSocket.BeginSend( data, 0, data.Length, SocketFlags.None,
new AsyncCallback( SendDataEnd ), _session.ClientSocket);
}

///

1<summary>   
2/// 关闭连接   
3/// </summary>

public virtual void Close()
{
if(!_isConnected)
{
return;
}

_session.Close();

_session = null;

_isConnected = false;
}

#endregion

#region 受保护方法

///

1<summary>   
2/// 数据发送完成处理函数   
3/// </summary>

///

1<param name="iar"/>

protected virtual void SendDataEnd(IAsyncResult iar)
{
Socket remote = (Socket)iar.AsyncState;
int sent = remote.EndSend(iar);
Debug.Assert(sent !=0);

}

///

1<summary>   
2/// 建立Tcp连接后处理过程   
3/// </summary>

///

1<param name="iar"/>

异步Socket
protected virtual void Connected(IAsyncResult iar)
{
Socket socket = (Socket)iar.AsyncState;

socket.EndConnect(iar);

//创建新的会话
_session = new Session(socket);

_isConnected = true;

//触发连接建立事件
if(ConnectedServer != null)
{
ConnectedServer(this, new NetEventArgs(_session));
}

//建立连接后应该立即接收数据
_session.ClientSocket.BeginReceive(_recvDataBuffer, 0,
DefaultBufferSize, SocketFlags.None,
new AsyncCallback(RecvData), socket);
}

///

1<summary>   
2/// 数据接收处理函数   
3/// </summary>

///

1<param name="iar"/>

异步Socket
protected virtual void RecvData(IAsyncResult iar)
{
Socket remote = (Socket)iar.AsyncState;

try
{
int recv = remote.EndReceive(iar);

//正常的退出
if(recv ==0 )
{
_session.TypeOfExit = Session.ExitType.NormalExit;

if(DisConnectedServer!=null)
{
DisConnectedServer(this, new NetEventArgs(_session));
}

return;
}

string receivedData = _coder.GetEncodingString( _recvDataBuffer,recv );

//通过事件发布收到的报文
if(ReceivedDatagram != null)
{
//通过报文解析器分析出报文
//如果定义了报文的尾标记,需要处理报文的多种情况
if(_resolver != null)
{
if( _session.Datagram !=null &&
_session.Datagram.Length !=0)
{
//加上最后一次通讯剩余的报文片断
receivedData= _session.Datagram + receivedData ;
}

string [] recvDatagrams = _resolver.Resolve(ref receivedData);

foreach(string newDatagram in recvDatagrams)
{
//Need Deep Copy.因为需要保证多个不同报文独立存在
ICloneable copySession = (ICloneable)_session;

Session clientSession = (Session)copySession.Clone();

clientSession.Datagram = newDatagram;

//发布一个报文消息
ReceivedDatagram(this,new NetEventArgs( clientSession ));
}

//剩余的代码片断,下次接收的时候使用
_session.Datagram = receivedData;
}
//没有定义报文的尾标记,直接交给消息订阅者使用
else
{
ICloneable copySession = (ICloneable)_session;

Session clientSession = (Session)copySession.Clone();

clientSession.Datagram = receivedData;

ReceivedDatagram( this, new NetEventArgs( clientSession ));

}

}//end of if(ReceivedDatagram != null)

//继续接收数据
_session.ClientSocket.BeginReceive(_recvDataBuffer, 0, DefaultBufferSize, SocketFlags.None,
new AsyncCallback(RecvData), _session.ClientSocket);
}
catch(SocketException ex)
{
//客户端退出
if( 10054 == ex.ErrorCode )
{
//服务器强制的关闭连接,强制退出
_session.TypeOfExit = Session.ExitType.ExceptionExit;

if(DisConnectedServer!=null)
{
DisConnectedServer(this, new NetEventArgs(_session));
}
}
else
{
throw( ex );
}
}
catch(ObjectDisposedException ex)
{
//这里的实现不够优雅
//当调用CloseSession()时,会结束数据接收,但是数据接收
//处理中会调用int recv = client.EndReceive(iar);
//就访问了CloseSession()已经处置的对象
//我想这样的实现方法也是无伤大雅的.
if(ex!=null)
{
ex =null;
//DoNothing;
}
}

}

#endregion

}

///

1<summary>   
2/// 通讯编码格式提供者,为通讯服务提供编码和解码服务   
3/// 你可以在继承类中定制自己的编码方式如:数据加密传输等   
4/// </summary>

public class Coder
{
///

1<summary>   
2/// 编码方式   
3/// </summary>

private EncodingMothord _encodingMothord;

protected Coder()
{

}

public Coder(EncodingMothord encodingMothord)
{
_encodingMothord = encodingMothord;
}

public enum EncodingMothord
{
Default =0,
Unicode,
UTF8,
ASCII,
}

///

1<summary>   
2/// 通讯数据解码   
3/// </summary>

///

1<param name="dataBytes"/>

需要解码的数据
///

1<returns>编码后的数据</returns>

public virtual string GetEncodingString( byte [] dataBytes,int size)
{
switch( _encodingMothord )
{
case EncodingMothord.Default:
{
return Encoding.Default.GetString(dataBytes,0,size);
}
case EncodingMothord.Unicode:
{
return Encoding.Unicode.GetString(dataBytes,0,size);
}
case EncodingMothord.UTF8:
{
return Encoding.UTF8.GetString(dataBytes,0,size);
}
case EncodingMothord.ASCII:
{
return Encoding.ASCII.GetString(dataBytes,0,size);
}
default:
{
throw( new Exception("未定义的编码格式"));
}
}

}

///

1<summary>   
2/// 数据编码   
3/// </summary>

///

1<param name="datagram"/>

需要编码的报文
///

1<returns>编码后的数据</returns>

public virtual byte[] GetEncodingBytes(string datagram)
{
switch( _encodingMothord)
{
case EncodingMothord.Default:
{
return Encoding.Default.GetBytes(datagram);
}
case EncodingMothord.Unicode:
{
return Encoding.Unicode.GetBytes(datagram);
}
case EncodingMothord.UTF8:
{
return Encoding.UTF8.GetBytes(datagram);
}
case EncodingMothord.ASCII:
{
return Encoding.ASCII.GetBytes(datagram);
}
default:
{
throw( new Exception("未定义的编码格式"));
}
}
}

}

///

1<summary>   
2/// 数据报文分析器,通过分析接收到的原始数据,得到完整的数据报文.   
3/// 继承该类可以实现自己的报文解析方法.   
4/// 通常的报文识别方法包括:固定长度,长度标记,标记符等方法   
5/// 本类的现实的是标记符的方法,你可以在继承类中实现其他的方法   
6/// </summary>

public class DatagramResolver
{
///

1<summary>   
2/// 报文结束标记   
3/// </summary>

private string endTag;

///

1<summary>   
2/// 返回结束标记   
3/// </summary>

string EndTag
{
get
{
return endTag;
}
}

///

1<summary>   
2/// 受保护的默认构造函数,提供给继承类使用   
3/// </summary>

protected DatagramResolver()
{

}

///

1<summary>   
2/// 构造函数   
3/// </summary>

///

1<param name="endTag"/>

报文结束标记
public DatagramResolver(string endTag)
{
if(endTag == null)
{
throw (new ArgumentNullException("结束标记不能为null"));
}

if(endTag == "")
{
throw (new ArgumentException("结束标记符号不能为空字符串"));
}

this.endTag = endTag;
}

///

1<summary>   
2/// 解析报文   
3/// </summary>

///

1<param name="rawDatagram"/>

原始数据,返回未使用的报文片断,
/// 该片断会保存在Session的Datagram对象中
///

1<returns>报文数组,原始数据可能包含多个报文</returns>

public virtual string [] Resolve(ref string rawDatagram)
{
ArrayList datagrams = new ArrayList();

//末尾标记位置索引
int tagIndex =-1;

while(true)
{
tagIndex = rawDatagram.IndexOf(endTag,tagIndex+1);

if( tagIndex == -1 )
{
break;
}
else
{
//按照末尾标记把字符串分为左右两个部分
string newDatagram = rawDatagram.Substring(
0, tagIndex+endTag.Length);

datagrams.Add(newDatagram);

if(tagIndex+endTag.Length >= rawDatagram.Length)
{
rawDatagram="";

break;
}

rawDatagram = rawDatagram.Substring(tagIndex+endTag.Length,
rawDatagram.Length - newDatagram.Length);

//从开始位置开始查找
tagIndex=0;
}
}

string [] results= new string[datagrams.Count];

datagrams.CopyTo(results);

return results;
}

}

///

 1<summary>   
 2/// 客户端与服务器之间的会话类   
 3///   
 4/// 版本: 1.1   
 5/// 替换版本: 1.0   
 6///   
 7/// 说明:   
 8/// 会话类包含远程通讯端的状态,这些状态包括Socket,报文内容,   
 9/// 客户端退出的类型(正常关闭,强制退出两种类型)   
10/// </summary>

public class Session:ICloneable
{
#region 字段

///

1<summary>   
2/// 会话ID   
3/// </summary>

private SessionId _id;

///

1<summary>   
2/// 客户端发送到服务器的报文   
3/// 注意:在有些情况下报文可能只是报文的片断而不完整   
4/// </summary>

private string _datagram;

///

1<summary>   
2/// 客户端的Socket   
3/// </summary>

private Socket _cliSock;

///

1<summary>   
2/// 客户端的退出类型   
3/// </summary>

private ExitType _exitType;

///

1<summary>   
2/// 退出类型枚举   
3/// </summary>

public enum ExitType
{
NormalExit ,
ExceptionExit
};

#endregion

#region 属性

///

1<summary>   
2/// 返回会话的ID   
3/// </summary>

public SessionId ID
{
get
{
return _id;
}
}

///

1<summary>   
2/// 存取会话的报文   
3/// </summary>

public string Datagram
{
get
{
return _datagram;
}
set
{
_datagram = value;
}
}

///

1<summary>   
2/// 获得与客户端会话关联的Socket对象   
3/// </summary>

public Socket ClientSocket
{
get
{
return _cliSock;
}
}

///

1<summary>   
2/// 存取客户端的退出方式   
3/// </summary>

public ExitType TypeOfExit
{
get
{
return _exitType;
}

set
{
_exitType = value;
}
}

#endregion

#region 方法

///

1<summary>   
2/// 使用Socket对象的Handle值作为HashCode,它具有良好的线性特征.   
3/// </summary>

///

1<returns></returns>

public override int GetHashCode()
{
return (int)_cliSock.Handle;
}

///

1<summary>   
2/// 返回两个Session是否代表同一个客户端   
3/// </summary>

///

1<param name="obj"/>

///

1<returns></returns>

public override bool Equals(object obj)
{
Session rightObj = (Session)obj;

return (int)_cliSock.Handle == (int)rightObj.ClientSocket.Handle;

}

///

1<summary>   
2/// 重载ToString()方法,返回Session对象的特征   
3/// </summary>

///

1<returns></returns>

public override string ToString()
{
string result = string.Format("Session:{0},IP:{1}",
_id,_cliSock.RemoteEndPoint.ToString());

//result.C
return result;
}

///

1<summary>   
2/// 构造函数   
3/// </summary>

///

1<param name="cliSock"/>

会话使用的Socket连接
public Session( Socket cliSock)
{
Debug.Assert( cliSock !=null );

_cliSock = cliSock;

_id = new SessionId( (int)cliSock.Handle);
}

///

1<summary>   
2/// 关闭会话   
3/// </summary>

public void Close()
{
Debug.Assert( _cliSock !=null );

//关闭数据的接受和发送
_cliSock.Shutdown( SocketShutdown.Both );

//清理资源
_cliSock.Close();
}

#endregion

#region ICloneable 成员

object System.ICloneable.Clone()
{
Session newSession = new Session(_cliSock);
newSession.Datagram = _datagram;
newSession.TypeOfExit = _exitType;

return newSession;
}

#endregion
}

///

1<summary>   
2/// 唯一的标志一个Session,辅助Session对象在Hash表中完成特定功能   
3/// </summary>

public class SessionId
{
///

1<summary>   
2/// 与Session对象的Socket对象的Handle值相同,必须用这个值来初始化它   
3/// </summary>

private int _id;

///

1<summary>   
2/// 返回ID值   
3/// </summary>

public int ID
{
get
{
return _id;
}
}

///

1<summary>   
2/// 构造函数   
3/// </summary>

///

1<param name="id"/>

Socket的Handle值
public SessionId(int id)
{
_id = id;
}

///

1<summary>   
2/// 重载.为了符合Hashtable键值特征   
3/// </summary>

///

1<param name="obj"/>

///

1<returns></returns>

public override bool Equals(object obj)
{
if(obj != null )
{
SessionId right = (SessionId) obj;

return _id == right._id;
}
else if(this == null)
{
return true;
}
else
{
return false;
}

}

///

1<summary>   
2/// 重载.为了符合Hashtable键值特征   
3/// </summary>

///

1<returns></returns>

public override int GetHashCode()
{
return _id;
}

///

1<summary>   
2/// 重载,为了方便显示输出   
3/// </summary>

///

1<returns></returns>

public override string ToString()
{
return _id.ToString ();
}

}

///

1<summary>   
2/// 服务器程序的事件参数,包含了激发该事件的会话对象   
3/// </summary>

public class NetEventArgs:EventArgs
{

#region 字段

///

1<summary>   
2/// 客户端与服务器之间的会话   
3/// </summary>

private Session _client;

#endregion

#region 构造函数
///

1<summary>   
2/// 构造函数   
3/// </summary>

///

1<param name="client"/>

客户端会话
public NetEventArgs(Session client)
{
if( null == client)
{
throw(new ArgumentNullException());
}

_client = client;
}
#endregion

#region 属性

///

1<summary>   
2/// 获得激发该事件的会话对象   
3/// </summary>

public Session Client
{
get
{
return _client;
}

}

#endregion

}
}

Published At
Categories with Web编程
Tagged with
comments powered by Disqus