高效的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. ELK搭建(filebeat、elasticsearch、logstash、kibana)

    ELK部署(文章有点儿长,搭建时请到官网将tar包下载好,按步骤可以完成搭建使用) ELK指的是ElasticSearch.LogStash.Kibana三个开源工具 LogStash是负责数据的收集 ...

  2. Storm框架基础(一)

    * Storm框架基础(一) Storm简述 如果你了解过SparkStreaming,那么Storm就可以类比着入门,在此我们可以先做一个简单的比较:  在SparkStreaming中: 我们曾尝 ...

  3. Error creating bean with name 'testController': Injection of resource dependencies failed;

    启动ssm项目报错: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 't ...

  4. ListView优化-ViewHolder缓存

    安卓开发中ListView控件是一个使用频率相当的高级控件,通常用于展示一系列相似度极高的数据,当数据量极大或布局相当复杂时,ListView的性能优化就显得非常重要.所以在开发中不但功能上要满足,而 ...

  5. 高德地图和canvas画图结合应用(一)

    现在重构web项目的时候发现,以前项目中是高德画基站的扇区的时候,通过计算点来画多边形,在站点的数量比较多的时候,会增加请求,同时计算扇区的时候有大量的计算,这样会极度浪费服务器的性能,所以对这块进行 ...

  6. centos6.9安装virtualenv并配置python2.7环境

    一. 安装python2.7 解压文件 tar -xvf Python-2.7.14.tar 进入源码包目录 cd Python-2.7.14 开始构建之前指定安装的目录 默认会被安装进 /usr/l ...

  7. socket 编程的端口和地址复用

    在linux socket网络编程中,大规模并发TCP或UDP连接时,经常会用到端口复用:   int opt = 1;   if(setsockopt(sockfd, SOL_SOCKET,SO_R ...

  8. Android Recovery OTA升级(一)—— make otapackage

    文件夹 文件夹 概述 make otapackage BUILT_TARGET_FILES_PACKAGE ota_from_target_files WriteFullOTAPackage Sign ...

  9. 《TCP/IP具体解释》读书笔记(19章)-TCP的交互数据流

    在TCP进行传输数据时.能够分为成块数据流和交互数据流两种.假设按字节计算.成块数据与交互数据的比例约为90%和10%,TCP须要同一时候处理这两类数据,且处理的算法不同. 书籍本章中以Rlogin应 ...

  10. CoAP与物联网系统

    CoAP简单介绍 引自维基百科上的介绍,用的是谷歌翻译... 受约束的应用协议(COAP)是一种软件协议旨在以很easy的电子设备.使他们能够在互联网上进行交互式通信中使用. 它特别针对小型低功率传感 ...