正文
高效的TCP数据拆包器
小程序:扫一扫查出行
【扫一扫了解最新限行尾号】
复制小程序
【扫一扫了解最新限行尾号】
复制小程序
高效的TCP数据拆包器 接收器,每秒拆1KB的包达到30万以上
/// 数据同步协义数据接收器
/// </summary>
/// <remarks>
/// 主要功能有
/// 1.将一个TCPSocket的所有数据所有接收
/// 2.解析协义
/// 3.解析完毕后的协义调用 Handler通知外部处理
/// 4.定义一个协义解析线程不停的解析协义
/// </remarks>
public class TCPReceiver : IDisposable
{
#region 构造函数
/// <summary>
/// 数据同步协义数据接收器 实例
/// </summary>
public TCPReceiver()
{ } /// <summary>
/// 数据同步协义数据接收器 实例
/// </summary>
/// <param name="protocolhead">协议头</param>
/// <param name="protocolfoot">协议尾</param>
public TCPReceiver(byte[] protocolhead, byte[] protocolfoot = null)
{
//邦定包头,与包体
PackageHead = protocolhead;
PackageFoot = protocolfoot;
} #endregion /// <summary>
/// 最大单个协义体数据长度,默认10MB
/// </summary>
private int maxProtocolBinary = 1024 * 1024 * 10;
/// <summary>
/// 最大单个协义体数据长度
/// </summary>
public int MaxProtocolBinary
{
get { return maxProtocolBinary; }
set { maxProtocolBinary = value; }
} /// <summary>
/// 是否正在执行
/// </summary>
public bool IsRuning { get; set; } private Task task = null;
/// <summary>
/// 当前处理解析协义的线程
/// </summary>
public Task PraseProtocolTask
{
get { return task; }
} /// <summary>
/// 接收数据处理事件
/// </summary>
public Action<byte[], Socket> ProtocolReceivedHandler
{
get;
set;
} /// <summary>
/// 是从哪个节点接收的数据
/// </summary>
public Socket Handler
{
get;
set;
} #region 接收数据加入到队列
/// <summary>
/// 接收数据处理集合,默认开放1MB的空间
/// </summary>
// protected System.Collections.Generic.Queue<byte> byteQueue = new Queue<byte>(1024 * 1024); /// <summary>
/// 默认开放500空间,100万次单纯加入用时95毫秒
/// </summary>
private Queue<byte[]> receiveByteArrayQueue = new Queue<byte[]>(500);
/// <summary>
/// 接入队列处理器
/// </summary>
protected Queue<byte[]> ReceiveByteArrayQueue
{
get { return receiveByteArrayQueue; }
}#if DEBUG
//private int cuount = 1;
#endif /// <summary>
/// 接收数据
/// </summary>
public void Receive(byte[] buff)
{
#if DEBUG
//严重影响性能,会变慢1117倍
// Console.WriteLine(buff.ToHex());
//Console.WriteLine(buff.ByteArray2HexString());
// Console.WriteLine("-----"+cuount++);
#endif lock (receiveByteArrayQueue)
{
//加入对像数据
receiveByteArrayQueue.Enqueue(buff);
}
}
#endregion #region 线程控制 /// <summary>
/// 停止解析协义
/// </summary>
public void StopParseProtocol()
{
IsRuning = false;
//throw new NotImplementedException("请编写代码,在线程停止后须要将缓存队列中的数据所有处理完毕");
//在线程停止后须要将缓存队列中的数据所有处理完毕
for (; receiveByteArrayQueue.Count > 0; )
{
//处理数据
ProcessBytes();
}
}
#endregion #region 解析协义数据
/// <summary>
/// 分包用包头
/// </summary>
private byte[] packageHead = new byte[] { 0x7e };//0x7e /// <summary>
/// 分包用包头
/// </summary>
public byte[] PackageHead
{
get { return packageHead; }
set
{
if (value != null)
{
packageHead = value;
}
}
}
/// <summary>
/// 分包用包尾
/// </summary>
private byte[] packageFoot = new byte[] { 0x7e };
/// <summary>
/// 分包用包尾
/// </summary>
public byte[] PackageFoot
{
get { return packageFoot; }
set
{
if (value != null)
{
packageFoot = value; }
}
} /// <summary>
/// 用于处理数据协义的功能
/// </summary>
List<byte> bytes = new List<byte>(); /// <summary>
/// 默认开 3MB的数据接收缓冲区,假设超过3MB则数据会挂掉
/// </summary>
//private byte[] ByteBuff = null; /// <summary>
/// 协义数据实体队列,已经进行拆包后的协义数据
/// </summary>
private Queue<byte[]> ProtocolEntityQueue = new Queue<byte[]>(500); /// <summary>
/// 找到分包用包头
/// </summary>
bool FindPackageHead = false;
/// <summary>
/// 找包头的当着序号
/// </summary>
int findHeadindex = 0;
/// <summary>
/// 找包尾
/// </summary>
int findFootIndex = 0; /// <summary>
/// 解析协义方法
/// 之所以先所有放到一个query里是进行高速的接收
///
/// </summary>
public void PraseProtocol()
{
IsRuning = true;
while (IsRuning)
{
ProcessBytes();
}
}
/// <summary>
/// 处理队列中的数据删除包头,包尾巴
/// </summary>
public void ProcessBytes()
{
byte[] arr = null;
//開始解析数据
//1.取出数据
lock (receiveByteArrayQueue)
{
if (receiveByteArrayQueue.Count > 0)
{
arr = receiveByteArrayQueue.Dequeue();
}
}
if (arr != null)
{
//锁处理
lock (bytes)
{
//此协义数据中的协义数据索引
// List<int> ints = new List<int>(); //2.将数据进行包查找
//開始从队列中取数据
for (int k = 0; k < arr.Length; k++)
{
//队列有数据
byte b = arr[k];
//假设超过最大接收字节数
if (maxProtocolBinary <= bytes.Count)
{
bytes.Clear();
}
//加入到对像集合
bytes.Add(b);
//3.从集合的前面開始取数据.找包头,进行拆包
#region 找包头
//等于包数据
if (packageHead.Length > 0 && b == packageHead[findHeadindex] && !FindPackageHead)
{ //包头找完
if (findHeadindex == packageHead.Length - 1)
{ //ints.Add(k);
System.Threading.Interlocked.Exchange(ref findHeadindex, 0);
if (!FindPackageHead)
{
FindPackageHead = true;
}
//这里取一个完整包
byte[] byteFarm = bytes.Take(bytes.Count - packageHead.Length).ToArray();
//假设是有效的数据
if (byteFarm.Length > packageHead.Length)
{
lock (ProtocolEntityQueue)
{
ProtocolEntityQueue.Enqueue(byteFarm);
}
//開始从 bytes 中移除数据
bytes.Clear();
//加入包头
bytes.AddRange(packageHead);
}
//包头找完则找下一字节
continue;
}
else
{
System.Threading.Interlocked.Increment(ref findHeadindex);
}
}
else
{
System.Threading.Interlocked.Exchange(ref findHeadindex, 0);
//findHeadindex = 0;
if (!FindPackageHead && packageHead.Length == 0)
{
FindPackageHead = true;
}
}
#endregion #region 找包尾 if (packageFoot != null && packageFoot.Length > 0 && FindPackageHead)
{
if (b == packageFoot[findFootIndex])
{
//包尾找完
if (findFootIndex == packageFoot.Length - 1)
{
//删除包尾字节,可能会包括包头字节
//byte[] byteFarm = bytes.Take(bytes.Count - packageFoot.Length).ToArray();
byte[] byteFarm = bytes.ToArray();
//跳过包头字节,包尾字节
//byte[] byteFarm = bytes.Skip(packageHead.Length).Take(bytes.Count - (packageFoot.Length + packageHead.Length)).ToArray();
//假设是有效的数据
if (byteFarm.Length >= packageFoot.Length)
{
lock (ProtocolEntityQueue)
{
ProtocolEntityQueue.Enqueue(byteFarm);
}
//開始从 bytes 中移除数据
bytes.Clear();
}
FindPackageHead = false;
//包尾找完则找下一字节
continue;
}
else
{
System.Threading.Interlocked.Increment(ref findFootIndex);
}
}
else
{
System.Threading.Interlocked.Exchange(ref findFootIndex, 0);
//findFootIndex = 0; }
} #endregion }
}
//4.又一次组成一个byte[] 进行数据解析
lock (ProtocolEntityQueue)
{
if (ProtocolEntityQueue.Count > 0)
{
//循环所有接收到的数据包
for (; ProtocolEntityQueue.Count > 0; )
{ //取取删除尾巴的的数据 //解析协议数据
byte[] bytearr = ProtocolEntityQueue.Dequeue(); //数据要大于分包的长度
if (bytearr.Length >= packageFoot.Length && bytearr.Length >= packageHead.Length)
{
ProtocolReceivedHandler.Invoke(bytearr, Handler);
}
}
}
}
}
else
{
//停止执行
IsRuning = false;
//System.Threading.Thread.Sleep(5);
}
} #endregion /// <summary>
/// 析构方法
/// </summary>
public void Dispose()
{
StopParseProtocol();
} }
用法
TCPReceiver rece = new TCPReceiver();
//将接收到的数据增加处理
rece .Receive(buff);
另起一个线程进行处理
while(true)
{
rece .PraseProtocol();
}