[转]在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)

//////////////////////////////////////////////////////////////////////////////////////////
  /*
  标题:在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分
  当看到.NET中TcpListener和TcpClient的时候,我非常高兴,那就是我想要的通讯模式
  但是使用之后发现它们的力量太单薄了,我们需要一个更好的类库来替代它们.
  下面提供了一些类,可以很好的完成Tcp的C/S通讯模式.在本文的第二部分,我将为大家介绍怎么使用它们
  主要通过事件来现实整个的功能:
  服务器的事件包括:
  服务器满
  新客户端连接
  客户端关闭
  接收到数据
   
  客户端使用的事件包括:
  已连接服务器
  接收到数据
  连接关闭
  另外为了灵活的处理需求变化,还提供了编码器和报文解析器的实现方法.
  注意:该类库没有经过严格的测试,如出现Bug,请发送给我,我会觉得你的整个行为是对我的鼓励和支持.
  
*/
  
//////////////////////////////////////////////////////////////////////////////////////////
  
/// <summary>
  
/// (C)2003-2005 C2217 Studio
  
/// 保留所有权利
  
/// 
  
/// 文件名称: TcpCSFramework.cs
  
/// 文件ID: 
  
/// 编程语言: C#
  
/// 文件说明: 提供TCP网络服务的C/S的通讯构架基础类
  
/// (使用异步Socket编程实现)
  
/// 
  
/// 当前版本: 1.1
  
/// 替换版本: 1.0
  
/// 
  
/// 作者: 邓杨均
  
/// EMail: dyj057@gmail.com
  
/// 创建日期: 2005-3-9
  
/// 最后修改日期: 2005-3-17
  
/// 
  
/// 历史修改记录:
  
/// 
  
/// 时间: 2005-3-14
  
/// 修改内容: 
  
/// 1.创建Ibms.Net.TcpCSFramework命名空间和添加Session对象.
  
/// 2.修改NetEventArgs类,以适应新添加对象.
  
/// 3.添加了会话退出类型,更适合实际的情况.
  
/// 注意:
  
/// * 强制退出类型是应用程序直接结束,比如通过任务管理器结束
  
/// 程序或者程序异常退出等,没有执行正常的退出方法而产生的.
  
/// * 正常的退出类型是应用程序执行正常的退出的方法关键在于
  
/// 需要调用Socket.Shutdown( SocketShutdown.Both )后才调用
  
/// Socket.Close()方法,而不是直接的调用Socket.Close()方法,
  
/// 如果那样调用将产生强制退出类型.
  
/// 
  
/// 时间: 2005-3-16
  
/// 修改内容:
  
/// 1.创建TcpCli,Coder,DatagramResover对象,把抽象和实现部分分离
  
/// 2.文件版本修改为1.1,1.0版本仍然保留,更名为:
  
/// TcpCSFramework_v1.0.cs
  
/// 3.在TcpServer中修改自定义的hashtable为系统Hashtable类型
  
/// 
  
/// </summary>
  using System;
  
using System.Net.Sockets;
  
using System.Net;
  
using System.Text;
  
using System.Diagnostics;
  
using System.Collections;
  
