如何使用boost asio接收发送到255.255.255.255的UDP广播?

时间:2017-12-15 10:07:08

标签: c++ udp boost-asio

我编写了一个可以在客户端或服务器模式下运行的设备发现程序。在客户端模式下,它在端口30000上发送UDP广播数据包到255.255.255.255,然后在端口30001上侦听响应。在服务器模式下,它在端口30000上侦听UDP广播,并在端口30001上发送UDP广播数据包到255.255.255.255作为回应。

当我在2台设备上运行此程序时,IP地址为192.168.10.61和192.168.10.62,这一切都完美无缺。该程序的重​​点是允许具有未知IP地址的设备发现彼此,只要它们连接到同一物理网络即可。因此,为了测试,我将第一个设备的IP地址更改为随机的,如12.34.56.42/255.255.240。现在它停止工作了。

在192.168.10.62机器上使用tcpdump我可以看到收到12.134.56.42机器的UDP数据包:

# tcpdump -i eth0 port 30000 -c 1 -v
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
17:38:02.552427 IP (tos 0x0, ttl 64, id 18835, offset 0, flags [DF], proto UDP (17), length 49)
12.34.56.42.56815 > 255.255.255.255.30000: UDP, length 21
1 packet captured
6 packets received by filter
0 packets dropped by kernel

但是我的发现程序不再接收它。这是我用来接收UDP广播包的代码:

int WaitForPacket(uint16_t portNum, vector<char>& udpBuf, udp::endpoint& remoteEndpoint, const chrono::milliseconds timeout)
{
    io_service ioService;

    udp::socket socket(ioService, udp::endpoint(boost::asio::ip::address_v4::any(), portNum));
    socket.set_option(socket_base::broadcast(true));

    boost::system::error_code error;
    int numBytes = receive_from(socket, buffer(udpBuf), remoteEndpoint, error, timeout);

    if (error && error != error::message_size && error != error::timed_out)
    {
        printf("Got error: %s\n", error.message().c_str());
        return -1;
    }

    return numBytes;
}

/*
 * The boost asio library does not provide a blocking read with timeout function so we have to roll our own.
 */
int receive_from(
    boost::asio::ip::udp::socket&         socket,
    const boost::asio::mutable_buffers_1& buf,
    boost::asio::ip::udp::endpoint&       remoteEndpoint,
    boost::system::error_code&            error,
    chrono::milliseconds                  timeout)
{
    volatile bool ioDone = false;
    int numBytesReceived = 0;
    boost::asio::io_service& ioService = socket.get_io_service();    

    socket.async_receive_from(buf, remoteEndpoint,
                              [&error, &ioDone, &numBytesReceived](const boost::system::error_code& errorAsync, size_t bytesReceived)
                              {
                                  ioDone = true;
                                  error = errorAsync;
                                  numBytesReceived = bytesReceived;
                              });

    this_thread::sleep_for(chrono::milliseconds(100));
    ioService.reset();
    ioService.poll_one();

    auto endTime = chrono::system_clock::now() + timeout;

    while (!ioDone)
    {
        ioService.reset();
        this_thread::sleep_for(chrono::milliseconds(100));
        auto now = chrono::system_clock::now();
        if (now > endTime)
        {
            socket.cancel();
            error = error::timed_out;
            return 0;
        }
        ioService.poll_one();
    }
    ioService.reset();

    return numBytesReceived;
}

我在Stack Overflow上检查了类似的问题,并发现一些说接收套接字必须绑定到INADDR_ANY才能接收广播数据包。我最初是像udp::socket socket(ioService, udp::endpoint(udp::v4(), portNum));那样创建套接字,但现在已将其更改为使用ip::address_v4::any()。这没有任何区别。

有人可以告诉我需要更改什么才能按预期接收UDP广播包吗?

我在运行Linux的iMX6设备上运行它,并使用boost 1.58进行编译。

1 个答案:

答案 0 :(得分:3)

我终于发现了为什么我的代码永远不会看到广播数据包,即使tcpdump证明它们正在被接收。找到StackOverflow问题之后:

Disable reverse path filtering from Linux kernel space

似乎我只需要在这两个主机上禁用反向路径过滤:

echo 0 > /proc/sys/net/ipv4/conf/all/rp_filter
echo 0 > /proc/sys/net/ipv4/conf/eth0/rp_filter

然后我的代码完美无缺地进行修改。希望这有助于其他人想知道他们为什么不能通过网络广播地址(255.255.255.255)进行UDP广播。