高效的TCP数据拆包器 接收器,每秒拆1KB的包达到30万以上

  1. /// 数据同步协义数据接收器
  2. /// </summary>
  3. /// <remarks>
  4. /// 主要功能有
  5. /// 1.将一个TCPSocket的所有数据所有接收
  6. /// 2.解析协义
  7. /// 3.解析完毕后的协义调用 Handler通知外部处理
  8. /// 4.定义一个协义解析线程不停的解析协义
  9. /// </remarks>
  10. public class TCPReceiver : IDisposable
  11. {
  12. #region 构造函数
  13. /// <summary>
  14. /// 数据同步协义数据接收器 实例
  15. /// </summary>
  16. public TCPReceiver()
  17. {
  18.  
  19. }
  20.  
  21. /// <summary>
  22. /// 数据同步协义数据接收器 实例
  23. /// </summary>
  24. /// <param name="protocolhead">协议头</param>
  25. /// <param name="protocolfoot">协议尾</param>
  26. public TCPReceiver(byte[] protocolhead, byte[] protocolfoot = null)
  27. {
  28. //邦定包头,与包体
  29. PackageHead = protocolhead;
  30. PackageFoot = protocolfoot;
  31. }
  32.  
  33. #endregion
  34.  
  35. /// <summary>
  36. /// 最大单个协义体数据长度,默认10MB
  37. /// </summary>
  38. private int maxProtocolBinary = 1024 * 1024 * 10;
  39. /// <summary>
  40. /// 最大单个协义体数据长度
  41. /// </summary>
  42. public int MaxProtocolBinary
  43. {
  44. get { return maxProtocolBinary; }
  45. set { maxProtocolBinary = value; }
  46. }
  47.  
  48. /// <summary>
  49. /// 是否正在执行
  50. /// </summary>
  51. public bool IsRuning { get; set; }
  52.  
  53. private Task task = null;
  54. /// <summary>
  55. /// 当前处理解析协义的线程
  56. /// </summary>
  57. public Task PraseProtocolTask
  58. {
  59. get { return task; }
  60. }
  61.  
  62. /// <summary>
  63. /// 接收数据处理事件
  64. /// </summary>
  65. public Action<byte[], Socket> ProtocolReceivedHandler
  66. {
  67. get;
  68. set;
  69. }
  70.  
  71. /// <summary>
  72. /// 是从哪个节点接收的数据
  73. /// </summary>
  74. public Socket Handler
  75. {
  76. get;
  77. set;
  78. }
  79.  
  80. #region 接收数据加入到队列
  81. /// <summary>
  82. /// 接收数据处理集合,默认开放1MB的空间
  83. /// </summary>
  84. // protected System.Collections.Generic.Queue<byte> byteQueue = new Queue<byte>(1024 * 1024);
  85.  
  86. /// <summary>
  87. /// 默认开放500空间,100万次单纯加入用时95毫秒
  88. /// </summary>
  89. private Queue<byte[]> receiveByteArrayQueue = new Queue<byte[]>(500);
  90. /// <summary>
  91. /// 接入队列处理器
  92. /// </summary>
  93. protected Queue<byte[]> ReceiveByteArrayQueue
  94. {
  95. get { return receiveByteArrayQueue; }
  96. }
  97.  
  98. #if DEBUG
  99. //private int cuount = 1;
  100. #endif
  101.  
  102. /// <summary>
  103. /// 接收数据
  104. /// </summary>
  105. public void Receive(byte[] buff)
  106. {
  107. #if DEBUG
  108. //严重影响性能,会变慢1117倍
  109. // Console.WriteLine(buff.ToHex());
  110. //Console.WriteLine(buff.ByteArray2HexString());
  111. // Console.WriteLine("-----"+cuount++);
  112. #endif
  113.  
  114. lock (receiveByteArrayQueue)
  115. {
  116. //加入对像数据
  117. receiveByteArrayQueue.Enqueue(buff);
  118. }
  119. }
  120. #endregion
  121.  
  122. #region 线程控制
  123.  
  124. /// <summary>
  125. /// 停止解析协义
  126. /// </summary>
  127. public void StopParseProtocol()
  128. {
  129. IsRuning = false;
  130. //throw new NotImplementedException("请编写代码,在线程停止后须要将缓存队列中的数据所有处理完毕");
  131. //在线程停止后须要将缓存队列中的数据所有处理完毕
  132. for (; receiveByteArrayQueue.Count > 0; )
  133. {
  134. //处理数据
  135. ProcessBytes();
  136. }
  137. }
  138. #endregion
  139.  
  140. #region 解析协义数据
  141. /// <summary>
  142. /// 分包用包头
  143. /// </summary>
  144. private byte[] packageHead = new byte[] { 0x7e };//0x7e
  145.  
  146. /// <summary>
  147. /// 分包用包头
  148. /// </summary>
  149. public byte[] PackageHead
  150. {
  151. get { return packageHead; }
  152. set
  153. {
  154. if (value != null)
  155. {
  156. packageHead = value;
  157. }
  158. }
  159. }
  160. /// <summary>
  161. /// 分包用包尾
  162. /// </summary>
  163. private byte[] packageFoot = new byte[] { 0x7e };
  164. /// <summary>
  165. /// 分包用包尾
  166. /// </summary>
  167. public byte[] PackageFoot
  168. {
  169. get { return packageFoot; }
  170. set
  171. {
  172. if (value != null)
  173. {
  174. packageFoot = value;
  175.  
  176. }
  177. }
  178. }
  179.  
  180. /// <summary>
  181. /// 用于处理数据协义的功能
  182. /// </summary>
  183. List<byte> bytes = new List<byte>();
  184.  
  185. /// <summary>
  186. /// 默认开 3MB的数据接收缓冲区,假设超过3MB则数据会挂掉
  187. /// </summary>
  188. //private byte[] ByteBuff = null;
  189.  
  190. /// <summary>
  191. /// 协义数据实体队列,已经进行拆包后的协义数据
  192. /// </summary>
  193. private Queue<byte[]> ProtocolEntityQueue = new Queue<byte[]>(500);
  194.  
  195. /// <summary>
  196. /// 找到分包用包头
  197. /// </summary>
  198. bool FindPackageHead = false;
  199. /// <summary>
  200. /// 找包头的当着序号
  201. /// </summary>
  202. int findHeadindex = 0;
  203. /// <summary>
  204. /// 找包尾
  205. /// </summary>
  206. int findFootIndex = 0;
  207.  
  208. /// <summary>
  209. /// 解析协义方法
  210. /// 之所以先所有放到一个query里是进行高速的接收
  211. ///
  212. /// </summary>
  213. public void PraseProtocol()
  214. {
  215. IsRuning = true;
  216. while (IsRuning)
  217. {
  218. ProcessBytes();
  219. }
  220. }
  221. /// <summary>
  222. /// 处理队列中的数据删除包头,包尾巴
  223. /// </summary>
  224. public void ProcessBytes()
  225. {
  226. byte[] arr = null;
  227. //開始解析数据
  228. //1.取出数据
  229. lock (receiveByteArrayQueue)
  230. {
  231. if (receiveByteArrayQueue.Count > 0)
  232. {
  233. arr = receiveByteArrayQueue.Dequeue();
  234. }
  235. }
  236. if (arr != null)
  237. {
  238. //锁处理
  239. lock (bytes)
  240. {
  241. //此协义数据中的协义数据索引
  242. // List<int> ints = new List<int>();
  243.  
  244. //2.将数据进行包查找
  245. //開始从队列中取数据
  246. for (int k = 0; k < arr.Length; k++)
  247. {
  248. //队列有数据
  249. byte b = arr[k];
  250. //假设超过最大接收字节数
  251. if (maxProtocolBinary <= bytes.Count)
  252. {
  253. bytes.Clear();
  254. }
  255. //加入到对像集合
  256. bytes.Add(b);
  257. //3.从集合的前面開始取数据.找包头,进行拆包
  258. #region 找包头
  259. //等于包数据
  260. if (packageHead.Length > 0 && b == packageHead[findHeadindex] && !FindPackageHead)
  261. {
  262.  
  263. //包头找完
  264. if (findHeadindex == packageHead.Length - 1)
  265. {
  266.  
  267. //ints.Add(k);
  268. System.Threading.Interlocked.Exchange(ref findHeadindex, 0);
  269. if (!FindPackageHead)
  270. {
  271. FindPackageHead = true;
  272. }
  273. //这里取一个完整包
  274. byte[] byteFarm = bytes.Take(bytes.Count - packageHead.Length).ToArray();
  275. //假设是有效的数据
  276. if (byteFarm.Length > packageHead.Length)
  277. {
  278. lock (ProtocolEntityQueue)
  279. {
  280. ProtocolEntityQueue.Enqueue(byteFarm);
  281. }
  282. //開始从 bytes 中移除数据
  283. bytes.Clear();
  284. //加入包头
  285. bytes.AddRange(packageHead);
  286. }
  287. //包头找完则找下一字节
  288. continue;
  289. }
  290. else
  291. {
  292. System.Threading.Interlocked.Increment(ref findHeadindex);
  293. }
  294. }
  295. else
  296. {
  297. System.Threading.Interlocked.Exchange(ref findHeadindex, 0);
  298. //findHeadindex = 0;
  299. if (!FindPackageHead && packageHead.Length == 0)
  300. {
  301. FindPackageHead = true;
  302. }
  303. }
  304. #endregion
  305.  
  306. #region 找包尾
  307.  
  308. if (packageFoot != null && packageFoot.Length > 0 && FindPackageHead)
  309. {
  310. if (b == packageFoot[findFootIndex])
  311. {
  312. //包尾找完
  313. if (findFootIndex == packageFoot.Length - 1)
  314. {
  315. //删除包尾字节,可能会包括包头字节
  316. //byte[] byteFarm = bytes.Take(bytes.Count - packageFoot.Length).ToArray();
  317. byte[] byteFarm = bytes.ToArray();
  318. //跳过包头字节,包尾字节
  319. //byte[] byteFarm = bytes.Skip(packageHead.Length).Take(bytes.Count - (packageFoot.Length + packageHead.Length)).ToArray();
  320. //假设是有效的数据
  321. if (byteFarm.Length >= packageFoot.Length)
  322. {
  323. lock (ProtocolEntityQueue)
  324. {
  325. ProtocolEntityQueue.Enqueue(byteFarm);
  326. }
  327. //開始从 bytes 中移除数据
  328. bytes.Clear();
  329. }
  330. FindPackageHead = false;
  331. //包尾找完则找下一字节
  332. continue;
  333. }
  334. else
  335. {
  336. System.Threading.Interlocked.Increment(ref findFootIndex);
  337. }
  338. }
  339. else
  340. {
  341. System.Threading.Interlocked.Exchange(ref findFootIndex, 0);
  342. //findFootIndex = 0;
  343.  
  344. }
  345. }
  346.  
  347. #endregion
  348.  
  349. }
  350. }
  351. //4.又一次组成一个byte[] 进行数据解析
  352. lock (ProtocolEntityQueue)
  353. {
  354. if (ProtocolEntityQueue.Count > 0)
  355. {
  356. //循环所有接收到的数据包
  357. for (; ProtocolEntityQueue.Count > 0; )
  358. {
  359.  
  360. //取取删除尾巴的的数据
  361.  
  362. //解析协议数据
  363. byte[] bytearr = ProtocolEntityQueue.Dequeue();
  364.  
  365. //数据要大于分包的长度
  366. if (bytearr.Length >= packageFoot.Length && bytearr.Length >= packageHead.Length)
  367. {
  368. ProtocolReceivedHandler.Invoke(bytearr, Handler);
  369. }
  370. }
  371. }
  372. }
  373. }
  374. else
  375. {
  376. //停止执行
  377. IsRuning = false;
  378. //System.Threading.Thread.Sleep(5);
  379. }
  380. }
  381.  
  382. #endregion
  383.  
  384. /// <summary>
  385. /// 析构方法
  386. /// </summary>
  387. public void Dispose()
  388. {
  389. StopParseProtocol();
  390. }
  391.  
  392. }

