为什么此程序没有接收到预期的UDP数据包

Why does this program not receive the expected UDP packets?

本文关键字:UDP 数据包 程序 为什么      更新时间:2023-10-16

我正在尝试使用Boost asio接收UDP数据包。我的代码基于asio文档中的这个阻塞UDP客户端示例。

我正试图从C6655 TI DSP接收一个类似BOOTP的UDP数据包,该数据包以3秒的间隔发送。我让Wireshark在我的程序监听的同一个界面上观看,它可以看到数据包到达(请参阅下面从Wireshark导出的确切数据包数据(。数据包实际上并不是来自DSP的,我用tcpdump捕获了一个,我正在用packeth从树莓派上模拟它。

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

使用参数192.168.5.122 67 4000调用该命令,这意味着在192.168.5.122:67上侦听,超时时间为4000毫秒。

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

我还添加了以下内容,但没有用:

boost::asio::socket_base::broadcast option(true);
socket_.set_option(option);

我确实有一个程序能够正确地接收这个数据包,它是使用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;
}

这是我要收的包裹。这包括以太网帧:

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                                ......

我确实有一个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);
}

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

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

编辑:在使用长超时(4000ms(和您的确切代码时,我能够接收到发送的广播。我不得不安装";传统的";netcat,因为BSD netcat已损坏。但是下面的行有效

echo "hello world" | nc.traditional -b -u 192.168.XXX.255 1500

XXX、 255不是字面上的"255";XXX、 255";。这是我的本地广播地址-nc.bsd中的b选项已损坏(如果您使用上述选项约束nc.bsd,您可以看到原因(。

unix stackexchange nc.tradial提供了一个很好的例子,说明了其他人是如何理解nc.bsd为什么不能进行UDP广播的(-b选项什么都不做(。

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