namespace Ibms.Net.TcpCSFramework
  {
  
/// <summary>
  
/// 网络通讯事件模型委托
  
/// </summary>
  public delegate void NetEvent(object sender, NetEventArgs e);
  
/// <summary>
  
/// 提供TCP连接服务的服务器类
  
/// 
  
/// 版本: 1.1
  
/// 替换版本: 1.0 
  
/// 
  
/// 特点:
  
/// 1.使用hash表保存所有已连接客户端的状态,收到数据时能实现快速查找.每当
  
/// 有一个新的客户端连接就会产生一个新的会话(Session).该Session代表了客
  
/// 户端对象.
  
/// 2.使用异步的Socket事件作为基础,完成网络通讯功能.
  
/// 3.支持带标记的数据报文格式的识别,以完成大数据报文的传输和适应恶劣的网
  
/// 络环境.初步规定该类支持的最大数据报文为640K(即一个数据包的大小不能大于
  
/// 640K,否则服务器程序会自动删除报文数据,认为是非法数据),防止因为数据报文
  
/// 无限制的增长而倒是服务器崩溃
  
/// 4.通讯格式默认使用Encoding.Default格式这样就可以和以前32位程序的客户端
  
/// 通讯.也可以使用U-16和U-8的的通讯方式进行.可以在该DatagramResolver类的
  
/// 继承类中重载编码和解码函数,自定义加密格式进行通讯.总之确保客户端与服务
  
/// 器端使用相同的通讯格式
  
/// 5.使用C# native code,将来出于效率的考虑可以将C++代码写成的32位dll来代替
  
/// C#核心代码, 但这样做缺乏可移植性,而且是Unsafe代码(该类的C++代码也存在)
  
/// 6.可以限制服务器的最大登陆客户端数目
  
/// 7.比使用TcpListener提供更加精细的控制和更加强大异步数据传输的功能,可作为
  
/// TcpListener的替代类
  
/// 8.使用异步通讯模式,完全不用担心通讯阻塞和线程问题,无须考虑通讯的细节
  
/// 
  
/// 注意:
  
/// 1.部分的代码由Rational XDE生成,可能与编码规范不符
  
/// 
  
/// 原理:
  
/// 
  
///
  
/// 使用用法:
  
/// 
  
/// 例子:
  
/// 
  
/// </summary>
  public class TcpSvr
  {
   
#region 定义字段
   
   
/// <summary>
   
/// 默认的服务器最大连接客户端端数据
   
/// </summary>
   public const int DefaultMaxClient=100;
   
/// <summary>
   
/// 接收数据缓冲区大小64K
   
/// </summary>
   public const int DefaultBufferSize = 64*1024;
   
/// <summary>
   
/// 最大数据报文大小
   
/// </summary>
   public const int MaxDatagramSize = 640*1024;
   
/// <summary>
   
/// 报文解析器
   
/// </summary>
   private DatagramResolver _resolver;
   
/// <summary>
   
/// 通讯格式编码解码器
   
/// </summary>
   private Coder _coder;
   
/// <summary>
   
/// 服务器程序使用的端口
   
/// </summary>
   private ushort _port;
   
/// <summary>
   
/// 服务器程序允许的最大客户端连接数
   
/// </summary>
   private ushort _maxClient;
   
/// <summary>
   
/// 服务器的运行状态
   
/// </summary>
   private bool _isRun;
   
/// <summary>
   
/// 接收数据缓冲区
   
/// </summary>
   private byte[] _recvDataBuffer;
   
/// <summary>
   
/// 服务器使用的异步Socket类,
   
/// </summary>
   private Socket _svrSock;
   
/// <summary>
   
/// 保存所有客户端会话的哈希表
   
/// </summary>
   private Hashtable _sessionTable;
   
/// <summary>
   
/// 当前的连接的客户端数
   
/// </summary>
   private ushort _clientCount;
   
#endregion
   
#region 事件定义
   
   
/// <summary>
   
/// 客户端建立连接事件
   
/// </summary>
   public event NetEvent ClientConn;
   
/// <summary>
   
/// 客户端关闭事件
   
/// </summary>
   public event NetEvent ClientClose;
   
/// <summary>
   
/// 服务器已经满事件
   
/// </summary>
   public event NetEvent ServerFull;
   
/// <summary>
   
/// 服务器接收到数据事件
   
/// </summary>
   public event NetEvent RecvData;
   
#endregion
   
   
#region 构造函数
   
/// <summary>
   
/// 构造函数
   
/// </summary>
   
/// <param name="port">服务器端监听的端口号</param>
   
/// <param name="maxClient">服务器能容纳客户端的最大能力</param>
   
/// <param name="encodingMothord">通讯的编码方式</param>
   public TcpSvr( ushort port,ushort maxClient, Coder coder)
   {
   _port 
= port;
   _maxClient 
= maxClient;
   _coder 
= coder;
   }
   
/// <summary>
   
/// 构造函数(默认使用Default编码方式)
   
/// </summary>
   
/// <param name="port">服务器端监听的端口号</param>
   
/// <param name="maxClient">服务器能容纳客户端的最大能力</param>
   public TcpSvr( ushort port,ushort maxClient)
   {
   _port 
= port;
   _maxClient 
= maxClient; 
   _coder 
= new Coder(Coder.EncodingMothord.Default);
   }
   
   
// <summary>
   /// 构造函数(默认使用Default编码方式和DefaultMaxClient(100)个客户端的容量)
   
/// </summary>
   
/// <param name="port">服务器端监听的端口号</param>
   public TcpSvr( ushort port):this( port, DefaultMaxClient)
   {
   }
   
#endregion
   
#region 属性
   
/// <summary>
   
/// 服务器的Socket对象
   
/// </summary>
   public Socket ServerSocket
   {
   
get
   {
   
return _svrSock;
   }
   }
   
/// <summary>
   
/// 数据报文分析器
   
/// </summary>
   public DatagramResolver Resovlver
   {
   
get
   {
   
return _resolver;
   }
   
set
   {
   _resolver 
= value;
   }
   }
   
/// <summary>
   
/// 客户端会话数组,保存所有的客户端,不允许对该数组的内容进行修改
   
/// </summary>
   public Hashtable SessionTable 
   {
   
get 
   {
   
return _sessionTable;
   }
   }
   
/// <summary>
   
/// 服务器可以容纳客户端的最大能力
   
/// </summary>
   public int Capacity
   {
   
get 
   {
   
return _maxClient;
   }
   }
   
/// <summary>
   
/// 当前的客户端连接数
   
/// </summary>
   public int SessionCount
   {
   
get
   {
   
return _clientCount;
   }
   }
   
/// <summary>
   
/// 服务器运行状态
   
/// </summary>
   public bool IsRun
   {
   
get
   {
   
return _isRun;
   }
   
   }
   
#endregion
   
   
#region 公有方法
   
/// <summary>
   
/// 启动服务器程序,开始监听客户端请求
   
/// </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;
   }
   
   
/// <summary>
   
/// 停止服务器程序,所有与客户端的连接将关闭
   
/// </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;
   
   }
   
   
