C#环形缓冲区(队列)完全实现

时间:2022-01-28 18:17:45

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

为什么要用环形队列?
环形队列是在实际编程极为有用的数据结构,它有如下特点:
它是一个首尾相连的FIFO的数据结构,采用数组的线性空间,数据组织简单。能很快知道队列是否满为空。能以很快速度的来存取数据。
因为有简单高效的原因,甚至在硬件都实现了环形队列。 

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

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

调用实例
生产

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

消费

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

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:http://www.cnblogs.com/fanqie-liuxiao/p/5712094.html