下一个排队的数据报的大小 - UDP

6
我正在使用 System.Net.Sockets 中的 Socket 类来接收UDP数据包。
我想知道接收到的数据包的确切长度,以检查数据包的有效性。 Socket.Receive(Byte()) 方法文档说明如下:
如果您正在使用无连接Socket,则Receive将从您在Connect方法中指定的目标地址读取第一个排队的数据报。如果您接收到的数据报大于缓冲区参数的大小,则缓冲区将被填充消息的第一部分,多余的数据将丢失并抛出SocketException异常。 Socket.Available 属性提供可读取的字节数总和,这是所有排队数据报的大小之和。
是否有办法找出下一个数据报的大小?
public static void Receive()
{
    Byte[] buf, discardbuf;
    const int paklen = 32;                  //correct packet length
    int l;

    buf = new Byte[paklen];
    discardbuf = new Byte[4096];

    while (rcv_enable)
    {
        l = soc.Available;
        if (l == 0) continue;
        if (l == paklen) soc.Receive(buf);  //receive the correct packet
        else soc.Receive(discardbuf);       //discard otherwise
        Thread.Sleep(200);
        Console.WriteLine("Received {0}", l);
    };
}
2个回答

3
我假设您已开发了自己的协议并且期望知道数据报的大小,并希望防范流氓数据包。

由于性能似乎是个问题,您希望避免异常,建议查看接收方法的重载,该重载返回原始套接字错误而不是抛出异常。 MSDN文档确实(错误地?)说明此方法也会抛出异常,但我认为这并不是真的。 这绝对值得一试。

SocketError error;
byte[] buffer = new byte[512]; // Custom protocol max/fixed message size
int c = this.Receive(buffer, 0, buffer.Length, SocketFlags.None, out error);

if (error != SocketError.Success)
{
    if(error == SocketError.MessageSize)
    {
        // The message was to large to fit in our buffer
    }
}

使用已知合理大小的缓冲区,并使用重载检查SocketError错误代码,以确定读取是否成功或者你应该丢弃数据包。
然而,如果您自己的协议可以发送未知大小的数据报,直到达到最大数据报大小的限制,您别无选择,只能分配足够大以适合最大数据包(65k)的缓冲区(根据您的代码,您可以使用缓冲池来避免内存问题)。
还请查看SocketFlags枚举,其中包含一些可能对您有用的成员,例如Partial和Peek成员。

太好了!这个重载完成了任务,没有抛出任何异常。看来,MSDN文档是不正确的。顺便说一下,在MSDN中我找不到任何关于SocketFlags枚举的相关示例。 - Anniffer

2
为什么不直接检查Receive的返回值呢?我相信每个调用都会返回一个数据报。

它可以工作,但我总是需要将数据读入一个更大的缓冲区。否则,如果缓冲区小于数据报大小,则Receive()会抛出异常错误代码:10040在数据报套接字上发送的消息大于内部消息缓冲区或某些其他网络限制,或用于接收数据报的缓冲区小于数据报本身--实际上这是高性能代码,抛出和处理异常太昂贵了。 - Anniffer
啊,好的,不幸的是我不是 .NET 专家,但一个解决方案可能是找出你将要接收数据报的所有接口的最大 MTU。另一个解决方案是将缓冲区硬编码为 65535 字节,因为这是最大的 UDP 数据报大小(16 位长度字段),实际的最大值略小于此,即 65535 - IP 头 - UDP 头。 - Mattias Wadman

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