From 65cb4c8c19e60ae97da61d3235da2ea1e027f982 Mon Sep 17 00:00:00 2001 From: Trent Houliston Date: Wed, 16 Jul 2025 09:00:07 -0300 Subject: [PATCH] Add non-blocking socket support and error handling in NUClearNetwork This commit introduces a new function to set sockets to non-blocking mode and enhances error handling for network operations. Specifically, it throws exceptions for network errors during packet reception and sending, ensuring robust error management. The changes improve the reliability of the NUClearNetwork by preventing blocking behavior and handling potential errors gracefully. --- src/extension/network/NUClearNetwork.cpp | 47 ++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/src/extension/network/NUClearNetwork.cpp b/src/extension/network/NUClearNetwork.cpp index 11839da9..b1474c4f 100644 --- a/src/extension/network/NUClearNetwork.cpp +++ b/src/extension/network/NUClearNetwork.cpp @@ -77,11 +77,44 @@ namespace extension { // Now read the data for real const ssize_t received = recvmsg(fd, &mh, 0); + if (received < 0) { + throw std::system_error(network_errno, + std::system_category(), + "Network error when receiving the packet"); + } payload.resize(received); return {from, std::move(payload)}; } + + /** + * Sets a socket to non-blocking mode. + * + * @param fd The file descriptor of the socket to set to non-blocking. + */ + void set_non_blocking(const fd_t& fd) { +#ifdef _WIN32 + u_long mode = 1; + if (ioctlsocket(fd, FIONBIO, &mode) != 0) { + throw std::system_error(network_errno, + std::system_category(), + "Unable to set the socket to non-blocking"); + } +#else + int flags = fcntl(fd, F_GETFL, 0); + if (flags < 0) { + throw std::system_error(network_errno, std::system_category(), "Unable to get socket flags"); + } + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { + throw std::system_error(network_errno, + std::system_category(), + "Unable to set the socket to non-blocking"); + } +#endif + } + + } // namespace NUClearNetwork::PacketQueue::PacketTarget::PacketTarget(std::weak_ptr target, @@ -189,6 +222,9 @@ namespace extension { throw std::system_error(network_errno, std::system_category(), "Unable to set broadcast on the socket"); } + // Make the data socket non blocking + set_non_blocking(data_fd); + // Bind to the address, and if we fail throw an error if (::bind(data_fd, &address.sock, address.size()) != 0) { throw std::system_error(network_errno, @@ -1050,9 +1086,14 @@ namespace extension { message.msg_name = const_cast(&target.sock); // NOLINT(cppcoreguidelines-pro-type-const-cast) message.msg_namelen = target.size(); - // TODO(trent): if reliable, run select first to see if this socket is writeable - // If it is not reliable just don't send the message instead of blocking - sendmsg(data_fd, &message, 0); + // send a message, if it would block then we don't care because it will be resent + if (sendmsg(data_fd, &message, 0) < 0) { + if (errno != EWOULDBLOCK && errno != EAGAIN) { + throw std::system_error(network_errno, + std::system_category(), + "Network error when sending the packet"); + } + } }