簡體   English   中英

實施自定義流

[英]Implementing a custom stream

首先,我要提到的是我沒有可用的內部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()被調用后發送。
流還將偵聽_channel.Receive事件,並將字節添加到另一個內部緩沖區中,直到從流中讀取它們為止。 如果Stream沒有可用的數據,它將阻塞直到新數據可用。

但是,我正在為實施而苦苦掙扎。 我已經在內部使用兩個MemoryStream進行了實驗,但這導致緩沖區繼續吃越來越多的RAM。

我可以使用哪種集合/流來實現我的流?

考慮您需要從集合中得到什么,然后從那里開始。

當您需要某種類型的集合時,應考慮以下幾個問題:

  1. 您是否需要隨機訪問集合中的項目?

  2. 集合將被多個線程訪問嗎?

  3. 讀取后是否需要將數據保留在集合中?

  4. 訂購重要嗎? 如果是這樣,通過某種比較,什么順序-添加順序,反向添加順序,項目順序?

對於這種情況下的輸出緩沖區,答案為否,是,否和是:添加順序。 哪個幾乎可以選出ConcurrentQueue類。 這使您可以從一個或多個源中添加對象,這些對象不需要與正在讀取它們的代碼位於同一線程中。 它不允許您隨意對集合進行索引(當然,無論如何也不能直接對其建立索引),而這似乎並不需要。

我將對輸入緩沖區使用相同的類型,並使用“當前塊”緩沖區來保存最近讀取的緩沖區,並包裝在一些簡單的對象鎖定語義中以處理任何線程問題。

輸出部分看起來像這樣:

// 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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM