Question

I have a TCP socket that I use for RTSP communication. As the data is a mixture of line based textual data and byte sized blocks, I attached a StreamReader to the tcpClient.GetStream(), and call .ReadLine() whenever needed for the textual data.

When I need to read a response body, I have a fixed byte count so I tried using stream.Read() but that blocks, presumably as the StreamReader has already read the data into its own buffer. As that does character encoding, it only reads a fixed number of characters rather than bytes.

Is there any way I can read a fixed number of bytes from the stream without scrapping the StreamReader altogether or hoping that the binary data/content is 7-bit safe (and in turn won't decode as UTF-8)? An alternative is to set the StreamReader's encoding to ASCII but that potentially breaks the rest of the protocol which is defined as UTF-8.

Setup:

this.rtspStream = this.rtspSocket.GetStream();
this.rtspReader = new StreamReader(this.rtspStream, Encoding.UTF8);

Text reading:

string line;
while ((line = this.rtspReader.ReadLine()) != string.Empty) {
    // ...
}

Binary reading:

byte[] responseBody = new byte[contentLength];
this.rtspStream.Read(responseBody, 0, contentLength);
Était-ce utile?

La solution

Looking at the StreamReader source, it is not possible to mix the use of the StreamReader and reading of bytes direct from the stream as the StreamReader reads bytes into its internal buffer then decodes all it can into a character buffer available for reading.

Setting the buffer size to 0 doesn't help as it forces this to a minimum of 128 bytes.

For my use, I needed to scrap the StreamReader altogether and replace the ReadLine() with something that will read directly from the stream for parsing myself.

private string ReadLine() {
    // Stringbuilder to insert the read characters in to
    StringBuilder line = new StringBuilder();

    // Create an array to store the maximum number of bytes for a single character
    byte[] bytes = new byte[this.encoding.GetMaxByteCount(1)];
    int byteCount = 0;

    while (true) {
        // Read the first byte
        bytes[0] = (byte)this.rtspStream.ReadByte();
        byteCount = 1;

        // If the encoding says this isn't a full character, read until it does
        while (this.encoding.GetCharCount(bytes, 0, byteCount) == 0) {
            bytes[byteCount++] = (byte)this.rtspStream.ReadByte();
        }

        // Get the unencoded character
        char thisChar = this.encoding.GetChars(bytes, 0, byteCount)[0];

        // Exit if it's a new line (/r/n or /n)
        if (thisChar == '\r') { continue; }
        if (thisChar == '\n') { break; }

        line.Append(thisChar);
    }
    return line.ToString();
}

Autres conseils

I presume you've already handled all of the headers, including, possibly, a Content-Length header. I'm assuming that you now want to read the content body, which may or may not be text content.

I think the best you'll be able to do is to read the entire content body in as a stream, then to wrap the content in a StreamReader in the case where the content is text:

List<string> lines = new List<string>();
byte[] responseBody = new byte[contentLength];
this.rtspStream.Read(responseBody, 0, contentLength);
if (contentIsText)
{
    using (var memoryStream = new MemoryStream(responseBody, 0, contentLength))
    {
        using (var reader = new StreamReader(memoryStream))
        {
            string line;
            while ((line = reader.ReadLine()) != string.Empty)
            {
                lines.Add(line);
            }
        }
    }
    // Do something with lines
}
else
{
    // Do something with responseBody
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top