高效的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();

}


高效的TCP数据拆包器的更多相关文章

  1. 高效的TCP消息发送组件

    目前的.net 架构下缺乏高效的TCP消息发送组件,而这种组件是构建高性能分布式应用所必需的.为此我结合多年的底层开发经验开发了一个.net 下的高效TCP消息发送组件.这个组件在异步发送时可以达到每 ...

  2. s6-4 TCP 数据段

    传输控制协议  TCP (Transmission Control Protocol) 是专门为了在不可靠的互联网络上提供可靠的端到端字节流而设计的  TCP必须动态地适应不同的拓扑.带宽.延迟. ...

  3. WireShark抓包时TCP数据包出现may be caused by ip checksum offload

    最近用WireShark抓包时发现TCP数据包有报错:IP Checksum Offload,经过查阅资料终于找到了原因 总结下来就是wireshark抓到的数据包提示Checksum错误,是因为它截 ...

  4. [置顶] NS2中对TCP数据包和ACK包的TCP Sink类的主要实现代码详尽剖析--吐血放送

    NS2中对TCP数据包和ACK包的TCP Sink类的主要实现代码详尽剖析,限于个人水平,如有错误请留言指出! TcpSink类的recv()方法: void TcpSink::recv(Packet ...

  5. TCP数据包结构

    源端口号( 16 位):它(连同源主机 IP 地址)标识源主机的一个应用进程.目的端口号( 16 位):它(连同目的主机 IP 地址)标识目的主机的一个应用进程.这两个值加上 IP 报头中的源主机 I ...

  6. modbus tcp数据报文结构

    modbus tcp数据报文结构 请求:00 00 00 00 00 06 09 03 00 00 00 01 响应:00 00 00 00 00 05 09 03 02 12 34 一次modbus ...

  7. Wireshark抓包工具--TCP数据包seq ack等解读

    1.Wireshark的数据包详情窗口,如果是用中括号[]括起来的,表示注释,在数据包中不占字节 2.在二进制窗口中,如“DD 3D”,表示两个字节,一个字节8位 3.TCP数据包中,seq表示这个包 ...

  8. 【转载】TCP数据包结构

    最近在研究TCP协议,找了点资料,感觉很经典,所以转载过来. 如果本文中图片不能观看,请链接原始地址:http://xinxiangsui2018.blog.163.com/blog/static/1 ...

  9. Haproxy TCP数据转发

    在实际项目中需要用到haproxy做TCP转发,下面主要针对haproxy的安装及TCP数据转发配置进行说明 一.安装Haproxy (1)编译安装Haproxy mkdir -p /data01/h ...

随机推荐

  1. SSRS 报表 报表迁移

    数据拷贝下来以后在新的服务器上面还是进行原来的设置 点击报表服务器的连接查看报表服务器列表 这是给每个账户增加访问权限 在主界面点击文件夹设置,给每个角色分配操作报表的权限.,上面那个权限和这个权限都 ...

  2. 《剑指offer》旋转数组的最小数字

    一.题目描述 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 二.输入描述 输入一个递增排序的数组的一个旋转 三.输出描述 输出旋转数组的最小元素 例如: 例如数组{3,4,5,1 ...

  3. Oracle 流程控制语句

    分为选择语句循环语句两大类:一 选择语句1 if then ...end;set serveroutput on declare var_name1 varchar2(50):='East'; var ...

  4. SSM中使用POI实现excel的导入导出

    环境:导入POI对应的包 环境: Spring+SpringMVC+Mybatis POI对应的包 <dependency> <groupId>org.apache.poi&l ...

  5. c#做对比软件

    一些 HTML内容比较/文本差异比较 开源代码 1. DiffPlex - a .NET Diff Generator    http://diffplex.codeplex.com/SourceCo ...

  6. 列表的所有的input,将它的值以键值对的形式存放到一个数组里

    要求的格式 代码块 $('.btn-confirm').on('tap',function(){ var arr={}; var name = $("input[name='insuranc ...

  7. md5sum---文件校验和

    md5sum命令采用MD5报文摘要算法(128位)计算和检查文件的校验和.一般来说,安装了Linux后,就会有md5sum这个工具,直接在命令行终端直接运行. MD5算法常常被用来验证网络文件传输的完 ...

  8. caioj 1412 动态规划3:a+b问题(完全背包方案数)

    每个素数就是一个物品,然后就相当于求完全背包方案数 把max改成+就好了. #include<cstdio> #include<vector> #include<cstr ...

  9. caioj 1069 动态规划入门(二维一边推2:顺序对齐)(最长公共子序列拓展总结)

    caioj 1068是最长公共子序列裸体,秒过, 就不写博客了 caioj 1069到1071 都是最长公共字序列的拓展,我总结出了一个模型,屡试不爽    (1) 字符串下标从1开始,因为0用来表示 ...

  10. python中的类与继承

    Class 类的定义以及实例的建立 Python中,类通过 class 关键字定义. 例如最简单的一个类定义可以为: class Person(object): pass Python 的编程习惯,类 ...