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

//////////////////////////////////////////////////////////////////////////////////////////
  /*
  标题:在C#中使用异步Socket编程实现TCP网络服务的C/S的通讯构架(一)----基础类库部分
  当看到.NET中TcpListener和TcpClient的时候,我非常高兴,那就是我想要的通讯模式
  但是使用之后发现它们的力量太单薄了,我们需要一个更好的类库来替代它们.
  下面提供了一些类,可以很好的完成Tcp的C/S通讯模式.在本文的第二部分,我将为大家介绍怎么使用它们
  主要通过事件来现实整个的功能:
  服务器的事件包括:
  服务器满
  新客户端连接
  客户端关闭
  接收到数据
   
  客户端使用的事件包括:
  已连接服务器
  接收到数据
  连接关闭
  另外为了灵活的处理需求变化,还提供了编码器和报文解析器的实现方法.
  注意:该类库没有经过严格的测试,如出现Bug,请发送给我,我会觉得你的整个行为是对我的鼓励和支持.
  */
  //////////////////////////////////////////////////////////////////////////////////////////
  /// <summary>
  /// 
  /// 文件名称: TcpCSFramework.cs
  /// 文件ID: 
  /// 编程语言: C#
  /// 文件说明: 提供TCP网络服务的C/S的通讯构架基础类
  /// (使用异步Socket编程实现)
  /// 
  /// 历史修改记录:
  /// 
  /// 时间: 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(this, new 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
   {
   throw( new 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(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);
   }
   /// <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)
   {
   //客户端退出
   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;
   }
   }
   
   }
   /// <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(this, new 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(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
  }
  /// <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:
   {
   throw( new 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:
   {
   throw( new 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)
   {
   if( null == client)
   {
   throw(new ArgumentNullException());
   }
   _client = client;
   }
   #endregion 
   #region 属性
   
   /// <summary>
   /// 获得激发该事件的会话对象
   /// </summary>
   public Session Client
   {
   get
   {
   return _client;
   }
   
   }
   #endregion 
   
  }
  }