[转]C#.net同步异步SOCKET通讯和多线程总结

//////////////////////////////////////////////////////////////////////////////////////////
   /*
  标题:在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: [email protected]
  
///  创建日期: 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(
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  
   
  }
  } 

你可能感兴趣的:(socket)