导致Socket.Receive返回的最小字节数是多少?

时间:2012-10-26 12:51:21

标签: c# .net sockets

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

是否需要读取最小字节数?我问的原因是文档似乎完全有可能当socket.Receive可以返回时,整个长度指示符(4个字节)可能不可用。然后我们必须继续努力。最小化我们调用socket.receive的次数会更有效,因为它必须将内容复制到缓冲区中。因此,获取长度指示符一次获得一个字节更安全,假设4个字节始终可用或我们是否应该继续尝试使用偏移量变量获得4个字节是安全的吗?

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

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

2 个答案:

答案 0 :(得分:4)

  

可以安全地假设4个字节始终可用

NO。决不。如果有人用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。

这是正确的,“仅收到低水位”标志仅用于向后兼容,并且除了投掷错误之外什么也不做,按MSDN搜索SO_RCVLOWAT

  

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

这很遗憾,因为可以提升性能。但是,正如@cdleonard在评论中指出的那样,保持偏移变量的性能损失将是最小的,因为你通常 一次接收四个字节。

答案 1 :(得分:1)

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

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