【问题标题】:Reading NetworkStream doesn't advance stream阅读 NetworkStream 不会推进流
【发布时间】:2011-10-27 04:48:17
【问题描述】:

我有一个客户端-服务器应用程序,其中服务器传输一个 4 字节整数,指定下一次传输的大小。当我在客户端读取 4 字节整数(指定 FILE_SIZE)时,下一次读取流时,我会读取 FILE_SIZE + 4 字节。

从该流中读取时是否需要将偏移量指定为 4,或者是否有办法自动推进 NetworkStream 以便我的偏移量始终为 0?

服务器

NetworkStream theStream = theClient.getStream();

//...
//Calculate file size with FileInfo and put into byte[] size
//...

theStream.Write(size, 0, size.Length);
theStream.Flush();

客户

NetworkStream theStream = theClient.getStream();

//read size
byte[] size = new byte[4];
int bytesRead = theStream.Read(size, 0, 4);

...

//read content
byte[] content = new byte[4096];
bytesRead = theStream.Read(content, 0, 4096);

Console.WriteLine(bytesRead); // <-- Prints filesize + 4 

【问题讨论】:

  • 什么是filesize?和size.Length一样吗?
  • 确定size.Length(服务器端)是4吗? (而且您在客户端第二次读取时没有读取下一个文件的大小?)
  • 什么是大小?你所做的将是不稳定的。使用 BinaryReader/BinaryWriter 代替 ReadInt32/WriteInt32 以避免大小问题。
  • NetworkStream 不支持随机访问网络数据流。 CanSeek 属性的值,表示流是否支持查找,始终为 false;读取 Position 属性、读取 Length 属性或调用 Seek 方法将引发 NotSupportedException。 - 来自MSDN。也许您可以在读取内容时读取流并丢弃前 4 个字节?
  • size 是通过 BitConverter.GetBytes(fileInfo.Length) 获得的字节[];其中 fileInfo = new FileInfo(myFileName);

标签: c# stream networkstream


【解决方案1】:

对;找到了; FileInfo.Lengthlong;您的电话:

binWrite.Write(fileInfo.Length);

写入 8 个字节,little-endian。然后,您通过以下方式阅读:

filesize = binRead.ReadInt32();

which little-endian 将为您提供相同的值(至少对于 32 位)。不过,流中有 4 个 00 字节未使用(来自 long 的高字节) - 因此 4 字节不匹配。

使用以下之一:

  • binWrite.Write((int)fileInfo.Length);
  • filesize = binRead.ReadInt64();

【讨论】:

    【解决方案2】:

    NetworkStream 肯定会进步,但是在这两种情况下,您的阅读都不可靠;经典的“阅读已知数量的内容”是:

    static void ReadAll(Stream source, byte[] buffer, int bytes) {
      if(bytes > buffer.Length) throw new ArgumentOutOfRangeException("bytes");
      int bytesRead, offset = 0;
      while(bytes > 0 && (bytesRead = source.Reader(buffer, offset, bytes)) > 0) {
        offset += bytesRead;
        bytes -= bytesRead;
      }
      if(bytes != 0) throw new EndOfStreamException();
    }
    

    与:

    ReadAll(theStream, size, 4);
    ...
    ReadAll(theStream, content, contentLength);
    

    还要注意,在解析长度前缀时需要注意字节顺序。

    我怀疑您根本没有阅读完整的数据。

    【讨论】:

    • @aloneguid OP 使用的是Stream,并且就个人而言我更喜欢避免使用BinaryReader(但是,我倾向于对我的编码非常挑剔)
    • BinaryReader 在构造函数中接受 Stream 以及源编码。避免它的原因是什么?
    • @aloneguid 主要是因为对于我正在做的事情,它没有添加任何有用的东西。唯一的边缘案例是“阅读已知数量的内容”,这对于重现来说是微不足道的。此外,读/写操作不一定与您所需的数据格式一致(尤其是但不仅与字节序有关)。对于上下文,我维护了一个 OSS 二进制序列化库,具有非常具体的编码,它根本不匹配 BinaryReader 很好;p
    • 那只是在你的特殊情况下,我认为 BinaryXXX 从头开始​​编写二进制协议时可以节省几个晚上,C# 双方;)
    • @aloneguid 如果节省几个晚上是目标,更好的选择是:使用预滚动的二进制序列化库;p(除了,也许,我的夜晚......)
    猜你喜欢
    • 1970-01-01
    • 2011-05-25
    • 1970-01-01
    • 2010-11-07
    • 2014-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多