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

来源:互联网 发布:haproxy nginx 编辑:程序博客网 时间:2024/05/16 19:31

///////////////////////////////////////////////////////////////////////////////////////////*标题:在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(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    }}


原创粉丝点击