高效的TCP数据拆包器
/// 数据同步协义数据接收器
/// </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数据拆包器的更多相关文章
- 高效的TCP消息发送组件
目前的.net 架构下缺乏高效的TCP消息发送组件,而这种组件是构建高性能分布式应用所必需的.为此我结合多年的底层开发经验开发了一个.net 下的高效TCP消息发送组件.这个组件在异步发送时可以达到每 ...
- s6-4 TCP 数据段
传输控制协议 TCP (Transmission Control Protocol) 是专门为了在不可靠的互联网络上提供可靠的端到端字节流而设计的 TCP必须动态地适应不同的拓扑.带宽.延迟. ...
- WireShark抓包时TCP数据包出现may be caused by ip checksum offload
最近用WireShark抓包时发现TCP数据包有报错:IP Checksum Offload,经过查阅资料终于找到了原因 总结下来就是wireshark抓到的数据包提示Checksum错误,是因为它截 ...
- [置顶] NS2中对TCP数据包和ACK包的TCP Sink类的主要实现代码详尽剖析--吐血放送
NS2中对TCP数据包和ACK包的TCP Sink类的主要实现代码详尽剖析,限于个人水平,如有错误请留言指出! TcpSink类的recv()方法: void TcpSink::recv(Packet ...
- TCP数据包结构
源端口号( 16 位):它(连同源主机 IP 地址)标识源主机的一个应用进程.目的端口号( 16 位):它(连同目的主机 IP 地址)标识目的主机的一个应用进程.这两个值加上 IP 报头中的源主机 I ...
- 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 ...
- Wireshark抓包工具--TCP数据包seq ack等解读
1.Wireshark的数据包详情窗口,如果是用中括号[]括起来的,表示注释,在数据包中不占字节 2.在二进制窗口中,如“DD 3D”,表示两个字节,一个字节8位 3.TCP数据包中,seq表示这个包 ...
- 【转载】TCP数据包结构
最近在研究TCP协议,找了点资料,感觉很经典,所以转载过来. 如果本文中图片不能观看,请链接原始地址:http://xinxiangsui2018.blog.163.com/blog/static/1 ...
- Haproxy TCP数据转发
在实际项目中需要用到haproxy做TCP转发,下面主要针对haproxy的安装及TCP数据转发配置进行说明 一.安装Haproxy (1)编译安装Haproxy mkdir -p /data01/h ...
随机推荐
- Python 从入门到精通 全程最佳实现梳理
零零星星的时间,持续完善中...... 1.一些基础的必要信息归纳 Python 官网 www.python.org 发明者 吉多·范罗苏姆 发行时间 1991年,26年前 编程泛型 多泛型.面向对 ...
- AngularJs轻松入门(二)数据绑定
数据绑定是AngularJs中非常重要的特性,我们看一下下面的例子: <!DOCTYPE html> <html ng-app> <head lang="en& ...
- windows gitbub使用
1.安装git bush (windows没什么好说的 下一步,下一步,,) 2. 通过gitbush命令行生成密钥: (拷贝密钥) 3.密钥添加到github上面: 4.克隆项目: 5.提交: 查看 ...
- php八大设计模式之装饰器模式
我们都知道,得到一匹布需要大概这么几步: 1.染色 2.印花 3.裁剪 这种形式在面向对象中怎么实现呢? 面向过程[继承模式]实现: 继承模式得到需要的布料,一步一步的加工. 继承的层次越来越深,扩展 ...
- yii2-Ueditor百度编辑器
今天在网上看了下有关图片上传的教程,历经挫折才调试好,现在把相关代码及其说明贴出来,以供初次使用的朋友们参考. 资源下载 yii2.0-ueditor下载路径: https://link.jiansh ...
- Object-C,四则运算计算器
下面是是一个比较复杂的类. 定义一个四则运算计算器Caculator的接口和实现. 在main函数中,让用户输入四则运算表达式,比如a+b,a-b. 最后,在控制台输出结果. 用到的语法:接口.类的定 ...
- NYOJ 541 最强的战斗力
最强DE 战斗力 时间限制:1000 ms | 内存限制:65535 KB 难度: 描写叙述 春秋战国时期,赵国地大物博,资源很丰富.人民安居乐业.但很多国家对它虎视眈眈.准备联合起来对赵国发起一 ...
- HDU 3555 Bomb(数位DP模板啊两种形式)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3555 Problem Description The counter-terrorists found ...
- 跨域post 及 使用token防止csrf 攻击
环境: 后台使用的python - flask 前台使用angular框架 1.一个跨域post的样例: 跨域post有多种实现方式: 1.CORS:http://blog.csdn.net/hfah ...
- Redis封装之Hash
RedisHashService: /// <summary> /// Hash:类似dictionary,通过索引快速定位到指定元素的,耗时均等,跟string的区别在于不用反序列化,直 ...