【发布时间】:2018-05-29 12:35:01
【问题描述】:
我编写了一个可以在客户端或服务器模式下运行的设备发现程序。在客户端模式下,它通过端口 30000 向 255.255.255.255 发送 UDP 广播数据包,然后在端口 30001 上侦听响应。在服务器模式下,它在端口 30000 上侦听 UDP 广播,并在端口 30001 上向 255.255.255.255 发送 UDP 广播数据包作为回应。
当我在 IP 地址分别为 192.168.10.61 和 192.168.10.62 的 2 台设备上运行此程序时,一切正常。该程序的重点是允许具有未知 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 进行编译。
【问题讨论】:
-
您没有收到数据报,因为通过将第一个地址更改为 12.34.56.42/255.255.240,您自动将计算机从本地网络地址中排除。而且由于广播仅在 LAN 环境中有效,这就是为什么您没有完全收到消息的原因。您的计算机可能使用欺骗 IP 连接到同一网络,而该欺骗 IP 是它无法完全接收广播消息的原因。
-
您可能需要为可发现的设备设置一个专用网络,它们都将连接到先前的发现,但这并不是真正的发现。不过,您可以做的是让您的 WLAN 保持打开状态,并将其应用于连接到您的路由器的访问者。
-
处理任意地址的一种方法是手动将广播数据包中发送方的 IP 地址设置为 0.0.0.0(这也是 DHCP 的基本工作方式)。不要问我如何通过 boost 实现这一目标。据我所知,也许stackoverflow.com/questions/28196466/… 很有帮助。
-
@Ivan86 我认为网络广播地址(192.168.10.255)和UDP广播地址(255.255.255.255)之间的区别正是为了这个目的而存在的。如果您只能将数据包发送到与您在同一 IP 网络上的地址,则不需要 255.255.255.255 地址。正如 tcpdump 命令所示,数据包已被接收,但网络堆栈中的某些东西丢弃了它。像这样的数据包在 ARP 和 DHCP 中使用,所以绝对有可能,只是可能不在 boost asio UDP 层。
-
@Christop Freundl,感谢您的建议。我将尝试将发送方 IP 设置为 0.0.0.0,看看这对接收方是否有任何影响。
标签: c++ udp boost-asio