为什么这个程序没有收到预期的UDP数据包?

Ste*_*eve 9 c++ udp boost-asio

我试图使用Boost asio接收UDP数据包.我的代码基于asio文档中的阻塞UDP客户端示例.

我试图从C6655 TI DSP接收类似BOOTP的UDP数据包,这些数据包以3秒的间隔传输.我有Wireshark看着我的程序正在监听的相同接口,它可以看到数据包到达(参见下面的确切数据包数据,从Wireshark导出).数据包并非真正来自DSP,我捕获了一个,tcpdump我正在用带有封装的Raspberry Pi模拟它.

但是,我的程序没有收到数据包.它有4秒的超时(因为DSP每3秒播放一次).如果它达到超时,它会打印一条消息,否则它应该打印接收的字节数.该程序的完整(可编译)源代码如下(约100行).

使用参数调用该命令192.168.5.122 67 4000,这意味着在超时4000毫秒的情况下侦听192.168.5.122:67.

编辑:除了下面的代码,我还尝试了这个作为我的端点:udp::endpoint listen_endpoint(boost::asio::ip::address_v4::any(), atoi(argv[2]));以及0.0.0.0搜索结果建议的IP地址.

我还添加了以下内容无济于事:

boost::asio::socket_base::broadcast option(true);
socket_.set_option(option);
Run Code Online (Sandbox Code Playgroud)

我有一个能够正确接收此数据包的程序,使用Berkeley套接字编写.除了绑定到INADDR_ANY之外,我没有做任何特别的事情.

这是完整的程序:

//
// blocking_udp_client.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~
//
#include <boost/asio/deadline_timer.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/udp.hpp>
#include <boost/bind.hpp>
#include <boost/date_time/posix_time/posix_time_types.hpp>
#include <iostream>

using boost::asio::deadline_timer;
using boost::asio::ip::udp;

class listener
{
public:
    listener(const udp::endpoint& listen_endpoint)
        : socket_(io_service_, listen_endpoint)
        , deadline_(io_service_)
    {
        deadline_.expires_at(boost::posix_time::pos_infin);
        check_deadline();
    }

    std::size_t receive(const boost::asio::mutable_buffer& buffer, boost::posix_time::time_duration timeout, boost::system::error_code& ec)
    {
        deadline_.expires_from_now(timeout);
        ec = boost::asio::error::would_block;
        std::size_t length = 0;
        socket_.async_receive(boost::asio::buffer(buffer), boost::bind(&listener::handle_receive, _1, _2, &ec, &length));

        // TODO: The following do/while is hinky. Does run_one() need to happen before the comparison?
        do io_service_.run_one();
        while (ec == boost::asio::error::would_block);

        return length;
    }

private:
    void check_deadline()
    {
        if (deadline_.expires_at() <= deadline_timer::traits_type::now())
        {
            // cancel() won't work on XP. Something about using close() instead... Look it up. I'm doing this on Win10.
            socket_.cancel();
            deadline_.expires_at(boost::posix_time::pos_infin);
        }
        deadline_.async_wait(boost::bind(&listener::check_deadline, this));
    }

    static void handle_receive(const boost::system::error_code& ec, std::size_t length, boost::system::error_code* out_ec, std::size_t* out_length)
    {
        *out_ec = ec;
        *out_length = length;
    }

private:
    boost::asio::io_service io_service_;
    udp::socket socket_;
    deadline_timer deadline_;
};

