From 2b8fc67d75683b64aff57e8ad656bb3da1676fd3 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Fri, 12 May 2023 10:50:38 -0400 Subject: [PATCH] update UDPSocket for future use; --- src/network/UDPSocket.cpp | 144 ++++++++++++++++++++++++++++++++++++-- src/network/UDPSocket.h | 13 +++- 2 files changed, 148 insertions(+), 9 deletions(-) diff --git a/src/network/UDPSocket.cpp b/src/network/UDPSocket.cpp index bdb42f5d..db759223 100644 --- a/src/network/UDPSocket.cpp +++ b/src/network/UDPSocket.cpp @@ -12,7 +12,7 @@ // /* * Copyright (C) 2006-2016,2020 by Jonathan Naylor G4KLX -* Copyright (C) 2017-2020 by Bryan Biedenkapp N2PLL +* Copyright (C) 2017-2020,2023 by Bryan Biedenkapp N2PLL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,6 +41,12 @@ using namespace network; +// --------------------------------------------------------------------------- +// Constants +// --------------------------------------------------------------------------- + +#define MAX_BUFFER_COUNT 256 + // --------------------------------------------------------------------------- // Public Class Members // --------------------------------------------------------------------------- @@ -266,8 +272,9 @@ int UDPSocket::read(uint8_t* buffer, uint32_t length, sockaddr_storage& address, /// Length of data to write. /// IP address to write data to. /// +/// Total number of bytes written. /// Actual length of data written to remote UDP socket. -bool UDPSocket::write(const uint8_t* buffer, uint32_t length, const sockaddr_storage& address, uint32_t addrLen) +bool UDPSocket::write(const uint8_t* buffer, uint32_t length, const sockaddr_storage& address, uint32_t addrLen, int* lenWritten) { assert(buffer != nullptr); assert(length > 0U); @@ -279,26 +286,118 @@ bool UDPSocket::write(const uint8_t* buffer, uint32_t length, const sockaddr_sto continue; #if defined(_WIN32) || defined(_WIN64) - int ret = ::sendto(m_fd[i], (char*)buffer, length, 0, (sockaddr*)& address, addrLen); + int sent = ::sendto(m_fd[i], (char*)buffer, length, 0, (sockaddr*)& address, addrLen); #else - ssize_t ret = ::sendto(m_fd[i], (char*)buffer, length, 0, (sockaddr*)& address, addrLen); + ssize_t sent = ::sendto(m_fd[i], (char*)buffer, length, 0, (sockaddr*)& address, addrLen); #endif - if (ret < 0) { + if (sent < 0) { #if defined(_WIN32) || defined(_WIN64) LogError(LOG_NET, "Error returned from sendto, err: %lu", ::GetLastError()); #else LogError(LOG_NET, "Error returned from sendto, err: %d", errno); #endif + if (lenWritten != nullptr) { + *lenWritten = -1; + } } else { #if defined(_WIN32) || defined(_WIN64) - if (ret == int(length)) + if (sent == int(length)) result = true; #else - if (ret == ssize_t(length)) + if (sent == ssize_t(length)) result = true; #endif + if (lenWritten != nullptr) { + *lenWritten = sent; + } + } + } + + return result; +} + +/// +/// Write data to the UDP socket. +/// +/// Vector of buffers to write to socket. +/// IP address to write data to. +/// +/// Total number of bytes written. +/// Actual length of data written to remote UDP socket. +bool UDPSocket::write(BufferVector& buffers, const sockaddr_storage& address, uint32_t addrLen, int* lenWritten) +{ + bool result = false; + +#if defined(_WIN32) || defined(_WIN64) + DWORD sent = 0; + + if (buffers.size() > UINT16_MAX) { + // LOG_ERROR("Trying to send too large buffer"); + return RTP_INVALID_VALUE; + } +#else + int sent = 0; +#endif + +#if defined(_WIN32) || defined(_WIN64) + WSABUF wsaBuffers[MAX_BUFFER_COUNT]; + + // create WSABUFs from input buffers and send them at once + for (size_t i = 0; i < buffers.size(); ++i) { + wsaBuffers[i].len = (ULONG)buffers.at(i).first; + wsaBuffers[i].buf = (char*)buffers.at(i).second; + } +#else + struct mmsghdr header; + struct iovec chunks[MAX_BUFFER_COUNT]; + for (size_t i = 0; i < buffers.size(); ++i) { + chunks[i].iov_len = buffers.at(i).first; + chunks[i].iov_base = buffers.at(i).second; + sent += buffers.at(i).first; + } + + header.msg_hdr.msg_name = (void *)&address; + header.msg_hdr.msg_namelen = addrLen; + header.msg_hdr.msg_iov = chunks; + header.msg_hdr.msg_iovlen = buffers.size(); + header.msg_hdr.msg_control = 0; + header.msg_hdr.msg_controllen = 0; +#endif + + for (int i = 0; i < UDP_SOCKET_MAX; i++) { + if (m_fd[i] < 0 || m_af[i] != address.ss_family) + continue; + +#if defined(_WIN32) || defined(_WIN64) + int success = WSASendTo(m_fd[i], wsaBuffers, (DWORD)wsaBuffers.size(), &sent, 0, + (SOCKADDR *)&address, addrLen, nullptr, nullptr); + if (success != 0) { + LogError(LOG_NET, "Error returned from sendto, err: %lu", ::GetLastError()); + if (lenWritten != nullptr) { + *lenWritten = -1; + } + } +#else + if (sendmmsg(m_fd[i], &header, 1, 0) < 0) { + LogError(LOG_NET, "Error returned from sendmmsg, err: %d", errno); + if (lenWritten != nullptr) { + *lenWritten = -1; + } + } +#endif + + if (sent < 0) { + if (lenWritten != nullptr) { + *lenWritten = -1; + } + } + else { + result = true; + if (lenWritten != nullptr) { + *lenWritten = sent; + } } } @@ -488,6 +587,37 @@ std::string UDPSocket::address(const sockaddr_storage& addr) return address; } +/// +/// +/// +/// +/// +uint16_t UDPSocket::port(const sockaddr_storage& addr) +{ + uint16_t port = 0U; + + switch (addr.ss_family) { + case AF_INET: + { + struct sockaddr_in* in; + in = (struct sockaddr_in*) & addr; + port = ntohs(in->sin_port); + } + break; + case AF_INET6: + { + struct sockaddr_in6* in6; + in6 = (struct sockaddr_in6*) & addr; + port = ntohs(in6->sin6_port); + } + break; + default: + break; + } + + return port; +} + /// /// /// diff --git a/src/network/UDPSocket.h b/src/network/UDPSocket.h index 758898d6..f1e79a59 100644 --- a/src/network/UDPSocket.h +++ b/src/network/UDPSocket.h @@ -12,6 +12,7 @@ // /* * Copyright (C) 2006-2016,2020 by Jonathan Naylor G4KLX +* Copyright (C) 2023 by Bryan Biedenkapp N2PLL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,6 +34,7 @@ #include "Defines.h" #include +#include #if !defined(_WIN32) && !defined(_WIN64) #include @@ -60,6 +62,9 @@ enum IPMATCHTYPE { namespace network { + /* Vector of buffers that contain a full frames */ + typedef std::vector> BufferVector; + // --------------------------------------------------------------------------- // Class Declaration // This class implements low-level routines to communicate over a UDP @@ -72,7 +77,7 @@ namespace network UDPSocket(const std::string& address, uint16_t port = 0U); /// Initializes a new instance of the UDPSocket class. UDPSocket(uint16_t port = 0U); - /// Initializes a new instance of the UDPSocket class. + /// Finalizes a instance of the UDPSocket class. ~UDPSocket(); /// Opens UDP socket connection. @@ -85,7 +90,9 @@ namespace network /// Read data from the UDP socket. int read(uint8_t* buffer, uint32_t length, sockaddr_storage& address, uint32_t& addrLen); /// Write data to the UDP socket. - bool write(const uint8_t* buffer, uint32_t length, const sockaddr_storage& address, uint32_t addrLen); + bool write(const uint8_t* buffer, uint32_t length, const sockaddr_storage& address, uint32_t addrLen, int* lenWritten = nullptr); + /// Write data to the UDP socket. + bool write(BufferVector& buffers, const sockaddr_storage& address, uint32_t addrLen, int* lenWritten = nullptr); /// Closes the UDP socket connection. void close(); @@ -106,6 +113,8 @@ namespace network static bool match(const sockaddr_storage& addr1, const sockaddr_storage& addr2, IPMATCHTYPE type = IMT_ADDRESS_AND_PORT); /// static std::string address(const sockaddr_storage& addr); + /// + static uint16_t port(const sockaddr_storage& addr); /// static bool isNone(const sockaddr_storage& addr);