.net 3.5的Socket异步完成端口

根据.net 3.5中的异步Socket完成的一个模块,用队列来处理tcp数据包的粘包问题,用事件将数据包通知给上层调用;

代码写的不是很好,发出来希望和大家共同交流学习。

 因为是实际项目中用的模块,所以有些地方是按照项目需求做的,大体的结构还是没问题的,除了代码写的差点外!!!

一,主要类,进行异步Socket的基本处理,.net3.5中的Socket完成端口模型的实现

Net_Server
     public   class  NET_Server : IDisposable
    {
        
private   bool  IsDisponse = false ;
        
~ NET_Server()
        {
            
this .Dispose( false );
        }
        
public   void  Dispose()
        {
            Dispose(
true );
            GC.SuppressFinalize(
this );   // 请求系统不要调用指定对象的终结器
        }
        
protected   virtual   void  Dispose( bool  disponse)
        {
            
if  ( ! IsDisponse)
            {
                
try  {
                    
this ._ServerRun  =   false ;
                    
foreach  (DictionaryEntry client  in  ClientsTable)
                    {
                        ((Socket)client.Value).Shutdown(SocketShutdown.Both);
                    }
                    
this .Server.Close();
                    
this .ClientsTable.Clear();
                    clientpool.poolClear();
                }
                
catch  { }
                IsDisponse 
=   true ;
            }
        }

        
private  Socket Server  =   null ;
        
private  BufferManager Buffer;
        
private  SocketAsyncEventArgsPool RWpool;   // .net 3.5异步socket的关键

        
private  IPEndPoint _iep;

        
private   int  Max_Recv_Buff_Size  =   1024 ;    
        
private   int  Max_Connected_Count  =   512 ;


        
private  halfbuffpool clientpool;
        
public  NET_Server( string  ipsrc, int  port)
        {
            _iep 
=   new  IPEndPoint(IPAddress.Parse(ipsrc), port);
            
this .ClientsTable  =   new  Hashtable();
            clientpool 
=   new  halfbuffpool();    // 管理客户端的一个数组对象

        }
        
public  NET_Server( string  ipsrc,  int  port,  int  nconn,  int  nbuff)
        {
            
// 初始化
             this .Max_Recv_Buff_Size  =  nbuff;
            
this .Max_Connected_Count  =  nconn;
            _iep 
=   new  IPEndPoint(IPAddress.Parse(ipsrc), port);
            
this .ClientsTable  =   new  Hashtable();
            clientpool 
=   new  halfbuffpool();
        }

        
// 数据收发的超时时间
         private   int  _RecvOutTime  =   3000 ;
        
public   int  RecvTimeout {  get  {  return  _RecvOutTime; }  set  { _RecvOutTime  =  value; } }
        
private   int  _SendTimeout  =   1000 ;
        
public   int  SendTimeout {  get  {  return  _SendTimeout; }  set  { _SendTimeout  =  value; } }


        
public   event  ServerEventHandler Notification;
        
// 开启服务器
         public   bool  StartServer()
        {
            
this ._ServerRun  =   true ;
            
this .Server  =   new  Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            
bool  result  =   false ;
            
if  (IsDisponse)
            {
                
throw   new  ObjectDisposedException( " Server is Disponse! " );
            }
            
try
            {
                
//  设定与服务器相关参数
                 this .Server.Bind( this ._iep);
                
this .Server.Listen( 5 );
                
this .Server.SendTimeout  =   this ._SendTimeout;
                
this .Server.ReceiveTimeout  =   this ._RecvOutTime;
                result 
=   true ;
                
// 创建并初始化缓冲区管理对象
                Buffer  =   new  BufferManager( this .Max_Connected_Count * this .Max_Recv_Buff_Size,  this .Max_Recv_Buff_Size);
                Buffer.Inint();
                
// 构造SocketAsyncEventArgs池
                RWpool  =   new  SocketAsyncEventArgsPool( this .Max_Connected_Count);
                
for  ( int  i  =   0 ; i  <   this .Max_Connected_Count;i ++ )
                {
                    SocketAsyncEventArgs asyniar 
=   new  SocketAsyncEventArgs();
                    asyniar.Completed 
+=   new  EventHandler < SocketAsyncEventArgs > (Asyn_Completed);
                    RWpool.Push(asyniar);
                }
                Console.WriteLine(
" Begin Listen.... " );
                Accept();  
            }
            
catch  (Exception ex) { Notification( null new  ServerEventArgs(ArgsType.EXCEPTION, ex.Message, null ));}
            
return  result;
        }
        
private   void  Accept()   // 开始接受连接
        {
            
if  ( ! _ServerRun)  return ;
            
if  (RWpool.Count  >   0
            {
                SocketAsyncEventArgs asyniar 
=  RWpool.Pop();
                
if  ( ! Server.AcceptAsync(asyniar))  // 弹出一个SocketAsyncEventArgs对象开始AcceptAsyn
                {
                    BeginAccep(asyniar);      
                    
// 如果I/O挂起等待异步则触发AcceptAsyn_Asyn_Completed事件
                    
// 此时I/O操作同步完成,不会触发Asyn_Completed事件,所以指定BeginAccept()方法
                }   
            }
            
else
            {
                Console.WriteLine(
" Had Max Connected! " );
            }
        }

        
private  Hashtable ClientsTable;
        
private   void  BeginAccep(SocketAsyncEventArgs e)
        {
            
try
            {
                
if  (e.SocketError  ==  SocketError.Success)
                {
                    
string  clientip = string .Format( " {0} " ,((IPEndPoint)e.AcceptSocket.RemoteEndPoint).Address);
                    
if  (ClientsTable.Contains(clientip))
                    {
                        e.AcceptSocket.Shutdown(SocketShutdown.Both);
                        e.AcceptSocket 
=   null ;    // 保持SocketAsynEventArgs池 的堆栈平衡
                        RWpool.Push(e);
                        
return ;
                    }
                    
else
                        
this .ClientsTable.Add(clientip, e.AcceptSocket);
                    Notification(
null new  ServerEventArgs(ArgsType.CLIENTCONN, clientip, null ));
                    
// 查找客户如果不存在,则添加一个新客户
                    halfbuff client  =  clientpool.GetClient((IPEndPoint)e.AcceptSocket.RemoteEndPoint);
                    
if  (client  ==   null )
                    {
                        client 
=   new  halfbuff((IPEndPoint)e.AcceptSocket.RemoteEndPoint);
                        client.HalfEvent 
+=   new  EventHandler < HaflBuffEventArgs > (OnHalfEvent);
                        clientpool.poolAdd(client);
                    }
                    clientpool.poolAdd(client);
                    
// 接受一个连接,发送欢迎信息
                    e.AcceptSocket.Send(Encoding.ASCII.GetBytes( " send " ));
                    
if  (Buffer.SetBuffer(e))
                    {
                        
if  ( ! e.AcceptSocket.ReceiveAsync(e))   // 是否触发 Asyn_Commpleted事件
                        {
                            BeginReceive(e);
                        }
                    }
                }
                
else
                {
                    e.AcceptSocket 
=   null ;    // 保持SocketAsynEventArgs池 的堆栈平衡
                    RWpool.Push(e);
                    Console.WriteLine(
" Not Accept! " );
                }
            }
               
catch (Exception ex){
                   e.AcceptSocket 
=   null ;    // 保持SocketAsynEventArgs池 的堆栈平衡
                   RWpool.Push(e);
                   Notification(
null new  ServerEventArgs(ArgsType.EXCEPTION, ex.Message,  null ));
               }
            
finally
            {
                Accept();
            }

        }
        
private   void  OnHalfEvent( object  sender,HaflBuffEventArgs e)
        {
            Notification(
null new  ServerEventArgs(ArgsType.RECEIVE,  "" , e.BUFF));
        }
        
private   bool  _debug  =   false ;
        
public   bool  Debug {  get  {  return  _debug; }  set  { _debug  =  value; } }
        
private   void  BeginReceive(SocketAsyncEventArgs e)
        {
            halfbuff client 
=  clientpool.GetClient((IPEndPoint)e.AcceptSocket.RemoteEndPoint);
            
if  (e.SocketError  ==  SocketError.Success  &&  e.BytesTransferred  >   0 )
            {
                
byte [] data  =   new   byte [e.BytesTransferred];
                Array.Copy(e.Buffer, e.Offset, data, 
0 , data.Length); // 从e.Buffer块中复制数据出来,保证它可重用
                 if (_debug)
                    Notification(
null new  ServerEventArgs(ArgsType.DEBUG,  " debug " ,data));
                
if  (data.Length > 3 )   // 排除心跳包 01
                {
                    
if  (client  !=   null )
                        client.pool.Enqueue(data);
                }
                
if  ( ! e.AcceptSocket.ReceiveAsync(e))
                {
                    BeginReceive(e); 
//
                }
            }
            
else
            {
                
string  clientip = string .Format( " {0} " ,((IPEndPoint)e.AcceptSocket.RemoteEndPoint).Address);
                
this .ClientsTable.Remove(clientip);
                Console.WriteLine(
" A Client Disconnected,{0} " ,e.AcceptSocket.RemoteEndPoint);
                Notification(
null new  ServerEventArgs(ArgsType.CLIENTDISCONN, clientip, null ));
                
// 查找客户存在,则删除客户
                 if  (client != null )
                {
                    
// e.AcceptSocket.Shutdown(SocketShutdown.Both);
                    clientpool.poolDel(client);
                }
                e.AcceptSocket 
=   null ;
                Buffer.FreeBuffer(e);
                RWpool.Push(e);
                
if  (RWpool.Count  ==   1 )
                {
                    Accept();
                }
            }

        }
       
        
private   void  Asyn_Completed( object  sender, SocketAsyncEventArgs e)
        {
            
switch  (e.LastOperation)
            {
                
case  SocketAsyncOperation.Accept:
                    BeginAccep(e);
                    
break ;
                
case  SocketAsyncOperation.Receive:
                    BeginReceive(e);
                    
break ;

            }

        }
        
private   bool  _ServerRun  =   true ;
        
public   bool  ServerRun {  get  {  return  _ServerRun; }  set  { _ServerRun  =  value; } }
        
public   void  StopServer()
        {
            
this ._ServerRun  =   false ;
            
foreach  (DictionaryEntry client  in  ClientsTable)
            {
                ((Socket)client.Value).Shutdown(SocketShutdown.Both);
            }
            
if ( this .Server != null )
            
this .Server.Close();
            
this .ClientsTable.Clear();
            Console.WriteLine(
" The Server Stoped! " );
        }
}

 

 

二,缓冲区管理类,动态管理缓冲区的

BufferManager
internal   sealed   class  BufferManager
    {
        
private  Byte[] buffer;
        
private  Int32 bufferSize;
        
private  Int32 numSize;
        
private  Int32 currentIndex;
        
private  Stack < Int32 >  freeIndexPool;

        
public  BufferManager(Int32 numsize, Int32 buffersize)
        {
            
this .numSize  =  numsize;
            
this .bufferSize  =  buffersize;
        }

        
public   void  Inint()
        {
            buffer 
=   new   byte [numSize];
            freeIndexPool 
=   new  Stack < int > (numSize  /  bufferSize);
        }

        
internal   void  FreeBuffer(SocketAsyncEventArgs args)
        {
            freeIndexPool.Push(args.Offset);
            args.SetBuffer(
null 0 0 );
        }
        
internal  Boolean SetBuffer(SocketAsyncEventArgs args)
        {
            
if  ( this .freeIndexPool.Count  >   0 )
            {
                args.SetBuffer(
this .buffer,  this .freeIndexPool.Pop(),  this .bufferSize);
            }
            
else
            {
                
if  (( this .numSize  -   this .bufferSize)  <   this .currentIndex)
                {
                    
return   false ;
                }
                args.SetBuffer(
this .buffer,  this .currentIndex,  this .bufferSize);
                
this .currentIndex  +=   this .bufferSize;
            }
            
return   true ;
        }
        
public   void  Clear()
        {
            
lock ( this )
            {
                freeIndexPool.Clear();
                buffer 
=   null ;
            }
        }
    }

 

 三,是.net 3.5异步socket中对SocketAsyncEventArgs 对象进行管理的类

SocketAsyncEventArgsPool
  ///   <summary>
    
///  Based on example from  http://msdn2.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.socketasynceventargs.aspx
    
///  Represents a collection of reusable SocketAsyncEventArgs objects.  
    
///   </summary>
     internal   sealed   class  SocketAsyncEventArgsPool
    {
        
///   <summary>
        
///  SocketAsyncEventArgs栈
        
///   </summary>
        Stack < SocketAsyncEventArgs >  pool;

        
///   <summary>
        
///  初始化SocketAsyncEventArgs池
        
///   </summary>
        
///   <param name="capacity"> 最大可能使用的SocketAsyncEventArgs对象. </param>
         internal  SocketAsyncEventArgsPool(Int32 capacity)
        {
            
this .pool  =   new  Stack < SocketAsyncEventArgs > (capacity);
        }

        
///   <summary>
        
///  返回SocketAsyncEventArgs池中的 数量
        
///   </summary>
         internal  Int32 Count
        {
            
get  {  return   this .pool.Count; }
        }

        
///   <summary>
        
///  弹出一个SocketAsyncEventArgs
        
///   </summary>
        
///   <returns> SocketAsyncEventArgs removed from the pool. </returns>
         internal  SocketAsyncEventArgs Pop()
        {
            
lock  ( this .pool)
            {
                
return   this .pool.Pop();
            }
        }

        
///   <summary>
        
///  添加一个 SocketAsyncEventArgs
        
///   </summary>
        
///   <param name="item"> SocketAsyncEventArgs instance to add to the pool. </param>
         internal   void  Push(SocketAsyncEventArgs item)
        {
            
if  (item  ==   null )
            {
                
throw   new  ArgumentNullException( " Items added to a SocketAsyncEventArgsPool cannot be null " );
            }
            
lock  ( this .pool)
            {
                
this .pool.Push(item);
            }
        }
    }

 

 四,自定义的一个处理粘包和客户端管理的类

      其中 tcp的粘包处理流程是:

      Socket异步接收数据包-->将数据包添加到处理队列-->单独线程读取队列-->将数据包bytes的十六进制转为字符串模式-->根据数据包分隔符的字符对数据包进行分割处理

      利用包头和包尾的标识符重新组合包-->用事件通知给上层调用

      需要注意的地方:1, 客户端管理类的实现,每个客户连接都需要一个队列和一个处理包的线程;

                            2, C#中很方便的将包转为十六进制字符串形式进行重新的拆包组包,

                            3, 资源清理,即一个客户端断开连接或异常掉线后对它拥有的队列与线程的释放工作

     

  

代码
/* ********************************************************************** */
    
/* 此类设计的目的主要是为了处理半个数据包造成的丢包问题
    /***********************************************************************
*/
    
public   class  HaflBuffEventArgs:EventArgs
    {
        
public  HaflBuffEventArgs( byte [] buff)
        {
            _buff 
=  buff;
        }
        
private   byte [] _buff;
        
public   byte [] BUFF {  get  {  return  _buff; } }
    }
    
public   class  halfbuff
    {
        
public   event  EventHandler < HaflBuffEventArgs >  HalfEvent;
        
public  halfbuff(IPEndPoint iep)
        {
            client
= iep;
            pool 
=   new  Queue < byte [] > ();

            TimerCallback call 
=   new  TimerCallback(TimerCall);
            mutex 
=   new  Mutex();
            timer 
=   new  System.Threading.Timer(call,  null 0 500 );
        }
        
public  IPEndPoint client;
        
public  Queue < byte [] >  pool;
        
public  System.Threading.Timer timer;
        
private  System.Threading.Mutex mutex;
        
private   string  temphead  =   "" , tempend  =   "" , tempall  =   "" ;
        
private   void  TimerCall( object  info)
        {
            mutex.WaitOne();
            HaflBuffEventArgs e;
            
while  (pool.Count  >   0 )
            {
                
byte [] data  =  pool.Dequeue();
                
if  (data.Length  >   180 continue ;   // 如果数据包长度大于 180 则丢弃
                 string  strdata  =  Hex.byteToHexStr(data);
                
string [] sArry  =  Regex.Split(strdata,  " 11FF " );
                
if  (sArry[ 0 !=   "" )
                {
                    temphead 
=  sArry[ 0 ];
                    tempall 
=  tempend  +  temphead;
                    
if  (tempall.Substring( 0 4 ==   " 11FF "   &&  tempall.Substring(tempall.Length  -   4 4 ==   " EEFF " )
                    {
                        e 
=   new  HaflBuffEventArgs(Hex.ConvertHexToBytes(tempall));
                        HalfEvent(
null , e);
                        temphead 
=   "" ;
                        tempend 
=   "" ;
                        tempall 
=   "" ;
                    }
                }
                
if  (sArry.Length  >   1 )
                {
                    
for  ( int  i  =   1 ; i  <  sArry.Length; i ++ )
                    {
                        
if  (sArry[i].Contains( " EEFF " ))
                        {
                            
// 一个完整的包
                            e  =   new  HaflBuffEventArgs(Hex.ConvertHexToBytes( " 11FF "   +  sArry[i]));
                            HalfEvent(
null , e);
                        }
                        
else
                        {
                            
if  (i  ==  sArry.Length  -   1 )
                            {
                                tempend 
=   " 11FF "   +  sArry[i];
                            }
                        }
                    }

                }
            }
            mutex.ReleaseMutex();
        }
    }
    
public   class  halfbuffpool
    {
       
private  ArrayList arry;
       
public  halfbuffpool()
       {
            arry 
=   new  ArrayList();
       }
       
public   void  poolAdd(halfbuff e)
       {
           arry.Add(e);
       }
       
public   void  poolDel(halfbuff e)
       {
            e.timer.Dispose();
            arry.Remove(e);
       }
       
public   void  poolClear()
       {
           arry.Clear();
       }
       
public   int  Count {  get  {  return  arry.Count; } }
       
public  halfbuff  this [ int  index] {  get  {  return  (halfbuff)arry[index]; } }
       
public  halfbuff GetClient(IPEndPoint iep)
       {
           
foreach  (halfbuff e  in  arry)
           {
               
if (e.client == iep)
                   
return  e;
           }
           
return   null ;
       }
    }

 

 

五,自定义的消息,事件,及其他,代码就不贴了,可下载项目文件查看

 

 

 项目下载:/Files/cxwx/NetServers.rar

 

 错误之处还请批评指正!

你可能感兴趣的:(socket)