Jonathan Wood
Jonathan Wood

Reputation: 67345

Reading String from Stream

I am encrypting data to a stream. If, for example, my data is of type Int32, I will use BitConverter.GetBytes(myInt) to get the bytes and then write those bytes to the stream.

To read the data back, I read sizeof(Int32) to determine the number of bytes to read, read those bytes, and then use BitConverter.ToInt32(byteArray, 0) to convert the bytes back to an Int32.

So how would I do this with a string? Writing the string is no problem. But the trick when reading the string is knowing how many bytes to read before I can then convert it back to a string.

I have found similar questions, but they seem to assume the string occupies the entire stream and just read to the end of the stream. But here, I can have any number of other items before and after the string.

Note that StringReader is not an option here since I want the option of handling file data that may be larger than I want to load into memory.

Upvotes: 1

Views: 443

Answers (2)

Andre Andersen
Andre Andersen

Reputation: 1211

You would normally send a content length header, and then read the length determined by that information.

Here is some sample code:

public async Task ContinouslyReadFromStream(NetworkStream sourceStream, CancellationToken token)
{
    while (!ct.IsCancellationRequested && sourceStream.CanRead)
    {
        while (sourceStream.CanRead && !sourceStream.DataAvailable)
        {
            // Avoid potential high CPU usage when doing stream.ReadAsync
            // while waiting for data
            Thread.Sleep(10);
        }

        var lengthOfMessage = BitConverter.ToInt32(await ReadExactBytesAsync(stream, 4, ct), 0);
        var content = await ReadExactBytesAsync(stream, lengthOfMessage, ct);
        // Assuming you use UTF8 encoding
        var stringContent = Encoding.UTF8.GetString(content);

    }
}


protected static async Task<byte[]> ReadExactBytesAsync(Stream stream, int count, CancellationToken ct)
{
    var buffer = new byte[count];
    var totalBytesRemaining = count;
    var totalBytesRead = 0;
    while (totalBytesRemaining != 0)
    {
        var bytesRead = await stream.ReadAsync(buffer, totalBytesRead, totalBytesRemaining, ct);
        ct.ThrowIfCancellationRequested();
        totalBytesRead += bytesRead;
        totalBytesRemaining -= bytesRead;
    }
    return buffer;
}

Upvotes: 1

WDS
WDS

Reputation: 984

The solutions that come to mind are to either provide a predetermined sentinel value to signal the end of the string (ASM uses a 0 byte for this, for example), or to provide a fixed-length block of metadata ahead of each new datatype. In that block of metadata would be the type and the length, plus whatever other information you found it useful to include.

For compactness I would use the sentinel value if it will work in your system.

Upvotes: 1

Related Questions