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

C# 异步编程async/await 异步这个概念在不同语境下有不同的解释,比如在一个单核CPU里开启两个线程执行两个函数,通常认为这种调用是异步的,但对于CPU来说它是单核不可能同时运行两个函数,不过是由系统调度在不同的时间分片中执行。一般来说,如果两个工作能同时进行,就认为是异步的。在编程中,它通常代表函数的调用可以在不执行完的情况下返回,必要时在完成时回调。有一个概念常常被混淆,多线程和异步。 阅读详情

//////////////////////////////////////////////////////////////////////////////////////////

//http://www.cnblogs.com/llcto/archive/2009/10/15/1583663.html
  /*
  标题:在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 
   
  }
  } 

.net core 下的同步/异步改造实践上篇:同步转异步 目录简短精悍第一式:Task.FromResult低内存消耗第二式:ValueTask 采用async是一场激动人心的旅程,虽然最终的风景美丽宜人,但需要爬过的小山还是比较多的,现在就来看看怎么把现有的项目改造为支持async和await的异步方法吧… 简短精悍第一式:Task.FromResult 此方法创建一个 Task< TResult > 对象,该对象的 Task< ... 阅读详情

相关推荐

C#.net同步异步SOCKET通讯和多线程总结2[参照].pdf

C#.net同步异步SOCKET通讯和多线程总结2[参照].pdf

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

当看到.NET中TcpListener和TcpClient的时候,我非常高兴,那就是我想要的通讯模式 但是使用之后发现它们的力量太单薄了,我们需要一个更好的类库来替代它们. 下面提供了一些类,可以很好的完成Tcp的C/S通讯模式.在本文的第二部分,我将为大家介绍怎么使用它们 主要通过事件来现实整个的功能: 服务器的事件包括: 服务器满 新客户端连接 客户端关闭 接收到数据 客户端使

536

ifix5.8连接ab plc的点-通过rslinx

以ifix连接ab plc的点为例 1.先在rslinx上把plc的点导出为opc server 2.使用opc power tool 作为opc client 去连接 3.保存到项目的PDB文件夹下面,并且以节点名命名。 也可以不以节点名命名,系统自动会生成一个以节点名命名的这种文件,就显得很乱。 同时,项目的PDB目录下面必须还得拷过来一个de...

中国智造2025 4114

c#.net同步异步SOCKET通讯和多线程

c#.net同步异步SOCKET通讯和多线程 2009年12月18日   基于TCP协议的发送和接收端   TCP协议的接收端   using System.Threading ; //使用到线程   using System.IO ; //使用到StreamReader类   int port = 8000; //定义侦听端口号   private Thread thThr...

medrj81y 271

电脑的kernelbase.dll故障怎么处理?kernelbase.dll是什么文件

kernelbase.dll​是一个系统文件,属于Microsoft Windows操作系统的一部分。该DLL(动态链接库)文件包含了许多基本且低级的系统函数,这些函数被Windows上运行的各种程序和进程调用。kernelbase.dll通常与Kernel32.dll紧密相关,它是实现许多Windows API(应用程序编程接口)的封装。这些API涉及到内存管理、输入输出操作、进程和线程管理以及其他许多核心的操作系统功能。

电脑修复君的博客 4534

HBuilderx连接微信开发者工具

系列文章目录 文章目录系列文章目录前言一、环境1.HBuilerx 工具2.微信开发者工具二、关联微信开发者工具1.HBuilderx新建项目2.关联微信开发者工具3.运行微信小程序出现异常解决方法3.最终效果总结 前言 使用HBuilderx工具关联微信开发者工具,进行小程序开发 提示:以下是本篇文章正文内容,下面案例可供参考 一、环境 1.HBuilerx 工具 HBuilderx 2.微信开发者工具 微信开发者工具 二、关联微信开发者工具 1.HBuilderx新建项目 2.关联微信开发者工具

拥有必珍惜 3076

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

同步套接字通信 Socket支持下的网上点对点的通信 服务端实现监听连接,客户端实现发送连接请求,建立连接后进行发送和接收数据的功能 服务器端建立一个socket,设置好本机的ip和监听的端口与socket进行绑定,开始监听连接请求,当接收到连接请求后,发送确认,同客户端建立连接,开始与客户端进行通信。 客户端建立一个socket,设置好服务器端的IP和提供服务的端口,发出连接请求,接...

weixin_34026276的博客 303

Vivado ChipScope Debugging: Overcoming Common DRC Errors in FPGA Design

本文深入解析Vivado ChipScope调试中常见的DRC错误,特别是AVAL-244和AVAL-245等时钟相关问题,提供实用的Tcl命令解决方案和最佳实践。通过将DRC错误降级为警告的技巧,帮助FPGA设计者高效完成调试流程,同时强调何时应保留DRC检查以确保设计可靠性。

weixin_30335575的博客 417

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

原文地址http://www.cnblogs.com/luluping/archive/2010/01/06/1640070.html 同步套接字通信 Socket支持下的网上点对点的通信 服务端实现监听连接,客户端实现发送连接请求,建立连接后进行发送和接收数据的功能 服务器端建立一个socket,设置好本机的ip和监听的端口与socket进行绑定,开始监听连接请求,当接收到连接...

159

状态转移矩阵在离散控制系统中的应用与实现步骤详解

本文深入解析了状态转移矩阵在离散控制系统中的核心作用与实现步骤。通过一个具体实例,详细演示了如何将连续状态空间模型精确离散化,计算出离散系统矩阵A_d和B_d,并分享了采样周期选择、数值计算技巧及嵌入式C语言实现等关键工程经验,为开发高性能数字控制器提供实用指南。

weixin_28454475的博客 382

C++如何实时监测另一个进程是否已经退出?(附源码)

调用WaitForSingleObject实时监测目标进程是否已经退出

dvlinker的技术专栏 8276

[C#通信二]C#.net同步异步SOCKET通讯和多线程总结1(转)

C#.net同步异步SOCKET通讯和多线程总结同步套接字通信Socket支持下的网上点对点的通信服务端实现监听连接,客户端实现发送连接请求,建立连接后进行发送和接收数据的功能服务器端建立一个socket,设置好本机的ip和监听的端口与socket进行绑定,开始监听连接请求,当接收到连接请求后,发送确认,同客户端建立连接,开始与客户端进行通信。客户端建立一个socket,设置好

yagebu1983的专栏 3575

QT应用编程: 编写低功耗BLE蓝牙调试助手(Android系统APP)

一、环境介绍 QT版本: 5.12.6 编译环境: win10 64位 目标系统: Android 完整工程源码下载地址(包含APK文件):https://download.csdn.net/download/xiaolong1126626497/19051696 二、功能介绍 本软件是一款BLE蓝牙串口调试助手,支持常规的发送和接收调试,BLE是低功耗类型蓝牙,在智能家居、物联网领域使用较多。BLE低功耗蓝牙一般不能直接使用手机进行连接,手机蓝牙默认都是工作在传统蓝牙模式,无法直接连接BL..

7661

[C#通信二]C#.net同步异步SOCKET通讯和多线程总结1(转)

[C#通信二]C#.net同步异步SOCKET通讯和多线程总结1(转) 2010年07月30日   C#.net同步异步SOCKET通讯和多线程总结   同步套接字通信   Socket支持下的网上点对点的通信   服务端实现监听连接,客户端实现发送连接请求,建立连接后进行发送和接收数据的功能   服务器端建立一个socket,设置好本机的ip和监听的端口与socket进行绑定,...

xxka87xxka 146

微信小程序:errno: 1500104, errCode: 10008, errMsg: “writeBLECharacteristicValue:fail:system error, status

微信小程序errno: 1500104, errCode: 10008, errMsg: "writeBLECharacteristicValue:fail:system error, status: UNKNOWN (239)"微信小程序调用蓝牙API,wx.writeBLECharacteristicValue()报错,微信小程序蓝牙写入错误,在开发uniapp微信小程序蓝牙通信功能时报错,蓝牙开发设备控制核心api,微信小程序调用蓝牙API “wx.writeBLECharacteristicValu

✅ 精选推荐文章 3931

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

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

同步套接字通信 Socket支持下的网上点对点的通信 服务端实现监听连接,客户端实现发送连接请求,建立连接后进行发送和接收数据的功能 服务器端建立一个socket,设置好本机的ip和监听的端口与socket进行绑定,开始监听连接请求,当接收到连接请求后,发送确认,同客户端建立连接,开始与客户端进行通信。 客户端建立一个socket,设置好服务器端的IP和提供服务的端口,发出连接请求,接...

weixin_30640769的博客 81

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

【转载】[C#通信二]C#.net同步异步SOCKET通讯和多线程总结1(转)

C#.net同步异步SOCKET通讯和多线程总结 同步套接字通信 Socket支持下的网上点对点的通信 服务端实现监听连接,客户端实现发送连接请求,建立连接后进行发送和接收数据的功能 服务器端建立一个socket,设置好本机的ip和监听的端口与socket进行绑定,开始监听连接请求,当接收到连接请求后,发送确认,同客户端建立连接,开始与客户端进行通信。 客户端建立一个socket,设置好...

weixin_30260399的博客 86

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

网络传输之同步异步SOCKET通讯和多线程[转]

网络传输之同步异步SOCKET通讯和多线程[转] 同步套接字通信 Socket支持下的网上点对点的通信 服务端实现监听连接,客户端实现发送连接请求,建立连接后进行发送和接收数据的功能 服务器端建立一个socket,设置好本机的ip和监听的端口与socket进行绑定,开始监听连接请求,当接收到连接请求后,发送确认,同客户端建立连接,开始与客户端进行通信。 客户端建立一个sock...

dengao5267的博客 276
上一篇: 一种自定义网络通信协议
下一篇: Effective Emacs中文版
Open9i
博客等级 码龄16年 14粉丝 43原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值