公司项目中经常设计到串口通信,TCP通信,而且大多都是实时的大数据的传输,然后大家都知道协议通讯肯定涉及到什么,封包、拆包、粘包、校验……什么鬼的概念一大堆,说简单点儿就是要一个高效率可复用的缓存区。按照码农的惯性思维就是去百度、谷歌搜索看有没有现成的东西可以直接拿来用,然而我并没有找到,好吧不是很难的东西自己实现一个呗。开扯……

为什么要用环形队列?

环形队列是在实际编程极为有用的数据结构,它有如下特点:

它是一个首尾相连的FIFO的数据结构,采用数组的线性空间,数据组织简单。能很快知道队列是否满为空。能以很快速度的来存取数据。

因为有简单高效的原因,甚至在硬件都实现了环形队列。

C#完全实现(可直接使用)

鄙人新手这份代码肯定有不足之处,望大家指出交流,涉及到的多线程同步问题请调用者完成,不废话直接上代码。

  1. public class RingBufferManager
  2. {
  3. public byte[] Buffer { get; set; } // 存放内存的数组
  4. public int DataCount { get; set; } // 写入数据大小
  5. public int DataStart { get; set; } // 数据起始索引
  6. public int DataEnd { get; set; } // 数据结束索引
  7. public RingBufferManager(int bufferSize)
  8. {
  9. DataCount = 0; DataStart = 0; DataEnd = 0;
  10. Buffer = new byte[bufferSize];
  11. }
  12. public byte this[int index]
  13. {
  14. get
  15. {
  16. if (index >= DataCount) throw new Exception("环形缓冲区异常,索引溢出");
  17. if (DataStart + index < Buffer.Length)
  18. {
  19. return Buffer[DataStart + index];
  20. }
  21. else
  22. {
  23. return Buffer[(DataStart + index) - Buffer.Length];
  24. }
  25. }
  26. }
  27. public int GetDataCount() // 获得当前写入的字节数
  28. {
  29. return DataCount;
  30. }
  31. public int GetReserveCount() // 获得剩余的字节数
  32. {
  33. return Buffer.Length - DataCount;
  34. }
  35. public void Clear()
  36. {
  37. DataCount = 0;
  38. }
  39. public void Clear(int count) // 清空指定大小的数据
  40. {
  41. if (count >= DataCount) // 如果需要清理的数据大于现有数据大小,则全部清理
  42. {
  43. DataCount = 0;
  44. DataStart = 0;
  45. DataEnd = 0;
  46. }
  47. else
  48. {
  49. if (DataStart + count >= Buffer.Length)
  50. {
  51. DataStart = (DataStart + count) - Buffer.Length;
  52. }
  53. else
  54. {
  55. DataStart += count;
  56. }
  57. DataCount -= count;
  58. }
  59. }
  60. public void WriteBuffer(byte[] buffer, int offset, int count)
  61. {
  62. Int32 reserveCount = Buffer.Length - DataCount;
  63. if (reserveCount >= count) // 可用空间够使用
  64. {
  65. if (DataEnd + count < Buffer.Length) // 数据没到结尾
  66. {
  67. Array.Copy(buffer, offset, Buffer, DataEnd, count);
  68. DataEnd += count;
  69. DataCount += count;
  70. }
  71. else // 数据结束索引超出结尾 循环到开始
  72. {
  73. System.Diagnostics.Debug.WriteLine("缓存重新开始....");
  74. Int32 overflowIndexLength = (DataEnd + count) - Buffer.Length; // 超出索引长度
  75. Int32 endPushIndexLength = count - overflowIndexLength; // 填充在末尾的数据长度
  76. Array.Copy(buffer, offset, Buffer, DataEnd, endPushIndexLength);
  77. DataEnd = 0;
  78. offset += endPushIndexLength;
  79. DataCount += endPushIndexLength;
  80. if (overflowIndexLength != 0)
  81. {
  82. Array.Copy(buffer, offset, Buffer, DataEnd, overflowIndexLength);
  83. }
  84. DataEnd += overflowIndexLength; // 结束索引
  85. DataCount += overflowIndexLength; // 缓存大小
  86. }
  87. }
  88. else
  89. {
  90. // 缓存溢出,不处理
  91. }
  92. }
  93. public void ReadBuffer(byte[] targetBytes,Int32 offset, Int32 count)
  94. {
  95. if (count > DataCount) throw new Exception("环形缓冲区异常,读取长度大于数据长度");
  96. Int32 tempDataStart = DataStart;
  97. if (DataStart + count < Buffer.Length)
  98. {
  99. Array.Copy(Buffer, DataStart, targetBytes, offset, count);
  100. }
  101. else
  102. {
  103. Int32 overflowIndexLength = (DataStart + count) - Buffer.Length; // 超出索引长度
  104. Int32 endPushIndexLength = count - overflowIndexLength; // 填充在末尾的数据长度
  105. Array.Copy(Buffer, DataStart, targetBytes, offset, endPushIndexLength);
  106. offset += endPushIndexLength;
  107. if (overflowIndexLength != 0)
  108. {
  109. Array.Copy(Buffer, 0, targetBytes, offset, overflowIndexLength);
  110. }
  111. }
  112. }
  113. public void WriteBuffer(byte[] buffer)
  114. {
  115. WriteBuffer(buffer, 0, buffer.Length);
  116. }
  117. }

调用实例

