Skip to content

Commit 5d7569c

Browse files
committed
core: keep receiving UDP after a datagram error
Windows reports an ICMP port unreachable, caused by us sending to a remote which has gone away, as an error on the next receive. We treated any error as the end of the connection and stopped receiving, so a UDP link never came back once the other side had been away. Tell Windows to stop reporting those errors, and treat the ones about a single datagram as non-fatal so we keep listening either way.
1 parent da25395 commit 5d7569c

1 file changed

Lines changed: 35 additions & 0 deletions

File tree

cpp/src/mavsdk/core/udp_connection.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
#include <cassert>
66

7+
#if defined(WINDOWS)
8+
#include <winsock2.h>
9+
#include <mswsock.h>
10+
#endif
11+
712
#include <asio/buffer.hpp>
813
#include <asio/error.hpp>
914
#include <asio/ip/address.hpp>
@@ -83,6 +88,26 @@ ConnectionResult UdpConnection::setup_port()
8388

8489
_socket.set_option(asio::socket_base::reuse_address(true), ec);
8590

91+
#if defined(WINDOWS)
92+
// By default, Windows reports an ICMP port unreachable caused by one of our sends
93+
// as an error on the next receive on this socket. Switch that off, otherwise a
94+
// remote that goes away can take down a socket which is otherwise fine.
95+
BOOL report_icmp_errors = FALSE;
96+
DWORD bytes_returned = 0;
97+
if (WSAIoctl(
98+
_socket.native_handle(),
99+
SIO_UDP_CONNRESET,
100+
&report_icmp_errors,
101+
sizeof(report_icmp_errors),
102+
nullptr,
103+
0,
104+
&bytes_returned,
105+
nullptr,
106+
nullptr) == SOCKET_ERROR) {
107+
LogWarn("Could not disable ICMP error reporting: {}", WSAGetLastError());
108+
}
109+
#endif
110+
86111
_socket.bind(local_endpoint, ec);
87112
if (ec) {
88113
LogErr("Bind error: {}", ec.message());
@@ -273,6 +298,16 @@ void UdpConnection::do_receive()
273298
_sender_endpoint,
274299
[this](const asio::error_code& ec, std::size_t recv_len) {
275300
if (ec) {
301+
// These are errors about one datagram, not about the socket. Windows
302+
// reports an ICMP unreachable from an earlier send this way, meaning a
303+
// remote that has gone away would otherwise end our receiving for good.
304+
if (ec == asio::error::connection_refused || ec == asio::error::connection_reset ||
305+
ec == asio::error::network_reset || ec == asio::error::message_size) {
306+
LogDebug("Ignoring error from async_receive_from: {}", ec.message());
307+
do_receive();
308+
return;
309+
}
310+
276311
// operation_aborted happens when the socket is closed (stop()), which is normal.
277312
if (ec != asio::error::operation_aborted) {
278313
LogErr("Error from async_receive_from: {}", ec.message());

0 commit comments

Comments
 (0)