通过NetworkStream BinaryReader/BinaryWriter计算TcpClient传输的字节数

7
我正在使用围绕TcpClient构建的网络协议,使用BinaryReader从底层NetworkStream中读取字节(反之亦然,使用BinaryWriter进行写入)。
该协议以UTF-8编码传输字符串,并调用reader.ReadString()从流中读取它们(使用writer.Write(someStr)进行写入)。
有没有一种简单的方法来确定从(或写入到)NetworkStream中读取的字节数,而不必通过各种计算来计算传输的字符串的实际字节长度?
请注意,BinaryWriter.Write()在实际字符串字节之前写入了一个7位编码的整数,这使得任何手动计算都变得更加复杂。
还要注意,NetworkStream不支持Position属性,因为它会抱怨无法进行Seek操作。
此外,我希望避免引入需要将数据复制/扫描到读取/写入过程中的中间件,以免影响整个系统的性能。
是否有一种简便的高级方法来计算通过网络接口传递的字节数,而不必手动考虑字符串的编码和长度?

你想通过这样做实现什么目的?你可以在网络流和读取器之间插入一个自定义流来计算字节数。 - usr
@usr 我只需要知道读/写的总字节数 - 用于报告目的。底层协议栈中的某些内容必须知道发送/接收的字节数...我想尽可能轻松地获取这些信息。 - Optimax
2个回答

2

对于那些好奇我如何实现字节计数流的人,这就是它的全部荣耀(或者说是臭名昭著,视情况而定):

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;
    }
}

实现将缓冲区传递给底层流,因此确实不涉及数据复制。

1
Length 看起来有问题。它总是返回 0 吗?应该抛出异常。 - usr
1
位置也是。我几乎在新的简洁的6.0语法中忽略了它。需要习惯。此外,我认为如果没有处理,该类会非常危险。 - usr

1
你可以在网络流和读取器之间插入一个自定义流,用于计算字节数。
不需要复制数据。只需将通过的字节数加到计数器中即可。

你能否画个例子来说明如何做到这一点? - Optimax
有具体的问题吗?从Stream派生并以这种方式包装另一个流实例。 - usr
那不是必须要涉及到数据缓存吗?我不想拖慢速度。 - Optimax
我已经说过不会缓存了。如果你认为这不是真的,请给出一个有关为什么会有缓存的理由。也许你应该开始实现流,这样你就可以看到它的样子了。例如,在Read方法中,你将输入缓冲区传递给内部流。 - usr
我说过我想要一个简单的方法。虽然创建自定义流并不可怕 - 你是对的,比我想象的要容易 :-) - 确保它在以前使用NetworkStream的所有地方都被使用证明是一个很大的麻烦。这不是我正在寻找的简单解决方案,但它可以工作,所以我会接受你的答案。谢谢。 - Optimax

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接