生产

  1. int len = sConn.Receive(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, out se);
  2. if (len <= 0) throw new Exception("disconnect..");
  3. if (len > 0)
  4. {
  5. lock (LockReceiveBuffer)
  6. {
  7. while (len + receiveBufferManager.DataCount > MAX_BUFFER_LEN) // 缓存溢出处理
  8. {
  9. Monitor.Wait(LockReceiveBuffer,10000);
  10. }
  11. receiveBufferManager.WriteBuffer(receiveBuffer, 0, len);
  12. Monitor.PulseAll(LockReceiveBuffer);
  13. }
  14. }

消费

  1. lock (LockReceiveBuffer)
  2. {
  3. freame_byte = new byte[frameLen];
  4. receiveBufferManager.ReadBuffer(freame_byte, 0, frameLen);
  5. receiveBufferManager.Clear(frameLen);

验证

TCP大数据连续测试一周没出现问题内存问题。

C#环形缓冲区(队列)完全实现的更多相关文章

  1. linux device driver —— 环形缓冲区的实现

    还是没有接触到怎么控制硬件,但是在书里看到了一个挺巧妙的环形缓冲区实现. 此环形缓冲区实际为一个大小为bufsize的一维数组,有一个rp的读指针,一个wp的写指针. 在数据满时写进程会等待读进程读取 ...

  2. 35.Linux-分析并制作环形缓冲区

    在上章34.Linux-printk分析.使用printk调试驱动里讲述了: printk()会将打印信息存在内核的环形缓冲区log_buf[]里, 可以通过dmesg命令来查看log_buf[] 1 ...

  3. input子系统事件处理层(evdev)的环形缓冲区【转】

    在事件处理层(evdev.c)中结构体evdev_client定义了一个环形缓冲区(circular buffer),其原理是用数组的方式实现了一个先进先出的循环队列(circular queue), ...

  4. STM32进阶之串口环形缓冲区实现(转载)

    转载自微信公众号“玩转单片机”,感谢原作者“杰杰”. 队列的概念 在此之前,我们来回顾一下队列的基本概念:队列 (Queue):是一种先进先出(First In First Out ,简称 FIFO) ...

  5. STM32进阶之串口环形缓冲区实现

    队列的概念 在此之前,我们来回顾一下队列的基本概念: 队列 (Queue):是一种先进先出(First In First Out ,简称 FIFO)的线性表,只允许在一端插入(入队),在另一端进行删除 ...

  6. [LeetCode] Design Circular Deque 设计环形双向队列

    Design your implementation of the circular double-ended queue (deque). Your implementation should su ...

  7. 环形缓冲区-模仿linux kfifo【转】

    转自:https://blog.csdn.net/vertor11/article/details/53741681 struct kfifo{ uint8_t *buffer; uint32_t i ...

  8. linux网络编程--Circular Buffer(Ring Buffer) 环形缓冲区的设计与实现【转】

    转自:https://blog.csdn.net/yusiguyuan/article/details/18368095 1. 应用场景 网络编程中有这样一种场景:需要应用程序代码一边从TCP/IP协 ...

  9. linux下C语言实现多线程通信—环形缓冲区,可用于生产者(producer)/消费者(consumer)【转】

    转自:http://blog.chinaunix.net/uid-28458801-id-4262445.html 操作系统:ubuntu10.04 前言:     在嵌入式开发中,只要是带操作系统的 ...

随机推荐

  1. Java基础 -- 泛型之泛型参数

    泛型机制常用的参数有3个: “?”代表任意类型.如果只指定了<?>,而没有extends,则默认是允许任意类. extends关键字声明了类型的上界,表示参数化的类型可能是所指定的类型,或 ...

  2. ListBox点击改变相应的值

    private void lbCity_SelectedIndexChanged(object sender, EventArgs e) { 写逻辑 }

  3. Postman使用-2

    转载:https://www.cnblogs.com/yunman/p/7884537.html Postman是一款功能强大的网页调试与发送网页HTTP请求的Chrome插件. 接口请求流程 一.g ...

  4. angular1 表单验证demo

    这是一个angular1 验证表单的小栗子: 先看代码: <div ng-controller="myController"> <form name=" ...

  5. Complex复数类——课堂作业

    代码: #include<iostream> #include<cmath> using namespace std; class Complex { public: Comp ...

  6. [Xcode 实际操作]四、常用控件-(14)使用UIWebView控件加载本地HTML

    目录:[Swift]Xcode实际操作 本文将演示使用网页视图,加载并渲染网页代码. 在项目导航区,打开视图控制器的代码文件[ViewController.swift] import UIKit im ...

  7. 使用pods添加第三方的时候,出现ld: library not found for -lpop

    ld: library not found for -lpop 错误,是在使用pods添加第三方的时候,出现的编译错误,同时伴随着的是error: linker command failed with ...

  8. win10移动热点问题

    1.问题(win10移动热点相关) 具体描述: win10通过网线连接上网,打开移动热点后手机无法连接. 如下图所示,win10打开热,然后进入设置界面设置wlan名称和密码,手机填好密码,连接热点发 ...

  9. linux下python3的安装(已安装python2的情况下)

    前段时间想自学一下python,就在虚拟机里已安装python2.7的情况下又安装了最新版python3.6.4.于是问题来了..只要一打开终端就出现一大段错误代码(忘记截图了),当时看到是ros和p ...

  10. STP-18-Port-Channl上的负载均衡

    Ether Channel通过在多条链路上传输多个数据帧,增加了可用带宽.一个以太网帧总是通过一个Ether Channel中的一条链路传输.针对数据帧地址字段执行散列计算能够产生一个编号,标识这个数 ...