/// <summary>
   
/// 关闭所有的客户端会话,与所有的客户端连接会断开
   
/// </summary>
   public virtual void CloseAllClient()
   {
   
foreach(Session client in _sessionTable.Values)
   {
   client.Close();
   }
   _sessionTable.Clear();
   }
   
/// <summary>
   
/// 关闭一个与客户端之间的会话
   
/// </summary>
   
/// <param name="closeClient">需要关闭的客户端会话对象</param>
   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(
thisnew NetEventArgs( closeClient ));
   }
   closeClient.Close();
   }
   }
   
/// <summary>
   
/// 发送数据
   
/// </summary>
   
/// <param name="recvDataClient">接收数据的客户端会话</param>
   
/// <param name="datagram">数据报文</param>
   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 受保护方法
   
/// <summary>
   
/// 关闭一个客户端Socket,首先需要关闭Session
   
/// </summary>
   
/// <param name="client">目标Socket对象</param>
   
/// <param name="exitType">客户端退出的类型</param>
   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
   {
   
thrownew ApplicationException("需要关闭的Socket对象不存在"));
   }
   }
   
/// <summary>
   
/// 客户端连接处理函数
   
/// </summary>
   
/// <param name="iar">欲建立服务器连接的Socket对象</param>
   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(
thisnew 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(
thisnew NetEventArgs(newSession ) );
   }
   }
   
//继续接受客户端
   _svrSock.BeginAccept(new AsyncCallback( AcceptConn ), _svrSock);
   }
   
/// <summary>
   
/// 通过Socket对象查找Session对象
   
/// </summary>
   
/// <param name="client"></param>
   
