سؤال

حاليا ، ليس هناك أ NetworkStream.Peek الطريقة في C#. ما هي أفضل طريقة لتنفيذ مثل هذه الطريقة التي تعمل تمامًا NetworkStream.ReadByte إلا أن العائد byte لم تتم إزالته بالفعل من Stream?

هل كانت مفيدة؟

المحلول

إذا لم تكن بحاجة إلى استرداد البايت فعليًا ، فيمكنك الرجوع إلى DataAvailable منشأه.

خلاف ذلك ، يمكنك لفه مع StreamReader واستدعاءها Peek طريقة.

لاحظ أن أيا من هذه غير موثوقة بشكل خاص للقراءة من دفق الشبكة ، بسبب مشكلات الكمون. قد تصبح البيانات متوفرة (موجودة في المخزن المؤقت للقراءة) في اللحظة بعد، بعدما أنت نظرة خاطفة.

لست متأكدًا من ما تنوي فعله بهذا ، ولكن Read طريقة على NetworkStream هي مكالمة حظر ، لذلك لا تحتاج حقًا إلى التحقق من الحالة ، حتى لو كنت تتلقى قطعًا. إذا كنت تحاول الحفاظ على استجابة التطبيق أثناء القراءة من الدفق ، فيجب عليك استخدام مؤشر ترابط أو مكالمة غير متزامنة لتلقي البيانات بدلاً من ذلك.

تحرير: وفقا ل هذا المشنور, StreamReader.Peek هو عربات التي تجرها الدواب على أ NetworkStream, ، أو على الأقل لديه سلوك غير موثق ، لذا كن حذرًا إذا اخترت السير في هذا الطريق.


تم التحديث - الرد على التعليقات

فكرة "نظرة خاطفة" على الدفق الفعلي نفسه مستحيل في الواقع ؛ إنه مجرد دفق ، وبمجرد استلام البايت ، لم يعد على الدفق. تدعم بعض التدفقات البحث حتى تتمكن من إعادة قراءة هذا البايت من الناحية الفنية ، ولكن NetworkStream ليس أحدهم.

ينطبق النظر فقط عند قراءة الدفق في المخزن المؤقت ؛ بمجرد أن تكون البيانات في المخزن المؤقت ، يكون النظر أمرًا سهلاً لأنك فقط تحقق من كل ما في الموضع الحالي في المخزن المؤقت. لهذا السبب أ StreamReader قادر على القيام بذلك ؛ رقم Stream سيكون لدى الفصل بشكل عام خاص به Peek طريقة.

الآن ، لهذه المشكلة على وجه التحديد ، أتساءل عما إذا كانت هذه هي الإجابة الصحيحة أم لا. أفهم فكرة اختيار طريقة للمعالجة ديناميكيًا ، لكن هل أنت حقًا هل تحتاج إلى القيام بذلك على التيار الخام؟ لا يمكنك قراءة الدفق في صفيف بايت أولاً ، أو حتى نسخه إلى أ MemoryStream, ، ومعالجةها من تلك النقطة فصاعدا؟

المشكلة الرئيسية التي أراها هي أنه إذا حدث شيء سيء عندما تقرأ من دفق الشبكة ، فقد اختفت البيانات. ولكن إذا قرأته في موقع مؤقت أولاً ، فيمكنك تصحيح هذا. يمكنك معرفة ما هي البيانات ولماذا الكائن الذي كان يحاول معالجة البيانات فشل في منتصف الطريق.

بشكل عام ، أول شيء تريد القيام به بـ NetworkStream يقرأها في المخزن المؤقت المحلي. السبب الوحيد الذي يجعلني أفكر في عدم القيام بذلك هو إذا كنت تقرأ كمية هائلة من البيانات - وحتى ذلك الحين ، قد أفكر في استخدام نظام الملفات كمخزن مؤقت وسيط إذا لم يكن مناسبًا للذاكرة.

