一个简易的Socket组件,希望与牛人菜鸟们分享交流
在这之前几乎没使用过.NET的Socket编程,最近由于工作的关系需要做些客户端与服务器完成TCP通信。自己闭门造成,写了一个类库,初步测试通过,发布出来和大家交流下,也希望在这方面比较牛X的批评指正。
首先,公司内部基本上所有的TCP协议基本上都是基于一些协议的,无论收或者发,都离不开一个协议包的概念,因此这里定义了一个接口来表示,代码如下:
代码
/// <summary>
/// 所有数据包继承该接口
/// </summary>
public interface IPacket
{
Byte[] GetBytes();
}
/// 所有数据包继承该接口
/// </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 == null) throw 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, 0, this._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, 0, this._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, 0, this.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);
}
}
}
{
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 == null) throw 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, 0, this._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, 0, this._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, 0, this.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,将来有可能用来模拟客户端的多角色登陆。