一个.net客户端通讯框架的设计(三)---设计IO连接

 IAsyncIOService(对应代码里的IAsyncConnector)

这是一个异步IO服务接口,含有一下几个方法。

 

IAsyncConnector
public   interface  IAsyncConnector : IDisposable
    {
        
void  Connect(EndPoint endPoint);
        ConnectionStatus ConnectionStatus { 
get ; }
        
void  Send < T > (T msg);
        
void  Close();
        
void  Close(Boolean isImmediately);
        
void  Abort();
    }

 

 

异步模型

win32支持多种异步I/O模型,比如从最简单的select到复杂但高效的IOCP,.NET将这些底层进行了很好的封装,为开发人员提供了非常方便好用异步API。对于.NET上异步Socket的使用这里就不复述了,大家可以参阅MSDN上的相关内容。

在使用异步I/O的时候有些地方需要注意:一是网络上每次传来的数据并不是我们想读多少就传多少,并且不同于阻塞I/O编程,我们read 10个字节的数据,只有读完10个字节后才会进行下一步,对于.NET异步编程,,我们要自己去计算每个消息的完整性并记录未读完的数据以防止连包、粘包的发生。二是SocketAsyncEventArgs对象的复用性,我们需要适当使用池化来减少频繁的新对象创建。其实这个框架稍加改动既是一个服务器框架。

池化SocketAsyncEventArgs

.NET的异步Socket API需要频繁使用SocketAsyncEventArgs实例,我们需要设计一个池来存储、提供SocketAsyncEventArgs实例的复用,GenericPool<T>是我写的一个简单的泛型池实现。这里有个细节需要注意,每个对象入池和从池中去除前都可能需要一些清理/初始化之类的操作,一种方案是让每个池化的类型都实现一个接口,但是这种方式过于暴力和死板,我们定义一个IPoolAspectHandler<T>来解除Pool和池化对象间的耦合性。

public   interface  IPoolAspectHandler < T >
    {
        
void  OnInitialize(T obj);
        
void  OnPut(T obj);
        
void  OnResolve(T obj);
        
void  OnClear(T obj);
    }
 

 

 
   
PoolAspectHandlerAdapter
public   class  PoolAspectHandlerAdapter < T >  : IPoolAspectHandler < T >
    {
        
#region  IPoolAspectHandler<T> Members
        
public   void  OnInitialize(T obj)
        {
 
        }
 
        
public   void  OnPut(T obj)
        {
            
        }
 
        
public   void  OnResolve(T obj)
        {
            
        }
 
        
public   void  OnClear(T obj)
        {
 
        
        }
        
#endregion
    }

 

 
   
SocketRecvEventArgsAspectHandler
class  SocketRecvEventArgsAspectHandler : IPoolAspectHandler < SocketAsyncEventArgs >
    {
        
public  Action < SocketAsyncEventArgs >  OnInitializeEvent;
        
public  Action < SocketAsyncEventArgs >  OnClearupEventReference;
        
#region  IPoolAspectHandler<SocketAsyncEventArgs> Members
        
private   static   readonly   int  RECV_BUFFER_SIZE  =   10 ;
        
public   void  OnInitialize(SocketAsyncEventArgs obj)
        {
            
if (OnInitializeEvent  !=   null )
                OnInitializeEvent(obj);
            var buffer 
=  FastBuffer.Allocate( 512 );
            buffer.AutoExtend 
=   true ;
            var bufferStatus 
=   new  BufferStatus(RECV_BUFFER_SIZE)
            {
                Buffer 
=  buffer
            };
            obj.UserToken 
=  bufferStatus;
 
            var recvBuffer 
=   new   byte [RECV_BUFFER_SIZE];
            obj.SetBuffer(recvBuffer, 
0 , recvBuffer.Length);
        }
 
        
public   void  OnPut(SocketAsyncEventArgs obj)
        {
            
        }
 
        
public   void  OnResolve(SocketAsyncEventArgs obj)
        {
            
        }
 
        
public   void  OnClear(SocketAsyncEventArgs obj)
        {
            obj.BufferList 
=   null ;
            obj.UserToken 
=   null ;
        }
        
#endregion
    }

 

 
   
SocketSendEventArgsAspectHandler
class  SocketSendEventArgsAspectHandler : IPoolAspectHandler < SocketAsyncEventArgs >
    {
        
public  Action < SocketAsyncEventArgs >  OnInitializeEvent;
        
public  Action < SocketAsyncEventArgs >  OnClearupEventReference;
        
#region  IPoolAspectHandler<SocketAsyncEventArgs> Members
        
public   void  OnInitialize(SocketAsyncEventArgs obj)
        {
            
if (OnInitializeEvent  !=   null )
                OnInitializeEvent(obj);
        }
 
        
public   void  OnPut(SocketAsyncEventArgs obj)
        {
            obj.UserToken 
=   null ;
        }
 
        
public   void  OnResolve(SocketAsyncEventArgs obj)
        {
            
        }
 
        
public   void  OnClear(SocketAsyncEventArgs obj)
        {
            obj.SetBuffer(
0 0 );
            obj.BufferList 
=   null ;
        }
        
#endregion
    }

 

 

GenericPool
public   class  GenericPool < T >  
    { 
        
public   static   readonly   int  DEFAULT_MAXSIZE  =   1000
        
private  ConcurrentQueue < T >  _store  =   new  ConcurrentQueue < T > (); 
        
private  IPoolAspectHandler < T >  _aspectHandler; 
        
private   int  _maxSize  =  DEFAULT_MAXSIZE; 
        
public   static   readonly  GenericPool < T >  Instance; 
        
static  GenericPool() 
        { 
            Instance 
=   new  GenericPool < T > (); 
        } 
        
public  GenericPool() 
            : 
this (DEFAULT_MAXSIZE,  new  PoolAspectHandlerAdapter < T > ()) 
        { 
        } 
        
public  GenericPool( int  maxSize, IPoolAspectHandler < T >  aspectHandler) 
        { 
            
this ._maxSize  =  maxSize; 
            
this ._aspectHandler  =  aspectHandler; 
        } 
        
public  T Resolve() 
        { 
            T result 
=   default (T); 
            _store.TryDequeue(
out  result); 
            
if  (result  ==   null
            { 
                
lock  (_store) 
                { 
                    _store.TryDequeue(
out  result); 
                    
if  (result  ==   null
                    { 
                        var obj 
=  Activator.CreateInstance < T > (); 
                        
this ._aspectHandler.OnInitialize(obj); 
                        
if  (Put(obj)) 
                            _store.TryDequeue(
out  result); 
                        
else  
                            
throw   new  InvalidOperationException( " 池已满 " ); 
                    } 
                } 
            } 
            
return  result; 
        } 
        
public   bool  Put(T obj) 
        { 
            
if  (_store.Count  <  _maxSize) 
            { 
                
this ._aspectHandler.OnPut(obj); 
                _store.Enqueue(obj); 
                
return   true
            } 
            
else  
                
return   false
        } 
        
public   void  Empty() 
        { 
            
while  ( ! _store.IsEmpty) 
            { 
                var obj 
=  Resolve(); 
                
this ._aspectHandler.OnClear(obj); 
            } 
        } 
        
public   int  Count 
        { 
            
get  
            { 
                
return   this ._store.Count; 
            } 
        } 
        
public   void  Clear(T obj) 
        { 
            
this ._aspectHandler.OnClear(obj); 
        } 
    }

 

 

IDecoder和IEncoder

每当从网络上获取数据时,都将数据与上一次接消息残留的数据进行拼接并交由IDecode进行处理。通过IDecode接口的两个方法:Decodable和Decode,Decodable判断数据是否完整,返回3种枚举值(OK,NEED_DATA,BAD_DATA),如果读完或者拼接成了一个完整的数据则返回OK,如果缺少数据返回NEED_DATA,如果数据有错误返回BAD_DATA。

IEncoder<T>的作用是将类型为T的对象按照协议序编码为为byte[]数据。

 
   
public   interface  IDecoder
    {
        DecodeResult Decodable(
byte [] data);
        
void  Decode( byte [] data,  out   int  usedSize,  out   object  msg);
    }

 

 
   
public   enum  DecodeResult
    {
        OK, 
        NOT_OK, 
        NEED_DATA
    }

 

 
   
public   interface  IEncoder < T >
    {
        
byte [] Encode(T msg);
    }

 

IoHandler

IoHandler接口有六个方法,SessionOpened,SessionCreated,MessageSent,MessageReceived,ExceptionCaught,SessionClosed。
分别对应连接打开、创建、发送、接受、异常、关闭六种情况。我们可以再SessionCreated中队连接进行安全性审查或是初始化session。
在MessageReceived中进行业务处理,在SessionClosed中持久化用户的会话数据。
 
   
IoHandler
public   interface  IoHandler
    {
        
void  SessionOpened();
        
void  SessionCreated();
        
void  MessageSent( object  msg);
        
void  MessageReceived( object  msg);
        
void  ExceptionCaught();
        
void  SessionClosed();
    }

对于丢包、粘包、数据拼接的处理

这就是为什么我们需要消息头首先告诉我们整个消息的长度是多少的原因。我们知道了消息长度,就可以判断第一次收到的消息是否完整或者包含写一条消息的数据。将多余的数据存在一个FastBuffer中,在下一次接受到数据时将新数据追加到这个FashBuffer中,再提交给IDecode.Decodable处理即可。

我们来看其完整的实现:

AsyncConnector
public   class  AsyncConnector : IAsyncConnector
    {
        
#region  async sockets
        
private  Socket _socket;
        
#endregion
        
#region  reused socketasynceventargs
        
private  GenericPool < SocketAsyncEventArgs >  _asyncSendArgsPool  =   new  GenericPool < SocketAsyncEventArgs > ();
        
private  GenericPool < SocketAsyncEventArgs >  _asyncRecvArgsPool  =   new  GenericPool < SocketAsyncEventArgs > ();
        
#endregion
        
#region  fields
        
// private IoSession session = new IoSession();
         private  ConnectionStatus _connectionStatus  =  ConnectionStatus.Unused;
        
#endregion
        
#region  ctors
        
public  AsyncConnector()
        {
            var sendAspectHandler 
=   new  SocketSendEventArgsAspectHandler();
            sendAspectHandler.OnClearupEventReference 
=  (p)  =>
                {
                    p.Completed 
-=  _socketArgs_Completed;
                };
            _asyncSendArgsPool
                
=   new  GenericPool < SocketAsyncEventArgs > (GenericPool < SocketAsyncEventArgs > .DEFAULT_MAXSIZE, sendAspectHandler);
 
            var recvAspectHandler 
=   new  SocketRecvEventArgsAspectHandler();
            recvAspectHandler.OnInitializeEvent 
+=  (p)  =>
                p.Completed 
+=  _socketArgs_Completed;
            recvAspectHandler.OnClearupEventReference 
=  (p)  =>
                p.Completed 
-=  _socketArgs_Completed;
            _asyncRecvArgsPool 
                
=   new  GenericPool < SocketAsyncEventArgs > (GenericPool  < SocketAsyncEventArgs > .DEFAULT_MAXSIZE, recvAspectHandler);
        }
        
#endregion
        
#region  Property
        
/* public IoSession Session
        {
            get
            {
                return session;
            }
        }
*/
        
public   event  ConnectionStatusChangedEventHandler OnConnectionStatusChanged;
        
public  IoHandler Handler
        {
            
get ;
            
set ;
        }
        
public  IProtocolFactory ProtocolFactory
        {
            
get ;
            
set ;
        }
        
#endregion
        
public   void  Connect(EndPoint endPoint)
        {
            _socket 
=   new  Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            SocketAsyncEventArgs _socketArgs 
=   new  SocketAsyncEventArgs();
            _socketArgs.Completed 
+=   new  EventHandler < SocketAsyncEventArgs > (_socketArgs_Connected);
            _socketArgs.RemoteEndPoint 
=  endPoint;
            ConnectionStatus 
=  IO.ConnectionStatus.Connecting;
            
if  ( ! _socket.ConnectAsync(_socketArgs))
            {
                _socketArgs_Connected(_socket, _socketArgs);
            }
        }
 
        
public  ConnectionStatus ConnectionStatus
        {
            
get
            {
                
return  _connectionStatus;
            }
            
set
            {
                
if  (value  ==  _connectionStatus)
                    
return ;
                
if  (OnConnectionStatusChanged  !=   null )
                    OnConnectionStatusChanged(_connectionStatus, value);
                _connectionStatus 
=  value;
            }
        }
 
        
public   void  Close()
        {
            _socket.Close();
            ConnectionStatus 
=  IO.ConnectionStatus.Closed;
        }
 
        
public   void  Close( bool  isImmediately)
        {
            _socket.Close();
            ConnectionStatus 
=  IO.ConnectionStatus.Closed;
        }
 
        
public   void  Abort()
        {
            _socket.Close();
            ConnectionStatus 
=  IO.ConnectionStatus.Closed;
        }
        
public   void  Dispose()
        {
            
if  (_socket  !=   null )
            {
                
if  (_socket.Connected)
                {
                    _socket.Shutdown(SocketShutdown.Both);
                    _socket.Close();
                    _socket.Dispose();
                    _socket 
=   null ;
                    ConnectionStatus 
=  IO.ConnectionStatus.Closed;
                }
                _asyncRecvArgsPool.Empty();
                _asyncSendArgsPool.Empty();
            }
            
// session.Dispose();
        }
        
public   void  Send < T > (T msg)
        {
            var args 
=  _asyncSendArgsPool.Resolve();
            
byte [] ret  =  ProtocolFactory.GetEncoder < T > ().Encode(msg);
            args.SetBuffer(ret, 
0 , ret.Length);
            _socket.SendAsync(args);
        }
        
private   void  _socketArgs_Connected( object  sender, SocketAsyncEventArgs args)
        {
            args.Completed 
-=  _socketArgs_Connected;
            
if  (args.SocketError  ==  SocketError.Success)
            {
                ConnectionStatus 
=  IO.ConnectionStatus.Connected;
                var recvArgs 
=  _asyncRecvArgsPool.Resolve();
                
if  ( ! _socket.ReceiveAsync(recvArgs))
                    _socketArgs_Received(sender, recvArgs);
            }
            
else
            {
                ConnectionStatus 
=  IO.ConnectionStatus.Unavailable;
            }
        }
        
private   void  _socketArgs_Completed( object  sender, SocketAsyncEventArgs args)
        {
            
switch (args.LastOperation)
            {
                
case  SocketAsyncOperation.Send:
                    _socketArgs_Sended(sender, args);
                    
break ;
                
case  SocketAsyncOperation.Receive:
                    _socketArgs_Received(sender, args);
                    
break ;
            }
        }
        
private   void  _socketArgs_Received( object  sender, SocketAsyncEventArgs args)
        {
            var socket 
=  sender  as  Socket;
            
if  (args.SocketError  ==  SocketError.Success)
            {
                
// check if the buffer is full used(ByteTransfered, OffSet)
                var bufferStatus  =  args.UserToken  as  BufferStatus;
                bufferStatus.Buffer.Append(args.Buffer, args.Offset
/* bufferStatus.ReadOffset */ , args.BytesTransferred);
                bufferStatus.IncreaseTransfered(args.BytesTransferred);
                var decoder 
=  ProtocolFactory.GetDecoder();
                var data 
=  bufferStatus.Buffer.copyAvaliableBytes();
                var result 
=  decoder.Decodable(data);
                
switch  (result)
                {
                    
case  DecodeResult.NEED_DATA:
                        socket.ReceiveAsync(args);
                        
break ;
                    
case  DecodeResult.NOT_OK:
                        
throw   new  BadImageFormatException();
                    
case  DecodeResult.OK:
                        
int  usedSize;
                        
object  netMsg  =   null ;
                        decoder.Decode(data, 
out  usedSize,  out  netMsg);
                        ThreadPool.QueueUserWorkItem(Handler.MessageReceived, netMsg);
                        bufferStatus.Buffer.Reset();
                        bufferStatus.Buffer.Append(data, usedSize, data.Length 
-  usedSize);
                        socket.ReceiveAsync(args);
                        
break ;
                }
            }
        }
        
private   void  _socketArgs_Sended( object  sender, SocketAsyncEventArgs args)
        {
            
if  (args.SocketError  ==  SocketError.Success)
            {
            }
            
else
            {
            }
            _asyncSendArgsPool.Put(args);
        }
    }

 

代码下载 

http://files.cnblogs.com/wJiang/ClientConnLib.rar

 

你可能感兴趣的:(.net)