int main(int argc, char* argv[])
{
    try
    {
        if (argc != 4)
        {
            std::cerr << "Usage: blocking_udp_timeout <listen_addr> <listen_port> <timeout_ms>\n";
            return 1;
        }

        udp::endpoint listen_endpoint(boost::asio::ip::address::from_string("0.0.0.0"), atoi(argv[2]));
        std::cout << "Endpoint: " << listen_endpoint << std::endl;

        auto timeout = atoi(argv[3]);
        std::cout << "Timeout : " << timeout << std::endl;

        listener c(listen_endpoint);

        for (;;)
        {
            char data[1024];
            boost::system::error_code ec;
            auto n = c.receive(boost::asio::buffer(data), boost::posix_time::milliseconds{timeout}, ec);

            if (ec)
            {
                std::cout << "Receive error: " << ec.message() << "\n";
            }
            else
            {
                std::cout << "Received " << n << " bytes." << std::endl;
            }
        }
    }
    catch (std::exception& e)
    {
        std::cerr << "Exception: " << e.what() << "\n";
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这是我想要收到的数据包.这包括以太网帧:

0000   ff ff ff ff ff ff c4 ed ba aa 28 35 08 00 45 00  ..........(5..E.
0010   01 48 00 01 00 00 10 11 a9 a5 00 00 00 00 00 00  .H..............
0020   00 00 00 44 00 43 01 34 00 00 01 01 06 00 12 34  ...D.C.4.......4
0030   56 78 00 01 00 00 00 00 00 00 00 00 00 00 00 00  Vx..............
0040   00 00 00 00 00 00 c4 ed ba aa 28 35 00 00 00 00  ..........(5....
0050   00 00 00 00 00 00 74 69 2d 62 6f 6f 74 2d 74 61  ......ti-boot-ta
0060   62 6c 65 2d 73 76 72 00 00 00 00 00 00 00 00 00  ble-svr.........
0070   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0080   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0090   00 00 00 00 00 00 74 69 2d 62 6f 6f 74 2d 74 61  ......ti-boot-ta
00a0   62 6c 65 2d 30 30 30 37 00 00 00 00 00 00 00 00  ble-0007........
00b0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00c0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00d0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00e0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
00f0   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0100   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0110   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0120   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0130   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0140   00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
0150   00 00 00 00 00 00                                ......
Run Code Online (Sandbox Code Playgroud)

我有一个可以接收此数据包的Berkeley套接字实现(我已经删除了错误处理和其他错误代码):

{
    struct sockaddr_in servaddr;
    socklen_t len;
    char mesg[RECV_BUFFER_LENGTH];

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(67);
    bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
    n = recvfrom(sockfd, mesg, RECV_BUFFER_LENGTH, 0, NULL, &len);
}
Run Code Online (Sandbox Code Playgroud)

Dmi*_*ich 3

socket_.cancel()考虑在下一次调用之前但之后会发生什么socket_.async_receive()。如果有任何数据到达,则不会为套接字分配“接收处理程序”。当计时器到期时,它会导致run_once()被调用 3 次(因为每个async_wait()expires_at()以及其他一些会导致已分配的处理程序被取消,并导致已分配的处理程序被发布到运行队列,并带有错误代码operation_aborted)。

您还没有提到您的超时设置是什么。您的代码所基于的示例(Boost 文档中的示例)将超时设置为 10 秒,但您可以将其配置为可配置。如果超时太紧,这将导致旋转(后->取消前一个->调用前一个处理程序->后->等),并且可能socket_.cancel()在接收数据包时调用。如果是这样的话,您就不必使用广播来测试它。您也可以通过点对点连接看到它。

编辑:在使用较长的超时(4000ms)和您的确切代码时,我能够收到发送的广播。我必须安装“传统”netcat,因为 BSD netcat 已损坏。但下面的行有效

echo "hello world" | nc.traditional -b -u 192.168.XXX.255 1500
Run Code Online (Sandbox Code Playgroud)

XXX.255 并不是字面上的“XXX.255”。这是我的本地广播地址。nc.bsd 中的 -b 选项已损坏(如果使用上述选项 strace nc.bsd,您就会明白为什么)。

unix stackexchange nc.traditional有一个很好的例子,说明其他人如何弄清楚为什么 nc.bsd 不能进行 UDP 广播(-b 选项不执行任何操作)。

PackEth 无法发送广播或 ptp 流量,因此我不会用它来衡量是否可以发送广播流量。诚然,我对此没有太多经验,所以我不知道它是否坏了或者我是否没有正确配置。