/// <returns>找到的Session对象,如果为null,说明并不存在该回话</returns>
   private Session FindSession( Socket client )
   {
   SessionId id 
= new SessionId((int)client.Handle);
   
return (Session)_sessionTable[id];
   }
   
   
/// <summary>
   
/// 接受数据完成处理函数,异步的特性就体现在这个函数中,
   
/// 收到数据后,会自动解析为字符串报文
   
/// </summary>
   
/// <param name="iar">目标客户端Socket</param>
   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)
   {
   
//客户端退出
   if10054 == ex.ErrorCode )
   {
   
//客户端强制关闭
   CloseClient(client, Session.ExitType.ExceptionExit);
   }
   
   }
   
catch(ObjectDisposedException ex)
   {
   
//这里的实现不够优雅
   
//当调用CloseSession()时,会结束数据接收,但是数据接收
   
//处理中会调用int recv = client.EndReceive(iar);
   
//就访问了CloseSession()已经处置的对象
   
//我想这样的实现方法也是无伤大雅的.
   if(ex!=null)
   {
   ex
=null;
   
//DoNothing;
   }
   }
   
   }
   
/// <summary>
   
/// 发送数据完成处理函数
   
/// </summary>
   
/// <param name="iar">目标客户端Socket</param>
   protected virtual void SendDataEnd(IAsyncResult iar)
   {
   Socket client 
= (Socket)iar.AsyncState;
   
int sent = client.EndSend(iar);
   }
   
#endregion
  }
  
/// <summary>
  
/// 提供Tcp网络连接服务的客户端类
  
/// 
  
/// 版本: 1.0
  
/// 替换版本: 
  
///
  
/// 特征:
  
/// 原理:
  
/// 1.使用异步Socket通讯与服务器按照一定的通讯格式通讯,请注意与服务器的通
  
/// 讯格式一定要一致,否则可能造成服务器程序崩溃,整个问题没有克服,怎么从byte[]
  
/// 判断它的编码格式
  
/// 2.支持带标记的数据报文格式的识别,以完成大数据报文的传输和适应恶劣的网
  
/// 络环境.
  
/// 用法:
  
/// 注意:
  
/// </summary>
  public class TcpCli
  {
   
#region 字段
   
/// <summary>
   
/// 客户端与服务器之间的会话类
   
/// </summary>
   private Session _session;
   
/// <summary>
   
/// 客户端是否已经连接服务器
   
/// </summary>
   private bool _isConnected = false;
   
/// <summary>
   
/// 接收数据缓冲区大小64K
   
/// </summary>
   public const int DefaultBufferSize = 64*1024;
   
/// <summary>
   
/// 报文解析器
   
/// </summary>
   private DatagramResolver _resolver;
   
/// <summary>
   
/// 通讯格式编码解码器
   
/// </summary>
   private Coder _coder;
   
/// <summary>
   
/// 接收数据缓冲区
   
/// </summary>
   private byte[] _recvDataBuffer = new byte[DefaultBufferSize];
   
#endregion
   
#region 事件定义
   
//需要订阅事件才能收到事件的通知,如果订阅者退出,必须取消订阅
   
   
/// <summary>
   
/// 已经连接服务器事件
   
/// </summary>
   public event NetEvent ConnectedServer;
   
/// <summary>
   
/// 接收到数据报文事件
   
/// </summary>
   public event NetEvent ReceivedDatagram;
   
/// <summary>
   
/// 连接断开事件
   
/// </summary>
   public event NetEvent DisConnectedServer;
   
#endregion
   
#region 属性
   
/// <summary>
   
/// 返回客户端与服务器之间的会话对象
   
/// </summary>
   public Session ClientSession
   {
   
get
   {
   
return _session;
   }
   }
   
/// <summary>
   
/// 返回客户端与服务器之间的连接状态
   
/// </summary>
   public bool IsConnected
   {
   
get
   {
   
return _isConnected;
   }
   }
   
/// <summary>
   
/// 数据报文分析器
   
/// </summary>
   public DatagramResolver Resovlver
   {
   
get
   {
   
return _resolver;
   }
   
set
   {
   _resolver 
= value;
   }
   }
   
/// <summary>
   
/// 编码解码器
   
/// </summary>
   public Coder ServerCoder
   {
   
get
   {
   
return _coder;
   }
   }
   
#endregion
   
   
#region 公有方法
   
/// <summary>
   
/// 默认构造函数,使用默认的编码格式
   
/// </summary>
   public TcpCli()
   {
   _coder 
= new Coder( Coder.EncodingMothord.Default );
   }
   
/// <summary>
   
/// 构造函数,使用一个特定的编码器来初始化
   
/// </summary>
   
/// <param name="_coder">报文编码器</param>
   public TcpCli( Coder coder )
   {
   _coder 
= coder;
   }
   
/// <summary>
   
/// 连接服务器
   
/// </summary>
   
/// <param name="ip">服务器IP地址</param>
   
/// <param name="port">服务器端口</param>
   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);
   }
   
