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);