.NET 阻塞套接字读取,直到 X 个字节可用?

3
假设我已经实现了一个简单的TCP协议,其中每条消息由以下内容组成:
  1. 表示数据长度的int
  2. 二进制数据,长度由1指定。
对于读取这样的消息,我希望得到类似以下的结果:
int length = input.ReadInt();
byte[] data = input.ReadBytes(length);

使用Socket.ReceiveNetworkStream.Read读取可用的字节数。我希望调用ReadBytes的时候能够阻塞,直到有length个字节可用。
有没有一种简单的方法来做到这一点,而不必循环读取,重启偏移量等待剩余数据?
在真实的应用程序中,读取可能应该异步完成或在后台线程上完成,但我现在暂时忽略了这个问题。重要的是能够使读取在所有数据可用之前不完成。
编辑
我知道我可以自己缓冲数据,并且我知道如何做到这一点。它只是一个循环,围绕Receive继续下一个偏移量。我要问的是,是否有可重复使用的这样的循环实现,无需任何自己的循环(或者另外一种可重复使用的异步实现,在所有数据可用时完成)。
6个回答

5

某处,某些地方必须要循环执行。毕竟,可能需要多次套接字读取。

我认为 BinaryReader.Read 会一直循环读取,直到它已经读取了你所要求的数量或者到达流的末尾,但是假设你希望在到达流的末尾时抛出一个异常,我个人会编写一个单独的方法。毕竟,在一个地方轻松实现并且可以重复使用。


1
BinaryReader.Read 文档说明它返回读取的字节数,并且如果没有那么多可用的字节,则可能少于请求的字节数。我猜我必须测试这意味着“现在可用的字节”还是“套接字关闭之前接收到的字节”。 - Anders Abel
1
@Anders:是的,文档不太清楚。我相信它会循环 - 但这种歧义只是写自己的方法的另一个原因。只需要大约7行代码 - 就去做吧 :) - Jon Skeet
太遗憾你不在Java中,read(new byte [9])将阻塞直到读取9个字节为止...我也在寻找简单的东西。请注意,我的模拟服务器中没有循环。我们只需使用read(new byte [1])获取框架大小头,然后使用read(new byte [length])获取数据包本身...没有循环,只是在发送请求后进行两次读取。 - Dean Hiller
@DeanHiller:这取决于输入流,但通常情况下,InputStream.read不会阻塞直到填满缓冲区。DataInputStream.readFully会阻塞,但那是另一回事。如果您在通用的InputStream上使用read而没有检查返回值,那么您就会让自己处于失败的开放状态。 - Jon Skeet
@JonSkeet,抱歉,我在想常用的BufferedInputStream,这样开发人员就不必编写循环了...是的,基本的InputStreams没有这个功能...C#需要这个,那会很好。显然,你只需将任何InputStream包装到BufferedInputStream中,并从BufferInputStream读取即可。 - Dean Hiller

0

1
请注意 - 如果您不读取数据,它将阻塞套接字,因此如果您正在等待超过仅仅一个小数据片段,则可能会永远等待。因此,我不建议使用此方法等待接收一定量的数据。 - Lucero

0
您需要自己缓冲数据,read 函数的规范是它可以在返回时读取从 1 字节到缓冲区大小之间任意数量的字节。

0

0

听起来yield语句可以很好地适应这种情况。

例如,假设有一个循环监视传入的流,并且一旦达到每个长度数字,您通过'yield'返回控制权给IEnumerable / foreach的调用者。

也许yield可以通过事件发出信号,以实现与IEnumerable的替代解耦。但我发现IEnumerable很方便。


-1

嗯,有点糟糕我们得写一个循环,但我仍然希望有人在这个论坛帖子中放置一个循环,这样我就可以快速剪切和粘贴... 下面是我的(是的,我宁愿基于时间来控制i,但这仅用于单元测试,并且它们控制模拟服务器,所以我并不太在意+ 我不希望我的单元测试在调用我的Read方法时因故障而被卡住)

        int numBytes = 0;
        int i = 0;
        while(numBytes != length)
        {
            numBytes += latestClient.Receive(body, numBytes, length-numBytes, SocketFlags.None);                
            if(i == 10000)
                throw new Exception("Could not read enough data.  read="+numBytes+" but expected="+length);
        }

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