【问题标题】:Determining the size of the next UDP datagram in system's queue确定系统队列中下一个 UDP 数据报的大小
【发布时间】:2010-09-23 20:11:26
【问题描述】:

我想知道系统队列中下一个 UDP 数据报的大小。

我发现 this question 有类似的疑问,但使用了 boost。最后一个答案(截至 2010 年 9 月 23 日)说了一些关于在 OS X 中使用 getsockoptSO_NREAD 选项的信息,但我在 Windows 中找不到任何相关信息(使用 Winsock)。

Here我发现我可以使用ioctlsocketFIONREAD来找出整个队列的大小,但是我没有找到关于第一个数据报的任何信息。

所以我的问题是: 有没有办法使用套接字 API 确定队列中下一个 UDP 数据报的大小?(我没有使用 boost)。

我希望我的代码如下所示:

char BigBuffer[ 64 * 1024 ];
void Read( void *Buf, size_t Size ) {
    size_t LengthInQueue = WhatTheSizeOfTheNextDatagram();
    if( Size < LengthInQueue ) {
         recvfrom( Socket, BigBuffer, 64*1024,  /*...*/ );
         memcpy( Buf, BigBuffer, Size );
    }
    else {
         recvfrom( Socket, Buf, size,  /*...*/ );
    }
}

为了空间和可读性,我省略了错误检查和一些参数。 我想避免在不需要时复制到中间缓冲区。

【问题讨论】:

  • 为什么要这样做?一个 UDP 数据报总是低于 64K。当然,您可以只在 Windows 中提供 64K 缓冲区吗?
  • 我试图在读取函数中避免缓冲区复制。
  • 在普通的 BSD sockets/POSIX/friends 上,通常使用 recvfrom()MSG_PEEK 标志来完成:即读取 header,计算完整消息中有多少字节,然后终于阅读了整个消息。否则,如果这是禁用 IP 分段的实际 UDP,则 UDP 数据报不能超过 9200 字节,即巨型帧的最大大小(因为 TCP/UDP 的 CRC16 不缩放 9200 字节以上)。
  • 请注意,为避免小副本而支付额外系统调用的成本实际上可能不是一个胜利。

标签: c++ c windows udp winsock


【解决方案1】:

如果我理解正确,您只想确保Buf 不会溢出,但您不关心Size 之外的任何额外数据,因为无论如何您都在丢弃它。在这种情况下,您只需要这样:

recvfrom( Socket, Buf, size,  /*...*/ );

数据包的剩余部分被自动丢弃。

引用自the docs

对于面向消息的套接字,数据从第一个排队的消息中提取,直到指定的缓冲区大小。如果数据报或消息大于指定的缓冲区,则缓冲区被数据报的第一部分填充,并且 recvfrom 生成错误 WSAEMSGSIZE。对于不可靠的协议(例如 UDP),多余的数据会丢失。

【讨论】:

  • 据我所知,没有直接的方法可以做到这一点。我猜这背后的基本原理是UDP数据包通常是协议指定的固定长度。
【解决方案2】:

你可以pop这个数据报并计算它的长度,为了获取数据报存在信息你可以使用select函数,为了获取所有数据报你可以使用recv_from函数和64k缓冲区大小agrument,然后这个函数结果就是你想要的。

【讨论】:

  • 我认为 Svisstack 正在获得类似于 boost::asio 的 message_peek 功能,这让您可以查看数据而无需将其从队列中删除。 boost.org/doc/libs/1_37_0/doc/html/boost_asio/reference/socket_base/message_peek.html 不知道他从哪里得到的术语。
  • 我可以将recvfrom 与 MSG_PEEK 一起使用,但它会复制缓冲区,然后需要再次复制才能将其从队列中删除。
【解决方案3】:

使用 FIONREAD 调用 ioctl(windows 上的 ioctlsocket)函数:

#ifdef WIN32
    if(ioctlsocket(socket, FIONREAD, &ul) == SOCKET_ERROR) {
        return -1;
    }
    return (int)ul;
#else
    int i; // ?? int ??
    if(ioctl(socket, FIONREAD, &i) == -1) {
            return -1;
    }
    return i;
#endif

【讨论】:

  • 根据this,如果我有 2 条(或更多)未完成的消息要读取,FIONREAD 将返回它们的总大小,而不是队列中第一条消息的大小。
猜你喜欢
  • 2012-01-17
  • 1970-01-01
  • 2021-06-01
  • 2010-10-27
  • 1970-01-01
  • 1970-01-01
  • 2011-10-12
  • 2019-12-17
  • 1970-01-01
相关资源
最近更新 更多