首先,我应该提一下,我没有可用的内部Stream
对象。而不是那样,我确实有这个对象:
public interface IChannel
{
void Send(byte[] data);
event EventHandler<byte[]> Receive;
}
我想实现一个Stream类,如下所示:
public class ChannelStream : Stream
{
private readonly IChannel _channel;
public ChannelStream(IChannel channel)
{
this._channel = channel;
}
// TODO: Implement Stream class
}
我需要的功能与NetworkStream
非常相似:
将字节写入我的流应该将这些字节添加到缓冲区,并在调用_channel.Send
后调用Flush()
Stream还将侦听_channel.Receive
个事件,并将字节添加到另一个内部缓冲区,直到从流中读取它们。如果Stream没有任何可用数据,则应阻止直到新数据可用。
然而,我正在努力实施。我已经在内部使用了两个MemoryStream
进行了实验,但这导致缓冲区继续吃掉越来越多的ram。
我可以使用哪种收集/流来实现我的流?
答案 0 :(得分:2)
从集合中考虑你需要的东西并从那里开始。
当您需要某种类型的集合时,您应该考虑以下几个问题:
您是否需要随机访问集合中的项目?
多个线程是否会访问该集合?
读取后是否需要保留集合中的数据?
订购重要吗?如果是这样,什么顺序 - 通过一些比较添加订单,反向添加订单,项目排序?
对于这种情况下的输出缓冲区,答案是no,yes,no和yes:添加顺序。这几乎是ConcurrentQueue
课的单挑。这允许您从一个或多个源添加对象,这些对象不需要与读取它们的代码位于同一个线程中。它不会让你随意索引收集(好吧,不是直接),你似乎并不需要。
我使用相同的类型作为输入缓冲区,使用&#39;当前块&#39;缓冲区,用于保存最近读取的缓冲区,包含在一些简单的对象锁定语义中,以处理任何线程问题。
输出部分如下所示:
// Output buffer
private readonly ConcurrentQueue<byte[]> _outputBuffer = new ConcurrentQueue<byte[]>();
public override void Write(byte[] buffer, int offset, int count)
{
// Copy written data to new buffer and add to output queue
byte[] data = new byte[count];
Buffer.BlockCopy(buffer, offset, data, 0, count);
_outputBuffer.Enqueue(data);
}
public override void Flush()
{
// pull everything out of the queue and send to wherever it is going
byte[] curr;
while (_outputBuffer.TryDequeue(out curr))
internalSendData(curr);
}
internalSendData
方法是数据传输到网络的地方。
读缓冲有点复杂:
// collection to hold unread input data
private readonly ConcurrentQueue<byte[]> _inputBuffer = new ConcurrentQueue<byte[]>();
// current data block being read from
private byte[] _inputCurrent = null;
// read offset in current block
private short _inputPos = 0;
// object for locking access to the above.
private readonly object _inputLock = new object();
public override int Read(byte[] buffer, int offset, int count)
{
int readCount = 0;
lock(_inputLock)
{
while (count > 0)
{
if (_inputCurrent == null || _inputCurrent.Length <= _inputPos)
{
// read next block from input buffer
if (!_inputBuffer.TryDequeue(out _inputCurrent))
break;
_inputPos = 0;
}
// copy bytes to destination
int nBytes = Math.Min(count, _inputCurrent.Length - _inputPos);
Buffer.BlockCopy(_inputCurrent, _inputPos, buffer, offset, nBytes);
// adjust all the offsets and counters
readCount += nBytes;
offset += nBytes;
count -= nBytes;
_inputPos += (short)nBytes;
}
}
return readCount;
}
希望这是有道理的。
使用队列进行这种软缓冲意味着只要数据被延迟发送或读取,数据就只保存在内存中。一旦你调用Flush
输出缓冲区的内存被释放用于垃圾收集,所以你不必担心内存爆炸,除非你试图发送比实际传输机制快得多的内存处理。但是,如果你每秒排队几兆字节的数据来通过ADSL连接,那么没有什么可以拯救你:P
我在上面添加了一些改进,比如一些检查,以确保在缓冲区处于合理的级别后自动调用Flush
。