/// <summary>
   
/// 发送数据报文
   
/// </summary>
   
/// <param name="datagram"></param>
   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);
   }
   
/// <summary>
   
/// 关闭连接
   
/// </summary>
   public virtual void Close()
   {
   
if(!_isConnected)
   {
   
return;
   }
   _session.Close();
   _session 
= null;
   _isConnected 
= false;
   }
   
#endregion 
   
#region 受保护方法
   
/// <summary>
   
/// 数据发送完成处理函数
   
/// </summary>
   
/// <param name="iar"></param>
   protected virtual void SendDataEnd(IAsyncResult iar)
   {
   Socket remote 
= (Socket)iar.AsyncState;
   
int sent = remote.EndSend(iar);
   Debug.Assert(sent 
!=0);
   }
   
/// <summary>
   
/// 建立Tcp连接后处理过程
   
/// </summary>
   
/// <param name="iar">异步Socket</param>
   protected virtual void Connected(IAsyncResult iar)
   {
   Socket socket 
= (Socket)iar.AsyncState;
   socket.EndConnect(iar);
   
//创建新的会话
   _session = new Session(socket);
   
   _isConnected 
= true;
   
//触发连接建立事件
   if(ConnectedServer != null)
   {
   ConnectedServer(
thisnew NetEventArgs(_session)); 
   }
   
//建立连接后应该立即接收数据
   _session.ClientSocket.BeginReceive(_recvDataBuffer, 0
   DefaultBufferSize, SocketFlags.None,
   
new AsyncCallback(RecvData), socket);
   }
   
/// <summary>
   
/// 数据接收处理函数
   
/// </summary>
   
/// <param name="iar">异步Socket</param>
   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(
thisnew 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( 
thisnew NetEventArgs( clientSession ));
   }
   }
//end of if(ReceivedDatagram != null)
   
//继续接收数据
   _session.ClientSocket.BeginReceive(_recvDataBuffer, 0, DefaultBufferSize, SocketFlags.None,
   
new AsyncCallback(RecvData), _session.ClientSocket);
   }
   
catch(SocketException ex)
   {
   
//客户端退出
   if10054 == ex.ErrorCode )
   {
   
//服务器强制的关闭连接,强制退出
   _session.TypeOfExit = Session.ExitType.ExceptionExit;
   
if(DisConnectedServer!=null)
   {
   DisConnectedServer(
thisnew NetEventArgs(_session));
   }
   }
   
else
   {
   
throw( ex );
   }
   }
   
catch(ObjectDisposedException ex)
   {
   
//这里的实现不够优雅
   
//当调用CloseSession()时,会结束数据接收,但是数据接收
   
//处理中会调用int recv = client.EndReceive(iar);
   
//就访问了CloseSession()已经处置的对象
   
//我想这样的实现方法也是无伤大雅的.
   if(ex!=null)
   {
   ex 
=null;
   
//DoNothing;
   }
   }
   }
   
   
#endregion
  }
  
/// <summary>
  
/// 通讯编码格式提供者,为通讯服务提供编码和解码服务
  
/// 你可以在继承类中定制自己的编码方式如:数据加密传输等
  