用法

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. MyBatis的架构设计以及实例分析--转

    原文地址:http://blog.csdn.net/luanlouis/article/details/40422941 MyBatis是目前非常流行的ORM框架,它的功能很强大,然而其实现却比较简单 ...

  2. Java基础学习(一) -- Java环境搭建、数据类型、分支循环等控制结构、简单一维数组详解

    一:java概述: 1982年,SUN公司诞生于美国斯坦福大学校园,并于1986年上市,在NASDAQ(纳斯达克:是全美证券商协会自动报价系统)的标识为SUNW,2007年改为JAVA. 2009年4 ...

  3. python3.x学习笔记2(基础知识)

    1.元组元组其实跟列表差不多,也是存一组数,只是它一旦创建,便不能在修改,所以又叫只读列表语法:names =('shgd','sjdh') 它只有两个方法,一个是count,一个是index 2.字 ...

  4. 当fastJson邂逅大写字段时

    在项目中遇到了一件令人头疼的事.使用fastJson反序列化时下面的Json时,得到对象属性总为null(如下图),可能细心的朋友一看就知道问题出在哪里,没错!问题就出在返回的字段首字母给大写了.fa ...

  5. 如何使 nginx 支撑更高并发

    /** * * * * 如何使 nginx 支撑更高的并发? * 原理: * 服务器方面可以从两个方面阐述: * 1.socket 链接方面:因为每次请求都是一次连接,而 nginx 服务器配置方面默 ...

  6. 我的PHP学习之路

    由于工作中,做微信小程序需要我自己写一些后台代码.并且公司后台用的是php.所以我决定在周末和下班后抽空学习php.一开始,我想找一些入门视频来学,然后发现好像效率不是很好.不如看书来得痛快.(主要是 ...

  7. caioj 1074 动态规划入门(中链式1:最小交换合并问题)

    经典的石子合并问题!!! 设f[i][j]为从i到j的最大值 然后我们先枚举区间大小,然后枚举起点终点来更新 f[i][j] = min(f[i][k] + f[k+1][j] + sum(i, j) ...

  8. ArcGIS api for javascript——创建地图

    描述 这个示例显示ArcGIS Server的一个地图.ArcGIS Server地图是缓存的,意味着它有服务器管理员建来提升性能的一组预先渲染的切片.由于这个原因地图通过ArcGISTiledMap ...

  9. psycopg2 ImportError: DLL load failed

    setup.py install 报错  error: command 'mt.exe' failed: No such file or directory  或者 Unable to find vc ...

  10. 算法导论————KMP

    [例题传送门:caioj1177] KMP模版:子串是否出现 [题意]有两个字符串SA和SB,SA是母串,SB是子串,问子串SB是否在母串SA中出现过.如果出现过输出第一次出现的起始位置和结束位置,否 ...