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

本文介绍了一种基于.NET的自定义Socket通信库的设计与实现,包括接口定义、数据包解析策略及异步数据接收机制。适用于多线程环境下的TCP通信。

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

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

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

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

 

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

ContractedBlock.gifExpandedBlockStart.gif代码
public class PacketEventArgs : EventArgs
    {
        
public IPacket Packet { getprivate set; }

        
public PacketEventArgs(IPacket packet)
        {
            
this.Packet = packet;
        }
    }

    
public delegate void PacketEventHandler(object sender,PacketEventArgs e);

 

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

 

ExpandedBlockStart.gif代码
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并通过事件通知,其定义如下:

ContractedBlock.gifExpandedBlockStart.gif代码
/// <summary>
    
/// 提供解析者策略的接口
    
/// </summary>
    public interface IPacketLoader
    {
        
/// <summary>
        
/// 包头长度
        
/// </summary>
        int HeadLength { getset; }

        
/// <summary>
        
/// 获取包体长度
        
/// </summary>
        int GetBodyLength(Byte[] head);

        IPacket Parse(Byte[] data);
    }

 

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

 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/Bruce-Su/archive/2009/12/01/1614308.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值