|
4 | 4 |
|
5 | 5 | #include <cassert> |
6 | 6 |
|
| 7 | +#if defined(WINDOWS) |
| 8 | +#include <winsock2.h> |
| 9 | +#include <mswsock.h> |
| 10 | +#endif |
| 11 | + |
7 | 12 | #include <asio/buffer.hpp> |
8 | 13 | #include <asio/error.hpp> |
9 | 14 | #include <asio/ip/address.hpp> |
@@ -83,6 +88,26 @@ ConnectionResult UdpConnection::setup_port() |
83 | 88 |
|
84 | 89 | _socket.set_option(asio::socket_base::reuse_address(true), ec); |
85 | 90 |
|
| 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 | + |
86 | 111 | _socket.bind(local_endpoint, ec); |
87 | 112 | if (ec) { |
88 | 113 | LogErr("Bind error: {}", ec.message()); |
@@ -273,6 +298,16 @@ void UdpConnection::do_receive() |
273 | 298 | _sender_endpoint, |
274 | 299 | [this](const asio::error_code& ec, std::size_t recv_len) { |
275 | 300 | 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 | + |
276 | 311 | // operation_aborted happens when the socket is closed (stop()), which is normal. |
277 | 312 | if (ec != asio::error::operation_aborted) { |
278 | 313 | LogErr("Error from async_receive_from: {}", ec.message()); |
|
0 commit comments