Loading src/posix/udp.cpp +45 −2 Original line number Diff line number Diff line Loading @@ -39,9 +39,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <fcntl.h> #include <sys/un.h> #include <sys/uio.h> // iovec, sendmmsg, recvmmsg #include <netinet/udp.h> // SOL_UDP #include <netinet/udp.h> // SOL_UDP / IPPROTO_UDP // Linux-specific: constants may be missing in old kernel headers // FreeBSD uses IPPROTO_UDP where Linux uses SOL_UDP #ifndef SOL_UDP #define SOL_UDP IPPROTO_UDP #endif // Linux-specific: constants may be missing in old kernel headers or on FreeBSD. // probeGSOGRO() will fail gracefully — setsockopt returns ENOPROTOOPT. #ifndef UDP_SEGMENT #define UDP_SEGMENT 103 #endif Loading @@ -49,6 +55,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define UDP_GRO 104 #endif // macOS lacks sendmmsg/recvmmsg; FreeBSD 11+ and Linux have them. #if defined(__APPLE__) #define NETPLUS_NO_MMSG 1 #endif namespace netplus { udp::udp() : socket() { Loading Loading @@ -413,6 +424,21 @@ ssize_t udp::sendBatch( } // --- sendmmsg path --- #ifdef NETPLUS_NO_MMSG // Fallback: sendto loop for platforms without sendmmsg int sent = 0; for (size_t i = 0; i < count; ++i) { ssize_t r; if (dest) { r = ::sendto(_Socket, datagrams[i].first, datagrams[i].second, MSG_DONTWAIT, reinterpret_cast<sockaddr*>(dest), dest_len); } else { r = ::send(_Socket, datagrams[i].first, datagrams[i].second, MSG_DONTWAIT); } if (r < 0) break; ++sent; } #else std::vector<struct iovec> iovecs(count); std::vector<struct mmsghdr> msgs(count); std::memset(msgs.data(), 0, sizeof(struct mmsghdr) * count); Loading @@ -430,6 +456,7 @@ ssize_t udp::sendBatch( int sent = ::sendmmsg(_Socket, msgs.data(), static_cast<unsigned int>(count), MSG_DONTWAIT); #endif return sent; } Loading @@ -451,6 +478,21 @@ size_t udp::recvBatchAddr(std::vector<std::vector<uint8_t>>& out, const int batch = std::min(max_count, 64); #ifdef NETPLUS_NO_MMSG // Fallback: recvfrom loop for platforms without recvmmsg static thread_local std::vector<uint8_t> recv_buf(65535); for (int i = 0; i < batch; ++i) { sockaddr_storage peer{}; socklen_t peer_len = sizeof(peer); ssize_t n = ::recvfrom(_Socket, recv_buf.data(), recv_buf.size(), MSG_DONTWAIT, reinterpret_cast<sockaddr*>(&peer), &peer_len); if (n <= 0) break; out.emplace_back(recv_buf.data(), recv_buf.data() + n); addrs.push_back(peer); } return out.size(); #else // Use thread_local static buffers to avoid 64×65KB heap allocation per call. // These are reused across calls on the same thread. static constexpr int MAX_BATCH = 64; Loading Loading @@ -517,6 +559,7 @@ size_t udp::recvBatchAddr(std::vector<std::vector<uint8_t>>& out, } return out.size(); #endif // NETPLUS_NO_MMSG } } // namespace netplus Loading
src/posix/udp.cpp +45 −2 Original line number Diff line number Diff line Loading @@ -39,9 +39,15 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <fcntl.h> #include <sys/un.h> #include <sys/uio.h> // iovec, sendmmsg, recvmmsg #include <netinet/udp.h> // SOL_UDP #include <netinet/udp.h> // SOL_UDP / IPPROTO_UDP // Linux-specific: constants may be missing in old kernel headers // FreeBSD uses IPPROTO_UDP where Linux uses SOL_UDP #ifndef SOL_UDP #define SOL_UDP IPPROTO_UDP #endif // Linux-specific: constants may be missing in old kernel headers or on FreeBSD. // probeGSOGRO() will fail gracefully — setsockopt returns ENOPROTOOPT. #ifndef UDP_SEGMENT #define UDP_SEGMENT 103 #endif Loading @@ -49,6 +55,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define UDP_GRO 104 #endif // macOS lacks sendmmsg/recvmmsg; FreeBSD 11+ and Linux have them. #if defined(__APPLE__) #define NETPLUS_NO_MMSG 1 #endif namespace netplus { udp::udp() : socket() { Loading Loading @@ -413,6 +424,21 @@ ssize_t udp::sendBatch( } // --- sendmmsg path --- #ifdef NETPLUS_NO_MMSG // Fallback: sendto loop for platforms without sendmmsg int sent = 0; for (size_t i = 0; i < count; ++i) { ssize_t r; if (dest) { r = ::sendto(_Socket, datagrams[i].first, datagrams[i].second, MSG_DONTWAIT, reinterpret_cast<sockaddr*>(dest), dest_len); } else { r = ::send(_Socket, datagrams[i].first, datagrams[i].second, MSG_DONTWAIT); } if (r < 0) break; ++sent; } #else std::vector<struct iovec> iovecs(count); std::vector<struct mmsghdr> msgs(count); std::memset(msgs.data(), 0, sizeof(struct mmsghdr) * count); Loading @@ -430,6 +456,7 @@ ssize_t udp::sendBatch( int sent = ::sendmmsg(_Socket, msgs.data(), static_cast<unsigned int>(count), MSG_DONTWAIT); #endif return sent; } Loading @@ -451,6 +478,21 @@ size_t udp::recvBatchAddr(std::vector<std::vector<uint8_t>>& out, const int batch = std::min(max_count, 64); #ifdef NETPLUS_NO_MMSG // Fallback: recvfrom loop for platforms without recvmmsg static thread_local std::vector<uint8_t> recv_buf(65535); for (int i = 0; i < batch; ++i) { sockaddr_storage peer{}; socklen_t peer_len = sizeof(peer); ssize_t n = ::recvfrom(_Socket, recv_buf.data(), recv_buf.size(), MSG_DONTWAIT, reinterpret_cast<sockaddr*>(&peer), &peer_len); if (n <= 0) break; out.emplace_back(recv_buf.data(), recv_buf.data() + n); addrs.push_back(peer); } return out.size(); #else // Use thread_local static buffers to avoid 64×65KB heap allocation per call. // These are reused across calls on the same thread. static constexpr int MAX_BATCH = 64; Loading Loading @@ -517,6 +559,7 @@ size_t udp::recvBatchAddr(std::vector<std::vector<uint8_t>>& out, } return out.size(); #endif // NETPLUS_NO_MMSG } } // namespace netplus