【问题标题】:What is the minimum number of bytes that will cause Socket.Receive to return?导致 Socket.Receive 返回的最小字节数是多少?
【发布时间】:2012-10-16 17:44:35
【问题描述】:

我们正在使用一个应用程序协议,该协议在前 4 个字节中指定消息的长度指示符。 Socket.Receive 将返回与当时协议堆栈中的数据一样多的数据,或者阻塞直到数据可用。这就是为什么我们必须不断地从套接字读取,直到我们收到长度指示器中的字节数。如果对方关闭连接,Socket.Receive 将返回 0。我都明白了。

是否有必须读取的最小字节数?我问的原因是从文档看来,当 socket.Receive 可以返回时,整个长度指示器(4 个字节)可能不可用。然后我们将不得不继续努力。尽量减少调用 socket.receive 的次数会更有效,因为它必须将内容复制进出缓冲区。那么一次获取一个字节来获取长度指示符是否更安全,假设 4 个字节将始终可用是否安全,或者我们应该继续尝试使用偏移变量获取 4 个字节?

我认为可能存在某种默认最低级别的原因是我遇到了一个名为 ReceiveLowWater 的变量,我可以在套接字选项中设置它。但这似乎只适用于 BSD。 MSDN 见 SO_RCVLOWAT。

这并不重要,但我正在尝试编写单元测试。我已经在接口后面封装了一个标准的 .Net Socket。

【问题讨论】:

  • 这不太可能,但您应该能够处理任意大小的读取。甚至 1 个字节。由于大多数情况下这种情况不太可能发生,因此您几乎不会受到性能影响。如果这 4 个字节不在流的最开始,那么它们实际上很可能会在读取之间被拆分。

标签: c# .net sockets


【解决方案1】:

假设 4 个字节始终可用是否安全

没有。绝不。如果有人正在使用 telnet 和键盘测试您的协议怎么办?还是通过真正缓慢或繁忙的连接?您可以一次接收一个字节,或者在多个Receive() 调用中接收一个拆分“长度指示器”。这不是单元测试问题,而是导致生产中出现问题的基本套接字问题,尤其是在压力大的情况下。

或者我们应该继续尝试使用偏移变量获取 4 个字节?

是的,你应该这样做。为了您的方便,您可以使用Socket.Receive() 重载,它允许您指定要读取的字节数,这样您就不会读取太多。但请注意它可以返回小于要求的值,这就是offset 参数的作用,所以它可以继续在同一个缓冲区中写入:

byte[] lenBuf = new byte[4];
int offset = 0;

while (offset < lenBuf.Length)
{       
    int received = socket.Receive(lenBuf, offset, lenBuf.Length - offset, 0);

    offset += received;     

    if (received == 0)
    {
        // connection gracefully closed, do your thing to handle that
    }
}

// Here you're ready to parse lenBuf

我认为可能存在某种默认最低级别的原因是我遇到了一个名为 ReceiveLowWater 的变量,我可以在套接字选项中设置它。但这似乎只适用于 BSD。

没错,“receive low water”标志只是为了向后兼容而包含的,除了抛出错误之外什么都不做,根据MSDN,搜索SO_RCVLOWAT

Windows TCP/IP 提供程序不支持此选项。如果在 Windows Vista 和更高版本上使用此选项,getsockopt 和 setsockopt 函数将失败并显示 WSAEINVAL。在早期版本的 Windows 上,这些函数因 WSAENOPROTOOPT" 而失败。所以我猜你必须使用偏移量。

很遗憾,因为它可以提高性能。然而,正如@cdleonard 在评论中指出的那样,保持偏移变量的性能损失将是最小的,因为您通常会一次收到四个字节。

【讨论】:

  • 您的代码基本上就是我要查询的内容。从文档中,该重载可能返回少于您要求的 4 个字节。我使用上面的方法,但在 while 循环中获取长度指示器中指定的所有数据。我知道这是正确的方法,因为我在文本和其他有信誉的 SO 用户中多次遇到过它。但是,当我们必须将那段代码放在 while 循环中以获取正文时,您为什么建议相同的方法总是至少获得 4 个字节。
  • @uriDium 你是对的,我对另一个在读取size 字节之前保持Receive()ing 的实现感到困惑。如文档中所述:“如果您使用的是面向连接的 Socket,则 Receive 方法将读取 尽可能多的数据,最多为 size 参数指定的字节数。”。所以,是的,你必须做偏移魔法。
  • 但据你所知,windows平台的插座有没有某种最低水位类型的功能? BSD 系统也是这样吗?
  • offset 增长时,作为第三个参数传递给Receive() 的值不应该以同样的方式缩小吗?如果没有,并且有更多数据,则缓冲区的大小即将到达,很可能会发生缓冲区溢出。
  • @alk 谢谢,你说得对。我不应该使用 Notepad++ 进行编码,也不应该发布未经测试的代码,尽管这只是为了说明这个想法。 :-)
【解决方案2】:

不,没有最小缓冲区大小,接收中的长度只需要与实际空间匹配即可。

如果您在消息实际数据之前发送了四个字节的长度,则接收者需要处理返回 1、2、3 或 4 个字节的情况,并不断重复读取,直到收到所有四个字节,然后重复接收实际数据的过程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-01-16
    • 1970-01-01
    • 1970-01-01
    • 2016-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-10
    相关资源
    最近更新 更多