لا أعرف متطلباتك الدقيقة ، ولكن مما تعلمته حتى الآن ، ستكون نصيحتي هي: لا تحاول معالجة بياناتك مباشرة من NetworkStream ما لم يكن هناك سبب مقنع للقيام بذلك. فكر في قراءة البيانات في الذاكرة أو على القرص أولاً ، ثم معالجة النسخة.

نصائح أخرى

واجهت نفس "نظرة خاطفة على الرقم السحري ، ثم قررت المعالج الدفق الذي يجب إرسال الدفق إلى" متطلبات "، وللأسف لا أستطيع أن يخرج عن هذه المشكلة - كما هو مقترح في تعليقات آرونوت - من خلال تمرير البايتات المستهلكة بالفعل في طرق معالجة الدفق في معلمات منفصلة ، لأن هذه الأساليب معطاة ويتوقعون system.io.stream ولا شيء آخر.

لقد قمت بحل هذا عن طريق إنشاء عالمي أكثر أو أقل Peekablestream الفصل الذي يلف تيار. إنه يعمل مع NetworkStreams ، ولكن أيضًا لأي دفق آخر ، شريطة لك Stream.Canread هو - هي.


يحرر

بدلاً من ذلك ، يمكنك استخدام العلامة التجارية الجديدة ReadSeekableStream وافعل

var readSeekableStream = new ReadSeekableStream(networkStream, /* >= */ count);
...
readSeekableStream.Read(..., count);
readSeekableStream.Seek(-count, SeekOrigin.Current);

في أي حال ، هنا يأتي PeekableStream:

/// <summary>
/// PeekableStream wraps a Stream and can be used to peek ahead in the underlying stream,
/// without consuming the bytes. In other words, doing Peek() will allow you to look ahead in the stream,
/// but it won't affect the result of subsequent Read() calls.
/// 
/// This is sometimes necessary, e.g. for peeking at the magic number of a stream of bytes and decide which
/// stream processor to hand over the stream.
/// </summary>
public class PeekableStream : Stream
{
    private readonly Stream underlyingStream;
    private readonly byte[] lookAheadBuffer;

    private int lookAheadIndex;

    public PeekableStream(Stream underlyingStream, int maxPeekBytes)
    {
        this.underlyingStream = underlyingStream;
        lookAheadBuffer = new byte[maxPeekBytes];
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
            underlyingStream.Dispose();

        base.Dispose(disposing);
    }

    /// <summary>
    /// Peeks at a maximum of count bytes, or less if the stream ends before that number of bytes can be read.
    /// 
    /// Calls to this method do not influence subsequent calls to Read() and Peek().
    /// 
    /// Please note that this method will always peek count bytes unless the end of the stream is reached before that - in contrast to the Read()
    /// method, which might read less than count bytes, even though the end of the stream has not been reached.
    /// </summary>
    /// <param name="buffer">An array of bytes. When this method returns, the buffer contains the specified byte array with the values between offset and
    /// (offset + number-of-peeked-bytes - 1) replaced by the bytes peeked from the current source.</param>
    /// <param name="offset">The zero-based byte offset in buffer at which to begin storing the data peeked from the current stream.</param>
    /// <param name="count">The maximum number of bytes to be peeked from the current stream.</param>
    /// <returns>The total number of bytes peeked into the buffer. If it is less than the number of bytes requested then the end of the stream has been reached.</returns>
    public virtual int Peek(byte[] buffer, int offset, int count)
    {
        if (count > lookAheadBuffer.Length)
            throw new ArgumentOutOfRangeException("count", "must be smaller than peekable size, which is " + lookAheadBuffer.Length);

        while (lookAheadIndex < count)
        {
            int bytesRead = underlyingStream.Read(lookAheadBuffer, lookAheadIndex, count - lookAheadIndex);

            if (bytesRead == 0) // end of stream reached
                break;

            lookAheadIndex += bytesRead;
        }

        int peeked = Math.Min(count, lookAheadIndex);
        Array.Copy(lookAheadBuffer, 0, buffer, offset, peeked);
        return peeked;
    }

