一个简易的Socket组件,希望与牛人菜鸟们分享交流

  在这之前几乎没使用过.NET的Socket编程,最近由于工作的关系需要做些客户端与服务器完成TCP通信。自己闭门造成,写了一个类库,初步测试通过,发布出来和大家交流下,也希望在这方面比较牛X的批评指正。

  首先,公司内部基本上所有的TCP协议基本上都是基于一些协议的,无论收或者发,都离不开一个协议包的概念,因此这里定义了一个接口来表示,代码如下:

代码
/// <summary>
    
/// 所有数据包继承该接口
    
/// </summary>
    public interface IPacket
    {
        Byte[] GetBytes();
    }

这里GetBytes()方法用于返回协议中包含的所有字节数组。

 

  接着,定义一个委托用于封装当收到一个完整包后的回调方法,当然还需要一个事件的参数类型:

代码

 

  然后是类库的主要对象,即一个封装了Socket引用的自定义套接字连接,其主要功能是缓存并解析数据,同时通过事件通知,解析数据包的过程中使用了策略模式,因为每个项目有可能会有不同的协议格式,代码如下:

 

代码
public sealed class MFSocket
    {
        
private Socket _socket;

        
/// <summary>
        
/// 异步接收工作区
        
/// </summary>
        private Byte[] _workbuff;

        
/// <summary>
        
/// 未解析数据存放区
        
/// </summary>
        private Byte[] _singlePacket;

        
/// <summary>
        
/// 异步接收参数
        
/// </summary>
        private SocketAsyncEventArgs _receiveArgs;

        
/// <summary>
        
/// 获取或设置数据包的解析策略
        
/// </summary>
        public IPacketLoader ParseStrategy
        {
            
get;
            
set;
        }

        
public bool Connected
        {
            
get
            {
                
return this._socket.Connected;
            }
        }

        
public event PacketEventHandler PacketReceived;

        
public MFSocket(string remoteIp, int port, IPacketLoader paser)
        {
            
if (paser == nullthrow new ArgumentException("paser");

            
this._socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            
try
            {
                
this._socket.Connect(remoteIp, port);
            }
            
catch
            {
                
throw;
            }

            
this.ParseStrategy = paser;
            
this._workbuff = new Byte[2048];
            
this._receiveArgs = new SocketAsyncEventArgs();
            
this._receiveArgs.SetBuffer(this._workbuff, 0this._workbuff.Length);
            
this._receiveArgs.Completed += new EventHandler<SocketAsyncEventArgs>(_receiveArgs_Completed);
            
this._socket.ReceiveAsync(this._receiveArgs);
        }

        
public void Send(IPacket packet)
        {
            
try
            {
                
this._socket.Send(packet.GetBytes());
            }
            
catch (SocketException ex)
            {
                
this._socket.Close();
                Exception outerEx 
= new Exception("连接中断。", ex);
                
throw outerEx;
            }
        }

        
private void _receiveArgs_Completed(object sender, SocketAsyncEventArgs e)
        {
            
if (this._singlePacket == null)
            {
                
this._singlePacket = ArrayHelper.SubArray(e.Buffer, 0, e.BytesTransferred);
            }
            
else
            {
                
this._singlePacket = ArrayHelper.Join(this._singlePacket, 0this._singlePacket.Length, e.Buffer, 0, e.BytesTransferred);
            }

            
if (e.BytesTransferred > 0)
            {
                
this.Parse();
                
this._socket.ReceiveAsync(e);       //这里类似递归调用,重用SocketAsyncEventArgs对象
            }
            
else
            {
                
this._socket.Close();
                
throw new Exception("连接中断。");
            }
        }

        
/// <summary>
        
/// 解析保存区数据
        
/// </summary>
        private void Parse()
        {
            
//先判断包头是否接收完成
            while (this._singlePacket.Length >= this.ParseStrategy.HeadLength)
            {
                  Byte[] head 
= ArrayHelper.SubArray(this._singlePacket, 0this.ParseStrategy.HeadLength);
                  
int packetLength = this.ParseStrategy.HeadLength + this.ParseStrategy.GetBodyLength(head);

                  
if(this._singlePacket.Length >= packetLength)
                  {
                       Byte[] data 
= ArrayHelper.SubArray(this._singlePacket, 0, packetLength);
                       
this._singlePacket = ArrayHelper.SubArray(this._singlePacket, packetLength, this._singlePacket.Length - packetLength);
                       PacketEventArgs arg 
= new PacketEventArgs(this.ParseStrategy.Parse(data));
                       
this.Notify(arg);
                  }
            }
        }

        
/// <summary>
        
/// 触发包体接收完成事件
        
/// </summary>
        private void Notify(PacketEventArgs args)
        {
            
if (this.PacketReceived != null)
            {
                
this.PacketReceived(this, args);
            }
        }
}


其内部通过使用SocketAsyncEventArgs对象并且递归调用收包方法来实现异步的数据收取,同时IPacketLoader是一个解析数据的策略对象的弱引用,在私有的Parse()方法中用于将数据解析成一个IPacket并通过事件通知,其定义如下:

代码

 

  类库基本结构比较简单,主要是考虑将收包及解析的过程封装,希望园子里这方面经验比较丰富的朋友能提点改进意见,尤其是当运用于多线程环境时是否存在一些潜在的BUG,将来有可能用来模拟客户端的多角色登陆。

 

 

 

 

 

 

 

 

 

 

posted on 2009-12-01 10:10 四有青年 阅读(...) 评论(...) 编辑 收藏

导航

统计