From 457be810a3902f16174ab78177ec49dfaf9ddfb8 Mon Sep 17 00:00:00 2001 From: SASANO Takayoshi Date: Sun, 6 Sep 2020 12:09:37 +0900 Subject: [PATCH] UDPSocket renewal Currently there is six variations of (IPv4) UDPSocket.cpp. - FMClients, NXDNClients, P25Clients, YSFClients(YSFParrot) use ::fprintf() for logging - YSFClients(YSFReflector) use LogError() and LogInfo() for logging special open(string &bindaddr) function - YSFClients(YSFGateway), DAPNETGateway use LogError() and LogInfo() for logging - P25Clients, DMRGateway use LogError() for logging no LogInfo("Opening UDP port on") message - NXDNClients use LogError() for logging no LogInfo("Opening UDP port on") message add #include - MMDVMHost use LogError() for logging no LogInfo("Opening UDP port on") message no assert(!address.empty()) at constructor to avoid explosion, commonized them. switch ::fprintf()/LogError by #define HAVE_LOG_H always display LogInfo("Opening UDP port on") message delete #include , this is not needed no assert(!address.empty()) at constructor and to support YSFReflector, add multiple socket support. default is #define UDP_SOCKET_MAX 1 so normally this feature is disabled. added these functions. CUDPSocket() (constructor without any parameters) open(index, af, addr, port) close(index) CUDPSocket() means CUDPSocket(address = "", port = 0) index selects socket, address and port is defined at open. to have compatibility for old codes, these function works as CUDPSocket(addr, port) store addr and port to index #0 CUDPSocket(port) store addr = "" and port to index #0 open() open with addr and port of index #0, AF_UNSPEC open(af) open with addr and port of index #0, specified af close() close *all* sockets read/write operation is for all opened sockets. --- Makefile | 1 + UDPSocket.cpp | 175 ++++++++++++++++++++++++++++++++------------------ UDPSocket.h | 18 ++++-- 3 files changed, 128 insertions(+), 66 deletions(-) diff --git a/Makefile b/Makefile index 1ad3c8a..16c0195 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ LIBS = -lpthread #CFLAGS = -g -O3 -Wall -DUSE_GPSD -std=c++0x -pthread #LIBS = -lpthread -lgps +CFLAGS += -DHAVE_LOG_H LDFLAGS = -g OBJECTS = \ diff --git a/UDPSocket.cpp b/UDPSocket.cpp index 0bbe27b..bfdc057 100644 --- a/UDPSocket.cpp +++ b/UDPSocket.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2016 by Jonathan Naylor G4KLX + * Copyright (C) 2006-2016,2018,2020 by Jonathan Naylor G4KLX * * 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 @@ -17,7 +17,6 @@ */ #include "UDPSocket.h" -#include "Log.h" #include @@ -26,11 +25,14 @@ #include #endif +#if defined(HAVE_LOG_H) +#include "Log.h" +#else +#define LogError(fmt, ...) ::fprintf(stderr, fmt "\n", ## __VA_ARGS__) +#define LogInfo(fmt, ...) ::fprintf(stderr, fmt "\n", ## __VA_ARGS__) +#endif -CUDPSocket::CUDPSocket(const std::string& address, unsigned int port) : -m_address(address), -m_port(port), -m_fd(-1) +CUDPSocket::CUDPSocket(const std::string& address, unsigned int port) { #if defined(_WIN32) || defined(_WIN64) WSAData data; @@ -38,19 +40,26 @@ m_fd(-1) if (wsaRet != 0) LogError("Error from WSAStartup"); #endif + for (int i = 0; i < UDP_SOCKET_MAX; i++) { + m_address[i] = ""; + m_port[i] = 0U; + m_af[i] = 0U; + m_fd[i] = -1; + } + + m_address[0] = address; + m_port[0] = port; + m_counter = 0U; } -CUDPSocket::CUDPSocket(unsigned int port) : -m_address(), -m_port(port), -m_fd(-1) +CUDPSocket::CUDPSocket(unsigned int port) { -#if defined(_WIN32) || defined(_WIN64) - WSAData data; - int wsaRet = ::WSAStartup(MAKEWORD(2, 2), &data); - if (wsaRet != 0) - LogError("Error from WSAStartup"); -#endif + CUDPSocket("", port); +} + +CUDPSocket::CUDPSocket() +{ + CUDPSocket("", 0U); } CUDPSocket::~CUDPSocket() @@ -149,12 +158,17 @@ bool CUDPSocket::isnone(const sockaddr_storage &addr) bool CUDPSocket::open() { - return open(AF_UNSPEC); + return open(0, AF_UNSPEC, m_address[0], m_port[0]); } bool CUDPSocket::open(const unsigned int af) { - int err; + return open(0, af, m_address[0], m_port[0]); +} + +bool CUDPSocket::open(const unsigned int index, const unsigned int af, const std::string& address, const unsigned int port) +{ + int err, fd; sockaddr_storage addr; unsigned int addrlen; struct addrinfo hints; @@ -164,14 +178,14 @@ bool CUDPSocket::open(const unsigned int af) hints.ai_family = af; /* to determine protocol family, call lookup() first. */ - err = lookup(m_address, m_port, addr, addrlen, hints); + err = lookup(address, port, addr, addrlen, hints); if (err) { - LogError("The local address is invalid - %s", m_address.c_str()); + LogError("The local address is invalid - %s", address.c_str()); return false; } - m_fd = ::socket(addr.ss_family, SOCK_DGRAM, 0); - if (m_fd < 0) { + fd = ::socket(addr.ss_family, SOCK_DGRAM, 0); + if (fd < 0) { #if defined(_WIN32) || defined(_WIN64) LogError("Cannot create the UDP socket, err: %lu", ::GetLastError()); #else @@ -180,9 +194,14 @@ bool CUDPSocket::open(const unsigned int af) return false; } - if (m_port > 0U) { + m_address[index] = address; + m_port[index] = port; + m_af[index] = addr.ss_family; + m_fd[index] = fd; + + if (port > 0U) { int reuse = 1; - if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) { + if (::setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) { #if defined(_WIN32) || defined(_WIN64) LogError("Cannot set the UDP socket option, err: %lu", ::GetLastError()); #else @@ -191,7 +210,7 @@ bool CUDPSocket::open(const unsigned int af) return false; } - if (::bind(m_fd, (sockaddr*)&addr, addrlen) == -1) { + if (::bind(fd, (sockaddr*)&addr, addrlen) == -1) { #if defined(_WIN32) || defined(_WIN64) LogError("Cannot bind the UDP address, err: %lu", ::GetLastError()); #else @@ -199,6 +218,8 @@ bool CUDPSocket::open(const unsigned int af) #endif return false; } + + LogInfo("Opening UDP port on %u", port); } return true; @@ -210,30 +231,43 @@ int CUDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storag assert(length > 0U); // Check that the readfrom() won't block - fd_set readFds; - FD_ZERO(&readFds); -#if defined(_WIN32) || defined(_WIN64) - FD_SET((unsigned int)m_fd, &readFds); -#else - FD_SET(m_fd, &readFds); -#endif + int i, n; + struct pollfd pfd[UDP_SOCKET_MAX]; + for (i = n = 0; i < UDP_SOCKET_MAX; i++) { + if (m_fd[i] >= 0) { + pfd[n].fd = m_fd[i]; + pfd[n].events = POLLIN; + n++; + } + } + + // no socket descriptor to receive + if (n == 0) + return 0; // Return immediately - timeval tv; - tv.tv_sec = 0L; - tv.tv_usec = 0L; - - int ret = ::select(m_fd + 1, &readFds, NULL, NULL, &tv); +#if defined(_WIN32) || defined(_WIN64) + int ret = WSAPoll(pfd, n, 0); +#else + int ret = ::poll(pfd, n, 0); +#endif if (ret < 0) { #if defined(_WIN32) || defined(_WIN64) - LogError("Error returned from UDP select, err: %lu", ::GetLastError()); + LogError("Error returned from UDP poll, err: %lu", ::GetLastError()); #else - LogError("Error returned from UDP select, err: %d", errno); + LogError("Error returned from UDP poll, err: %d", errno); #endif return -1; } - if (ret == 0) + int index; + for (i = 0; i < n; i++) { + // round robin + index = (i + m_counter) % n; + if (pfd[index].revents & POLLIN) + break; + } + if (i == n) return 0; #if defined(_WIN32) || defined(_WIN64) @@ -243,9 +277,9 @@ int CUDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storag #endif #if defined(_WIN32) || defined(_WIN64) - int len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&address, &size); + int len = ::recvfrom(pfd[index].fd, (char*)buffer, length, 0, (sockaddr *)&address, &size); #else - ssize_t len = ::recvfrom(m_fd, (char*)buffer, length, 0, (sockaddr *)&address, &size); + ssize_t len = ::recvfrom(pfd[index].fd, (char*)buffer, length, 0, (sockaddr *)&address, &size); #endif if (len <= 0) { #if defined(_WIN32) || defined(_WIN64) @@ -256,6 +290,7 @@ int CUDPSocket::read(unsigned char* buffer, unsigned int length, sockaddr_storag return -1; } + m_counter++; address_length = size; return len; } @@ -265,36 +300,52 @@ bool CUDPSocket::write(const unsigned char* buffer, unsigned int length, const s assert(buffer != NULL); assert(length > 0U); + bool result = false; + + 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 ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&address, address_length); + int ret = ::sendto(m_fd[i], (char *)buffer, length, 0, (sockaddr *)&address, address_length); #else - ssize_t ret = ::sendto(m_fd, (char *)buffer, length, 0, (sockaddr *)&address, address_length); + ssize_t ret = ::sendto(m_fd[i], (char *)buffer, length, 0, (sockaddr *)&address, address_length); #endif - if (ret < 0) { + + if (ret < 0) { #if defined(_WIN32) || defined(_WIN64) - LogError("Error returned from sendto, err: %lu", ::GetLastError()); + LogError("Error returned from sendto, err: %lu", ::GetLastError()); #else - LogError("Error returned from sendto, err: %d", errno); + LogError("Error returned from sendto, err: %d", errno); #endif - return false; + } else { +#if defined(_WIN32) || defined(_WIN64) + if (ret == int(length)) + result = true; +#else + if (ret == ssize_t(length)) + result = true; +#endif + } } -#if defined(_WIN32) || defined(_WIN64) - if (ret != int(length)) - return false; -#else - if (ret != ssize_t(length)) - return false; -#endif - - return true; + return result; } void CUDPSocket::close() { -#if defined(_WIN32) || defined(_WIN64) - ::closesocket(m_fd); -#else - ::close(m_fd); -#endif + for (int i = 0; i < UDP_SOCKET_MAX; i++) + close(m_fd[i]); +} + +void CUDPSocket::close(const unsigned int index) +{ + if (m_fd[index] >= 0) { +#if defined(_WIN32) || defined(_WIN64) + ::closesocket(m_fd[index]); +#else + ::close(m_fd[index]); +#endif + m_fd[index] = -1; + } } diff --git a/UDPSocket.h b/UDPSocket.h index ad5b2a1..5d9fd07 100644 --- a/UDPSocket.h +++ b/UDPSocket.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009-2011,2013,2015,2016 by Jonathan Naylor G4KLX + * Copyright (C) 2009-2011,2013,2015,2016,2020 by Jonathan Naylor G4KLX * * 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 @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -35,19 +36,26 @@ #include #endif +#if !defined(UDP_SOCKET_MAX) +#define UDP_SOCKET_MAX 1 +#endif + class CUDPSocket { public: CUDPSocket(const std::string& address, unsigned int port = 0U); CUDPSocket(unsigned int port = 0U); + CUDPSocket(); ~CUDPSocket(); bool open(); bool open(const unsigned int af); + bool open(const unsigned int index, const unsigned int af, const std::string& address, const unsigned int port); int read(unsigned char* buffer, unsigned int length, sockaddr_storage& address, unsigned int &address_length); bool write(const unsigned char* buffer, unsigned int length, const sockaddr_storage& address, unsigned int address_length); void close(); + void close(const unsigned int index); static int lookup(const std::string& hostName, unsigned int port, sockaddr_storage &address, unsigned int &address_length); static int lookup(const std::string& hostName, unsigned int port, sockaddr_storage &address, unsigned int &address_length, struct addrinfo &hints); @@ -56,9 +64,11 @@ public: static bool isnone(const sockaddr_storage &addr); private: - std::string m_address; - unsigned short m_port; - int m_fd; + std::string m_address[UDP_SOCKET_MAX]; + unsigned short m_port[UDP_SOCKET_MAX]; + unsigned int m_af[UDP_SOCKET_MAX]; + int m_fd[UDP_SOCKET_MAX]; + unsigned int m_counter; }; #endif