Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Count bytes transmitted by TcpClient via NetworkStream BinaryReader/BinaryWriter

I am using a networking protocol built around TcpClient, using BinaryReader to read bytes from the underlying NetworkStream (and, conversely, using BinaryWriter to write).

The protocol transmits strings in UTF-8 encoding, and is calling reader.ReadString() to read them from the stream (using writer.Write(someStr) to write).

Is there an easy way to determine the number of bytes read from (or written to) the NetworkStream, without having to jump through hoops to calculate the actual byte lengths of the strings transmitted?

Note that BinaryWriter.Write() writes a 7-bit-encoded integer before the actual bytes of the string, which makes any manual calculation additionally complex.

Also note that NetworkStream does not support the Position property, since it complains about not being able to Seek.

Furthermore, I would like to avoid introducing intermediaries that have to copy/scan data into the process of reading/writing as not to affect the performance of the whole system.

Is there an easy, high-level way of counting bytes passing through the network interface without having to manually account for the encoding and lengths of the strings?

like image 906
Optimax Avatar asked Dec 14 '15 18:12

Optimax


1 Answers

For those who are curious about how I implemented the byte-counting stream, here it is in all of its glory (or infamy, as the case may be):

using System;
using System.IO;

namespace Streams
{
    /// <summary>
    /// A wrapper around a <see cref="Stream"/> that keeps track of the number of bytes read and written.
    /// </summary>
    public class ByteCountingStream : Stream
    {
        private readonly Stream inner;

        private long totalBytesWritten;
        private long totalBytesRead;


        public ByteCountingStream(Stream inner)
        {
            this.inner = inner;
        }

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

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

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

        public override int Read(byte[] buffer, int offset, int count)
        {
            int readBytes = inner.Read(buffer, offset, count);
            totalBytesRead += readBytes;
            return readBytes;
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            inner.Write(buffer, offset, count);
            totalBytesWritten += count;
        }

        public override bool CanRead => true;
        public override bool CanSeek => false;
        public override bool CanWrite => true;

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

        public override long Position { get; set; }

        public long TotalBytesWritten => totalBytesWritten;
        public long TotalBytesRead => totalBytesRead;
    }
}

The implementation passes the buffer to the underlying stream so, indeed, there is no data copying involved.

like image 110
Optimax Avatar answered Sep 29 '22 16:09

Optimax