    public override bool CanRead { get { return true; } }

    public override long Position
    {
        get
        {
            return underlyingStream.Position - lookAheadIndex;
        }
        set
        {
            underlyingStream.Position = value;
            lookAheadIndex = 0; // this needs to be done AFTER the call to underlyingStream.Position, as that might throw NotSupportedException, 
                                // in which case we don't want to change the lookAhead status
        }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        int bytesTakenFromLookAheadBuffer = 0;
        if (count > 0 && lookAheadIndex > 0)
        {
            bytesTakenFromLookAheadBuffer = Math.Min(count, lookAheadIndex);
            Array.Copy(lookAheadBuffer, 0, buffer, offset, bytesTakenFromLookAheadBuffer);
            count -= bytesTakenFromLookAheadBuffer;
            offset += bytesTakenFromLookAheadBuffer;
            lookAheadIndex -= bytesTakenFromLookAheadBuffer;
            if (lookAheadIndex > 0) // move remaining bytes in lookAheadBuffer to front
                // copying into same array should be fine, according to http://msdn.microsoft.com/en-us/library/z50k9bft(v=VS.90).aspx :
                // "If sourceArray and destinationArray overlap, this method behaves as if the original values of sourceArray were preserved
                // in a temporary location before destinationArray is overwritten."
                Array.Copy(lookAheadBuffer, lookAheadBuffer.Length - bytesTakenFromLookAheadBuffer + 1, lookAheadBuffer, 0, lookAheadIndex);
        }

        return count > 0
            ? bytesTakenFromLookAheadBuffer + underlyingStream.Read(buffer, offset, count)
            : bytesTakenFromLookAheadBuffer;
    }

    public override int ReadByte()
    {
        if (lookAheadIndex > 0)
        {
            lookAheadIndex--;
            byte firstByte = lookAheadBuffer[0];
            if (lookAheadIndex > 0) // move remaining bytes in lookAheadBuffer to front
                Array.Copy(lookAheadBuffer, 1, lookAheadBuffer, 0, lookAheadIndex);
            return firstByte;
        }
        else
        {
            return underlyingStream.ReadByte();
        }
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        long ret = underlyingStream.Seek(offset, origin);
        lookAheadIndex = 0; // this needs to be done AFTER the call to underlyingStream.Seek(), as that might throw NotSupportedException,
                            // in which case we don't want to change the lookAhead status
        return ret;
    }

    // from here on, only simple delegations to underlyingStream

    public override bool CanSeek { get { return underlyingStream.CanSeek; } }
    public override bool CanWrite { get { return underlyingStream.CanWrite; } }
    public override bool CanTimeout { get { return underlyingStream.CanTimeout; } }
    public override int ReadTimeout { get { return underlyingStream.ReadTimeout; } set { underlyingStream.ReadTimeout = value; } }
    public override int WriteTimeout { get { return underlyingStream.WriteTimeout; } set { underlyingStream.WriteTimeout = value; } }
    public override void Flush() { underlyingStream.Flush(); }
    public override long Length { get { return underlyingStream.Length; } }
    public override void SetLength(long value) { underlyingStream.SetLength(value); }
    public override void Write(byte[] buffer, int offset, int count) { underlyingStream.Write(buffer, offset, count); }
    public override void WriteByte(byte value) { underlyingStream.WriteByte(value); }
}

إذا كان لديك إمكانية الوصول إلى Socket كائن ، يمكنك تجربة ملف استلام الطريقة, ، المرور SocketFlags.Peek. هذا مماثل ل MSG_PEEK العلم الذي يمكن نقله إلى recv اتصل في مآخذ BSD أو Winsock.

