为什么此程序没有收到预期的UDP数据包? [英] Why does this program not receive the expected UDP packets?

查看:449
本文介绍了为什么此程序没有收到预期的UDP数据包?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我尝试使用Boost asio接收UDP数据包。我的代码基于此阻止UDP客户端示例

I am trying to receive a UDP packet using Boost asio. My code is based off of this blocking UDP client example from the asio documentation.

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

I am trying to receive a BOOTP-like UDP packet from a C6655 TI DSP, which are being transmitted in 3 second intervals. I have Wireshark watching the same interface my program is listening on, and it can see the packets arriving (see below for the exact packet data, exported from Wireshark). The packets aren't really coming from the DSP, I captured one with tcpdump and I'm simulating it from a Raspberry Pi with packeth.

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

However, my program does not receive the packets. It has a 4 second timeout (since the DSP broadcasts every 3 seconds). If it hits the timeout, it prints a message to that effect, otherwise it's supposed to print the number of bytes received. The full (compilable) source code of the program follows (about 100 lines).

正在使用参数 192.168.5.122 67 4000 ,这意味着在192.168.5.122:67上以4000毫秒的超时侦听。

The command is being invoked with the parameters 192.168.5.122 67 4000, which means listen on 192.168.5.122:67 with a 4000 millisecond timeout.

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

In addition to the code below, I also tried this as my endpoint: udp::endpoint listen_endpoint(boost::asio::ip::address_v4::any(), atoi(argv[2])); as well as the IP address 0.0.0.0 as suggested by a search result somewhere.

我还添加了以下内容无效:

I also added the following to no avail:

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

我有一个程序能够正确接收这个包,使用Berkeley套接字写。

I do have a program that is able to properly receive this packet, written using Berkeley sockets. It's not doing anything special that I can see, beyond binding to 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;
}

这里是我想要接收的数据包。这包括以太网帧:

Here is the packet that I'm trying to receive. This includes the Ethernet frame:

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套接字实现,可以接收这个数据包(我已经删除错误处理和其他杂项。代码)

I do have a Berkeley socket implementation that can receive this packet (I have removed error handling and other misc. code):

{
    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);
}


推荐答案

c $ c> socket_.cancel(),但是在下一次调用 socket_.async_receive()之前。如果任何数据到达,则没有接收处理程序分配给套接字。当定时器到期时,它会导致 run_once()被调用3次(因为每个 async_wait() expires_at(),其他一些引起已经分配的处理程序的取消,并导致已经分配的处理程序被发布到运行队列,错误代码 operation_aborted )。

Consider what happens after socket_.cancel(), but before the next call to socket_.async_receive(). If any data arrives, there is no "receive handler" assigned to the socket. When the timer expires, it causes run_once() to be called 3 times (because each async_wait(), expires_at() and some of the others cause cancellations of already-assigned handlers and cause the already-assigned handlers to be posted to the run queue with the error code operation_aborted).

您尚未提及您的超时设置。您基于代码的示例(Boost文档中的示例)将超时设置为10秒,但您可以将其配置。如果超时太紧,这将导致自旋(post-> cancel previous->调用上一个handler-> post->等),并可能调用 socket_.cancel()。如果是这样,你不需要去做广播测试。

You haven't mentioned what your timeout is set to. The example on which you base your code (the one from Boost documentation) sets the timeout to 10 seconds, but you make it configurable. If the timeout is too tight, this would cause a spin (post->cancel previous->call previous handler->post->etc.) and possibly call socket_.cancel() while receiving the packet. If that's the case, you wouldn't have to go so far as to test it with a broadcast. You'd be able to see it with a point-to-point connection, too.

编辑:使用长时间超时(4000毫秒),您可以通过点对点连接看到它。 )和你的确切代码,我能够接收发送的广播。我不得不安装传统netcat因为BSD netcat破。

Edit: while using a long timeout (4000ms) and your exact code, I was able to receive a sent broadcast. I had to install "traditional" netcat because BSD netcat is broken. But the line below worked:

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

XXX.255不是字面上的XXX .255。这是我的本地广播地址。 -b选项在nc.bsd是破碎的(你可以看到为什么如果strace nc.bsd与上述选项)。

XXX.255 is not literally "XXX.255". It's my local broadcast address. -b option in nc.bsd is broken (you can see why if you strace nc.bsd with above options).

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

unix stackexchange nc.traditional has a good example of how others figured out why nc.bsd cannot do UDP broadcasts (-b option does nothing).

PackEth无法发送广播或ptp流量,因此我不会将其用作度量的是否可以发送广播流量。诚然,我没有太多的经验,所以我不知道它是否坏了或如果我没有它的配置正确。

PackEth wasn't able to to send broadcasts or p-t-p traffic, so I wouldn't use it as a measure of whether you can send broadcast traffic. Admittedly, I don't have much experience with it, so I don't know if it's broken or if I don't have it configured right.

这篇关于为什么此程序没有收到预期的UDP数据包?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