7

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?

Optimax
  • 1,534
  • 2
  • 16
  • 23
  • What do you want to achieve doing that? You could insert a custom stream between the networkstream and the reader that counts bytes. – usr Dec 14 '15 at 18:25
  • @usr I just need to know the total bytes read/written - for reporting purposes. *Something* in the underlying protocol stack must know the count of bytes sent/received... I'd like to get at that information - with minimal hassle. – Optimax Dec 14 '15 at 18:32

2 Answers2

2

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.

Optimax
  • 1,534
  • 2
  • 16
  • 23
  • 1
    `Length` looks broken. Does this not always return 0? Should throw. – usr Dec 14 '15 at 22:15
  • 1
    Position, too. I almost overlooked it with the new terse 6.0 syntax. Need to get used to that. Also, I think without disposal that class is quite dangerous. – usr Dec 15 '15 at 22:51
1

You could insert a custom stream between the networkstream and the reader that counts bytes.

It is not necessary to copy data to do that. Just add the number of bytes that passes by to a counter.

usr
  • 168,620
  • 35
  • 240
  • 369
  • Would you be able to sketch out an example of how to do that? – Optimax Dec 14 '15 at 18:38
  • Any concrete problems? Derive from Stream and wrap another stream instance that way. – usr Dec 14 '15 at 18:39
  • Wouldn't that have to involve buffering the data? I don't want to slow things down. – Optimax Dec 14 '15 at 18:43
  • I already said that there would be no buffering. If you don't think that's true provide a reason why there would be buffering. Maybe you should get started with the stream implementation so that you see how it looks like. In the Read method, for example, you pass the incoming buffer through to the inner stream. – usr Dec 14 '15 at 18:44
  • And I said I wanted an *easy* way. While creating the custom stream wasn't terrible - you were right, it was easier than I thought :-) - ensuring that it is used in all places where `NetworkStream` was being used previously proved a major hassle. Not the *easy* solution I was looking for, but it works, so I'll accept your answer. Thanks. – Optimax Dec 14 '15 at 19:58