【发布时间】:2015-10-03 00:54:13
【问题描述】:
我正在尝试使用 Boost asio 接收 UDP 数据包。我的代码基于this blocking UDP client example from the asio documentation。
我正在尝试从 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 ......
我确实有一个可以接收此数据包的伯克利套接字实现(我已删除错误处理和其他杂项代码):
{
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);
}
【问题讨论】:
-
BOOTP 通常是广播,需要特殊标志才能接收
-
客户端代码看起来不错:
socket_base::broadcast仅在发送 UDP 广播消息时才需要,并且绑定到预期数据的接口地址或address_v4::any()(即0.0.0.0)就可以了。这是一个简单的demo。粗略一看,发布的以太网帧中0.0.0.0的IPv4 目标地址很可疑,因为0.0.0.0在路由中通常具有特殊含义,看起来不像是广播地址。 -
@TannerSansbury 来自 TI 的文档指出“以太网就绪公告帧以 BOOTP 请求的形式制作,因此它可以使用标准格式。没有处理此消息的响应,它是构建以便大多数(如果不是全部)BOOTP 和 DHCP 服务器将丢弃它。”这可能就是您所指的(它们不是那么具体)。但是,当 BOOTP 客户端没有 IP 地址(或不知道从哪里获取)时,您希望 BOOTP 客户端在该字段中输入什么?
-
我将尝试编辑数据包并更改该 IP。不过,那将是不幸的,因为数据包来自我无法控制的引导 ROM。但是,我们有一个在 ARM 上运行的 Linux 上编写的程序,而 ARM 是带有该 DSP 的微型板载以太网网络上唯一的东西。它能够使用常规的 Berkeley 套接字接口接收此数据包(并且该源似乎没有做任何比绑定到 INADDR_ANY 更特别的事情)。当我在同一台设备上运行我的 asio 程序时,它没有收到数据包。这样就排除了底层问题,不是吗?
-
@Steve 除非内核文档声明它支持
0.0.0.0的旧式广播地址,否则我不会将其作为保证(尽管您可以别无选择)。在使用非连接读取时,您是否观察到相同的行为,例如socket.async_receive_from()(demo)?将允许连接读取操作过滤掉 invalid0.0.0.0源地址。
标签: c++ udp boost-asio