هنا بسيط جدا PeekStream التنفيذ الذي يسمح لك بإلقاء نظرة على عدد معين من البايتات في بداية الدفق فقط (على عكس القدرة على النظر في أي وقت). يتم إرجاع البايتات المشوهة على أنها أ Stream أنفسهم ، لتقليل التغييرات إلى التعليمات البرمجية الحالية.

إليك كيف تستخدمه:

Stream nonSeekableStream = ...;
PeekStream peekStream = new PeekStream(nonSeekableStream, 30); // Peek max 30 bytes
Stream initialBytesStream = peekStream.GetInitialBytesStream();
ParseHeaders(initialBytesStream);  // Work on initial bytes of nonSeekableStream
peekStream.Read(...) // Read normally, the read will start from the beginning

GetInitialBytesStream() يعيد دفقًا يمكن البحث عنه يحتوي على ما يصل إلى ما يصل إلى peekSize البايتات الأولية للتيار الأساسي (أقل إذا كان الدفق أقصر من peekSize).

بسبب بساطتها ، يجب أن تكون قراءة Peekstream أبطأ بشكل هامشي (إن وجدت) فقط من قراءة الدفق الأساسي مباشرة.

public class PeekStream : Stream
{
    private Stream m_stream;
    private byte[] m_buffer;
    private int m_start;
    private int m_end;

    public PeekStream(Stream stream, int peekSize)
    {
        if (stream == null)
        {
            throw new ArgumentNullException("stream");
        }
        if (!stream.CanRead)
        {
            throw new ArgumentException("Stream is not readable.");
        }
        if (peekSize < 0)
        {
            throw new ArgumentOutOfRangeException("peekSize");
        }
        m_stream = stream;
        m_buffer = new byte[peekSize];
        m_end = stream.Read(m_buffer, 0, peekSize);
    }

    public override bool CanRead
    {
        get
        {
            return true;
        }
    }

    public override bool CanWrite
    {
        get
        {
            return false;
        }
    }

    public override bool CanSeek
    {
        get
        {
            return false;
        }
    }

    public override long Length
    {
        get
        {
            throw new NotSupportedException();
        }
    }

    public override long Position
    {
        get
        {
            throw new NotSupportedException();
        }
        set
        {
            throw new NotSupportedException();
        }
    }

    public MemoryStream GetInitialBytesStream()
    {
        return new MemoryStream(m_buffer, 0, m_end, false);
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotSupportedException();
    }

    public override void SetLength(long value)
    {
        throw new NotSupportedException();
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        // Validate arguments
        if (buffer == null)
        {
            throw new ArgumentNullException("buffer");
        }
        if (offset < 0)
        {
            throw new ArgumentOutOfRangeException("offset");
        }
        if (offset + count > buffer.Length)
        {
            throw new ArgumentOutOfRangeException("count");
        }

        int totalRead = 0;

        // Read from buffer
        if (m_start < m_end)
        {
            int toRead = Math.Min(m_end - m_start, count);
            Array.Copy(m_buffer, m_start, buffer, offset, toRead);
            m_start += toRead;
            offset += toRead;
            count -= toRead;
            totalRead += toRead;
        }

        // Read from stream
        if (count > 0)
        {
            totalRead += m_stream.Read(buffer, offset, count);
        }

        // Return total bytes read
        return totalRead;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        throw new NotImplementedException();
    }

    public override int ReadByte()
    {
        if (m_start < m_end)
        {
            return m_buffer[m_start++];
        }
        else
        {
            return m_stream.ReadByte();
        }
    }

    public override void Flush()
    {
        m_stream.Flush();
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            m_stream.Dispose();
        }
        base.Dispose(disposing);
    }
}

إخلاء المسئولية: يتم أخذ Peekstream أعلاه من برنامج عمل ، لكنه لم يتم اختباره بشكل شامل ، لذلك قد يحتوي على الأخطاء. إنه يعمل بالنسبة لي ، لكنك قد تكشف عن بعض الحالات التي تفشل فيها.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top