/// </summary>
  public class Coder
  {
   
/// <summary>
   
/// 编码方式
   
/// </summary>
   private EncodingMothord _encodingMothord;
   
protected Coder()
   {
   
   }
   
   
public Coder(EncodingMothord encodingMothord)
   {
   _encodingMothord 
= encodingMothord;
   }
   
public enum EncodingMothord
   {
   Default 
=0,
   Unicode,
   UTF8,
   ASCII,
   }
   
/// <summary>
   
/// 通讯数据解码
   
/// </summary>
   
/// <param name="dataBytes">需要解码的数据</param>
   
/// <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:
   {
   
thrownew Exception("未定义的编码格式"));
   }
   }
   }
   
/// <summary>
   
/// 数据编码
   
/// </summary>
   
/// <param name="datagram">需要编码的报文</param>
   
/// <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:
   {
   
thrownew Exception("未定义的编码格式"));
   }
   }
   }
  }
  
/// <summary>
  
/// 数据报文分析器,通过分析接收到的原始数据,得到完整的数据报文.
  
/// 继承该类可以实现自己的报文解析方法.
  
/// 通常的报文识别方法包括:固定长度,长度标记,标记符等方法
  
/// 本类的现实的是标记符的方法,你可以在继承类中实现其他的方法
  
/// </summary>
  public class DatagramResolver
  {
   
/// <summary>
   
/// 报文结束标记
   
/// </summary>
   private string endTag;
   
/// <summary>
   
/// 返回结束标记
   
/// </summary>
   string EndTag
   {
   
get
   {
   
return endTag;
   }
   }
   
/// <summary>
   
/// 受保护的默认构造函数,提供给继承类使用
   
/// </summary>
   protected DatagramResolver()
   {
   }
   
/// <summary>
   
/// 构造函数
   
/// </summary>
   
/// <param name="endTag">报文结束标记</param>
   public DatagramResolver(string endTag)
   {
   
if(endTag == null)
   {
   
throw (new ArgumentNullException("结束标记不能为null"));
   }
   
if(endTag == "")
   {
   
throw (new ArgumentException("结束标记符号不能为空字符串"));
   }
   
this.endTag = endTag;
   }
   
/// <summary>
   
/// 解析报文
   
/// </summary>
   
/// <param name="rawDatagram">原始数据,返回未使用的报文片断,
   
/// 该片断会保存在Session的Datagram对象中</param>
   
/// <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;
   }
  }
  
/// <summary>
  
/// 客户端与服务器之间的会话类
  
/// 
  
/// 版本: 1.1
  
/// 替换版本: 1.0
  
/// 
  
/// 说明:
  
/// 会话类包含远程通讯端的状态,这些状态包括Socket,报文内容,
  
/// 客户端退出的类型(正常关闭,强制退出两种类型)
  
/// </summary>
  public class Session:ICloneable
  {
   
#region 字段
   
/// <summary>
   
/// 会话ID
   
/// </summary>
   private SessionId _id;
   
/// <summary>
   
/// 客户端发送到服务器的报文
   
/// 注意:在有些情况下报文可能只是报文的片断而不完整
   
/// </summary>
   private string _datagram;
   
   
/// <summary>
   
/// 客户端的Socket
   
/// </summary>
   private Socket _cliSock;
   
/// <summary>
   
/// 客户端的退出类型
   
/// </summary>
   private ExitType _exitType;
   
/// <summary>
   
/// 退出类型枚举
   
/// </summary>
   public enum ExitType
   {
   NormalExit ,
   ExceptionExit
   };
   
#endregion
   
#region 属性
   
/// <summary>
   
/// 返回会话的ID
   
/// </summary>
   public SessionId ID
   {
   
get
   {
   
return _id;
   }
   }
   
/// <summary>
   
/// 存取会话的报文
   
/// </summary>
   public string Datagram
   {
   
get
   {
   
return _datagram;
   }
   
set
   {
   _datagram 
= value;
   }
   }
   
   
/// <summary>
   
/// 获得与客户端会话关联的Socket对象
   
/// </summary>
   public Socket ClientSocket
   {
   
get
   {
   
return _cliSock;
   }
   }
   
/// <summary>
   
/// 存取客户端的退出方式
   
/// </summary>
   public ExitType TypeOfExit
   {
   
get
   {
   
return _exitType;
   }
   
set
   {
   _exitType 
= value;
   }
   }
   
#endregion
   
#region 方法
   
/// <summary>
   
/// 使用Socket对象的Handle值作为HashCode,它具有良好的线性特征.
   
/// </summary>
   
/// <returns></returns>
   public override int GetHashCode()
   {
   
return (int)_cliSock.Handle;
   }
   
/// <summary>
   
/// 返回两个Session是否代表同一个客户端
   
/// </summary>
   
/// <param name="obj"></param>
   
/// <returns></returns>
   public override bool Equals(object obj)
   {
   Session rightObj 
= (Session)obj;
   
   
return (int)_cliSock.Handle == (int)rightObj.ClientSocket.Handle;
   }
   
/// <summary>
   
/// 重载ToString()方法,返回Session对象的特征
   
/// </summary>
   
/// <returns></returns>
   public override string ToString()
   {
   
string result = string.Format("Session:{0},IP:{1}",
   _id,_cliSock.RemoteEndPoint.ToString());
   
//result.C
   return result;
   }
   
/// <summary>
   
/// 构造函数
   
/// </summary>
   
/// <param name="cliSock">会话使用的Socket连接</param>
   public Session( Socket cliSock)
   {
   Debug.Assert( cliSock 
!=null );
   _cliSock 
= cliSock;
   _id 
= new SessionId( (int)cliSock.Handle);
   }
   
/// <summary>
   
/// 关闭会话
   
/// </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
  }
  
/// <summary>
  
/// 唯一的标志一个Session,辅助Session对象在Hash表中完成特定功能
  
/// </summary>
  public class SessionId
  {
   
/// <summary>
   
/// 与Session对象的Socket对象的Handle值相同,必须用这个值来初始化它
   
/// </summary>
   private int _id;
   
/// <summary>
   
/// 返回ID值
   
/// </summary>
   public int ID
   {
   
get
   {
   
return _id;
   }
   }
   
/// <summary>
   
/// 构造函数
   
/// </summary>
   
/// <param name="id">Socket的Handle值</param>
   public SessionId(int id)
   {
   _id 
= id;
   }
   
/// <summary>
   
/// 重载.为了符合Hashtable键值特征
   
/// </summary>
   
/// <param name="obj"></param>
   
/// <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;
   }
   
   }
   
/// <summary>
   
/// 重载.为了符合Hashtable键值特征
   
/// </summary>
   
/// <returns></returns>
   public override int GetHashCode()
   {
   
return _id;
   }
   
/// <summary>
   
/// 重载,为了方便显示输出
   
/// </summary>
   
/// <returns></returns>
   public override string ToString()
   {
   
return _id.ToString ();
   }
  }
  
/// <summary>
  
/// 服务器程序的事件参数,包含了激发该事件的会话对象
  
/// </summary>
  public class NetEventArgs:EventArgs
  {
   
#region 字段
   
/// <summary>
   
/// 客户端与服务器之间的会话
   
/// </summary>
   private Session _client;
   
#endregion 
   
#region 构造函数
   
/// <summary>
   
/// 构造函数
   
/// </summary>
   
/// <param name="client">客户端会话</param>
   public NetEventArgs(Session client)
   {
   
ifnull == client)
   {
   
throw(new ArgumentNullException());
   }
   _client 
= client;
   }
   
#endregion 
   
#region 属性
   
   
/// <summary>
   
/// 获得激发该事件的会话对象
   
/// </summary>
   public Session Client
   {
   
get
   {
   
return _client;
   }
   
   }
   
#endregion 
   
  }
  }
posted @ 2009-10-15 08:58  虾吵吵  阅读(904)  评论(0编辑  收藏  举报