3

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

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

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

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

4

2 回答 2

7

是否可以安全地假设 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 在评论中指出的那样,保留偏移变量的性能损失将是最小的,因为您通常会一次收到四个字节。

于 2012-10-30T08:48:37.880 回答
2

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

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

于 2012-10-26T21:06:25.263 回答