Files
2026-08-29 23:17:41 +02:00

907 lines
29 KiB
C++

/**
* @file UDPSServer.cpp
* @brief Implementation of UDPSServer — multi-client UDPS session management.
*/
#include "UDPSServer.h"
#include "AdvancedErrorManagement.h"
#include "ErrorType.h"
#include "HighResolutionTimer.h"
#include "MemoryOperationsHelper.h"
#include "StreamString.h"
#include <arpa/inet.h>
#include <poll.h>
#include <sys/select.h>
#include <sys/socket.h>
namespace MARTe {
/* Out-of-line definitions for class-scope static const members that are
* ODR-used (e.g. passed through varargs in REPORT_ERROR). */
const uint32 UDPSServer::UDPS_SERVER_MAX_UDP_PAYLOAD;
// ---------------------------------------------------------------------------
// Constructor / Destructor
// ---------------------------------------------------------------------------
UDPSServer::UDPSServer()
: port(0u), maxPayloadSize(UDPS_SERVER_DEFAULT_MAX_PAYLOAD), dataPort(0u),
useMulticast(false), clientTimeoutTicks(0u), numUnicastClients(0u),
numTCPClients(0u), cachedConfig(NULL_PTR(uint8 *)), cachedConfigSize(0u),
sendBuf(NULL_PTR(uint8 *)), sendBufCapacity(0u), configCounter(0u),
started(false) {
for (uint32 i = 0u; i < UDPS_SERVER_MAX_UNICAST_CLIENTS; i++) {
unicastClients[i].ipAddr[0] = '\0';
unicastClients[i].clientPort = 0u;
unicastClients[i].lastSeenTicks = 0u;
unicastClients[i].active = false;
unicastClients[i].isStatic = false;
}
for (uint32 i = 0u; i < UDPS_SERVER_MAX_TCP_CLIENTS; i++) {
tcpClients[i] = NULL_PTR(BasicTCPSocket *);
}
}
UDPSServer::~UDPSServer() { (void)Stop(); }
// ---------------------------------------------------------------------------
// Initialise
// ---------------------------------------------------------------------------
bool UDPSServer::Initialise(StructuredDataI &data) {
uint32 portU32 = 0u;
if (data.Read("Port", portU32)) {
port = static_cast<uint16>(portU32);
} else {
REPORT_ERROR_STATIC(ErrorManagement::ParametersError,
"UDPSServer: Port not specified.");
return false;
}
/* port == 0 is valid: unicast push-only mode (no serverSocket bind,
* no CONNECT/DISCONNECT/ACK reception). Clients added via AddStaticClient().
*/
StreamString mcGroup;
if (data.Read("MulticastGroup", mcGroup) && (mcGroup.Size() > 0u)) {
multicastGroup = mcGroup;
useMulticast = true;
}
if (useMulticast) {
uint32 dpU32 = static_cast<uint32>(port) + 1u;
(void)data.Read("DataPort", dpU32);
dataPort = static_cast<uint16>(dpU32);
if (dataPort == port) {
REPORT_ERROR_STATIC(
ErrorManagement::ParametersError,
"UDPSServer: DataPort (%u) must differ from Port (%u).",
static_cast<uint32>(dataPort), static_cast<uint32>(port));
return false;
}
if (!data.Read("Interface", interface)) {
REPORT_ERROR_STATIC(
ErrorManagement::ParametersError,
"Missing mandatory `Interface` field for multicast operations");
return false;
}
}
uint32 mps = UDPS_SERVER_DEFAULT_MAX_PAYLOAD;
(void)data.Read("MaxPayloadSize", mps);
/* A payload larger than the largest legal UDP datagram cannot be sent —
* sendto would fail with EMSGSIZE and the whole cycle would be dropped. */
if (mps > UDPS_SERVER_MAX_UDP_PAYLOAD) {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
"UDPSServer: MaxPayloadSize %u exceeds the UDP datagram "
"limit (%u); clamping to %u.",
mps, UDPS_SERVER_MAX_UDP_PAYLOAD,
UDPS_SERVER_MAX_UDP_PAYLOAD);
mps = UDPS_SERVER_MAX_UDP_PAYLOAD;
}
maxPayloadSize = mps;
uint32 timeoutSecs = UDPS_SERVER_DEFAULT_CLIENT_TIMEOUT_S;
(void)data.Read("ClientTimeout", timeoutSecs);
clientTimeoutTicks = (timeoutSecs > 0u) ? (static_cast<uint64>(timeoutSecs) *
HighResolutionTimer::Frequency())
: 0u;
return true;
}
// ---------------------------------------------------------------------------
// Start / Stop
// ---------------------------------------------------------------------------
bool UDPSServer::Start() {
if (started) {
return true;
}
sendBufCapacity = UDPS_HEADER_SIZE + maxPayloadSize;
sendBuf = new uint8[sendBufCapacity];
if (sendBuf == NULL_PTR(uint8 *)) {
REPORT_ERROR_STATIC(ErrorManagement::FatalError,
"UDPSServer: Could not allocate send buffer.");
return false;
}
bool ok = true;
if (useMulticast) {
// TCP listener for CONNECT + CONFIG
ok = tcpListener.Open();
if (ok) {
ok = tcpListener.Listen(port, 5);
}
if (ok) {
tcpListener.SetBlocking(false);
}
// UDP data socket connected to multicast group.
// Set IP_MULTICAST_IF so outgoing datagrams leave on the specified
// interface rather than whichever the kernel routing table picks.
ok &= dataSocket.Open();
if (ok) {
struct in_addr localIf;
localIf.s_addr = inet_addr(interface.Buffer());
int fd = static_cast<int>(dataSocket.GetWriteHandle());
ok = (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
&localIf, static_cast<socklen_t>(sizeof(localIf))) == 0);
}
ok &= dataSocket.Connect(multicastGroup.Buffer(), dataPort);
if (!ok) {
REPORT_ERROR_STATIC(
ErrorManagement::FatalError,
"UDPSServer: Failed to open multicast sockets on port %u.",
static_cast<uint32>(port));
}
} else {
// Unicast send socket (unconnected; SetDestination per Write)
ok = uniSendSocket.Open();
if (!ok) {
REPORT_ERROR_STATIC(ErrorManagement::FatalError,
"UDPSServer: Failed to open unicast send socket.");
}
// Receive socket: only bind if port != 0 (skip for push-only mode)
if (ok && (port != 0u)) {
ok = serverSocket.Open();
if (ok) {
ok = serverSocket.Listen(port); // UDP "listen" = bind
}
if (!ok) {
REPORT_ERROR_STATIC(
ErrorManagement::FatalError,
"UDPSServer: Failed to bind receive socket on port %u.",
static_cast<uint32>(port));
}
}
}
started = ok;
return ok;
}
bool UDPSServer::Stop() {
if (!started) {
return true;
}
// Evict all unicast clients
for (uint32 i = 0u; i < UDPS_SERVER_MAX_UNICAST_CLIENTS; i++) {
if (unicastClients[i].active) {
EvictUnicastClient(i);
}
}
numUnicastClients = 0u;
// Close all TCP clients
for (uint32 i = 0u; i < UDPS_SERVER_MAX_TCP_CLIENTS; i++) {
if (tcpClients[i] != NULL_PTR(BasicTCPSocket *)) {
EvictTCPClient(i);
}
}
numTCPClients = 0u;
// Close sockets
if (serverSocket.IsValid()) {
(void)serverSocket.Close();
}
if (uniSendSocket.IsValid()) {
(void)uniSendSocket.Close();
}
if (tcpListener.IsValid()) {
(void)tcpListener.Close();
}
if (dataSocket.IsValid()) {
(void)dataSocket.Close();
}
// Free heap buffers
if (cachedConfig != NULL_PTR(uint8 *)) {
delete[] cachedConfig;
cachedConfig = NULL_PTR(uint8 *);
cachedConfigSize = 0u;
}
if (sendBuf != NULL_PTR(uint8 *)) {
delete[] sendBuf;
sendBuf = NULL_PTR(uint8 *);
sendBufCapacity = 0u;
}
started = false;
return true;
}
// ---------------------------------------------------------------------------
// ServiceClients
// ---------------------------------------------------------------------------
void UDPSServer::ServiceClients() {
if (!started) {
return;
}
if (useMulticast) {
// Poll TCP listener for new connections (non-blocking).
// Guard with poll() first so WaitConnection is never called on an empty
// queue — avoids the MARTe2 "Failed accept in unblocking mode" log spam.
if (tcpListener.IsValid()) {
struct pollfd pfd;
pfd.fd = static_cast<int>(tcpListener.GetReadHandle());
pfd.events = POLLIN;
pfd.revents = 0;
bool pending = (::poll(&pfd, 1u, 0) > 0) && ((pfd.revents & POLLIN) != 0);
BasicTCPSocket *newConn =
pending ? tcpListener.WaitConnection(0u) : NULL_PTR(BasicTCPSocket *);
if (newConn != NULL_PTR(BasicTCPSocket *)) {
// Find free TCP client slot
uint32 freeSlot = UDPS_SERVER_MAX_TCP_CLIENTS;
for (uint32 i = 0u; i < UDPS_SERVER_MAX_TCP_CLIENTS; i++) {
if (tcpClients[i] == NULL_PTR(BasicTCPSocket *)) {
freeSlot = i;
break;
}
}
if (freeSlot < UDPS_SERVER_MAX_TCP_CLIENTS) {
HandleMulticastTCPConnect(newConn, freeSlot);
} else {
REPORT_ERROR_STATIC(
ErrorManagement::Warning,
"UDPSServer: TCP client table full, rejecting new connection.");
(void)newConn->Close();
delete newConn;
}
}
}
// Poll existing TCP clients for DISCONNECT
for (uint32 i = 0u; i < UDPS_SERVER_MAX_TCP_CLIENTS; i++) {
if (tcpClients[i] == NULL_PTR(BasicTCPSocket *)) {
continue;
}
// Non-blocking peek
int fd = tcpClients[i]->GetReadHandle();
if (fd < 0 || fd >= FD_SETSIZE) {
continue; /* HI-6: skip FDs outside select range */
}
fd_set rset;
FD_ZERO(&rset);
FD_SET(fd, &rset);
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
int nready = select(fd + 1, &rset, NULL, NULL, &tv);
if (nready > 0) {
uint8 pktBuf[UDPS_HEADER_SIZE];
uint32 recvSize = UDPS_HEADER_SIZE;
bool recvOk =
tcpClients[i]->Read(reinterpret_cast<char8 *>(pktBuf), recvSize);
if (!recvOk || (recvSize == 0u)) {
REPORT_ERROR_STATIC(ErrorManagement::Information,
"UDPSServer: TCP client disconnected (slot %u).",
i);
EvictTCPClient(i);
continue;
}
if (recvSize >= UDPS_HEADER_SIZE) {
const UDPSPacketHeader *hdr =
reinterpret_cast<const UDPSPacketHeader *>(pktBuf);
if ((hdr->magic == UDPS_MAGIC) &&
(hdr->type == UDPS_TYPE_DISCONNECT)) {
REPORT_ERROR_STATIC(
ErrorManagement::Information,
"UDPSServer: TCP client sent DISCONNECT (slot %u).", i);
EvictTCPClient(i);
}
}
}
}
} else {
// Unicast: poll serverSocket for CONNECT / DISCONNECT / ACK
if (!serverSocket.IsValid()) {
return;
}
int fd = serverSocket.GetReadHandle();
if (fd < 0 || fd >= FD_SETSIZE) {
return; /* HI-6 */
}
fd_set rset;
FD_ZERO(&rset);
FD_SET(fd, &rset);
struct timeval tv = {0, 0};
int nready = select(fd + 1, &rset, NULL, NULL, &tv);
while (nready > 0) {
uint8 pktBuf[UDPS_HEADER_SIZE + 4u];
uint32 recvSize = static_cast<uint32>(sizeof(pktBuf));
bool recvOk =
serverSocket.Read(reinterpret_cast<char8 *>(pktBuf), recvSize);
if (!recvOk || (recvSize < UDPS_HEADER_SIZE)) {
break;
}
const UDPSPacketHeader *hdr =
reinterpret_cast<const UDPSPacketHeader *>(pktBuf);
if (hdr->magic != UDPS_MAGIC) {
break;
}
InternetHost src = serverSocket.GetSource();
if (hdr->type == UDPS_TYPE_CONNECT) {
HandleUnicastConnect(src);
} else if (hdr->type == UDPS_TYPE_DISCONNECT) {
HandleUnicastDisconnect(src);
} else if (hdr->type == UDPS_TYPE_ACK) {
HandleUnicastAck(src);
}
// Check if more data is ready
FD_ZERO(&rset);
FD_SET(fd, &rset);
tv.tv_sec = 0;
tv.tv_usec = 0;
nready = select(fd + 1, &rset, NULL, NULL, &tv);
}
// Evict stale clients (if timeout is configured)
if (clientTimeoutTicks > 0u) {
EvictStaleUnicastClients();
}
}
}
// ---------------------------------------------------------------------------
// SendConfig
// ---------------------------------------------------------------------------
bool UDPSServer::SendConfig(const uint8 *payload, uint32 payloadSize) {
if (!started) {
return false;
}
// Cache for future CONNECT clients
(void)CacheConfig(payload, payloadSize);
configCounter++;
bool ok = true;
if (useMulticast) {
// Send CONFIG over TCP to each connected client
for (uint32 i = 0u; i < UDPS_SERVER_MAX_TCP_CLIENTS; i++) {
if (tcpClients[i] == NULL_PTR(BasicTCPSocket *)) {
continue;
}
bool sent = SendFragmentedTCP(*tcpClients[i], UDPS_TYPE_CONFIG,
configCounter, payload, payloadSize);
if (!sent) {
REPORT_ERROR_STATIC(
ErrorManagement::Warning,
"UDPSServer: CONFIG send failed to TCP client %u, evicting.", i);
EvictTCPClient(i);
ok = false;
}
}
} else {
// Send CONFIG to each unicast client
for (uint32 i = 0u; i < UDPS_SERVER_MAX_UNICAST_CLIENTS; i++) {
if (!unicastClients[i].active) {
continue;
}
InternetHost dest(unicastClients[i].clientPort, unicastClients[i].ipAddr);
bool sent = SendFragmentedUDP(uniSendSocket, &dest, UDPS_TYPE_CONFIG,
configCounter, payload, payloadSize);
if (!sent) {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
"UDPSServer: CONFIG send failed to %s:%u.",
unicastClients[i].ipAddr,
static_cast<uint32>(unicastClients[i].clientPort));
ok = false;
}
}
}
return ok;
}
// ---------------------------------------------------------------------------
// SendData
// ---------------------------------------------------------------------------
bool UDPSServer::SendData(uint32 counter, const uint8 *payload,
uint32 payloadSize) {
if (!started) {
return false;
}
bool ok = true;
if (useMulticast) {
// Single multicast write (no dest needed — socket already connected)
bool sent =
SendFragmentedUDP(dataSocket, NULL_PTR(InternetHost *), UDPS_TYPE_DATA,
counter, payload, payloadSize);
if (!sent) {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
"UDPSServer: DATA send to multicast group failed.");
ok = false;
}
} else {
for (uint32 i = 0u; i < UDPS_SERVER_MAX_UNICAST_CLIENTS; i++) {
if (!unicastClients[i].active) {
continue;
}
InternetHost dest(unicastClients[i].clientPort, unicastClients[i].ipAddr);
bool sent = SendFragmentedUDP(uniSendSocket, &dest, UDPS_TYPE_DATA,
counter, payload, payloadSize);
if (!sent) {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
"UDPSServer: DATA send failed to %s:%u.",
unicastClients[i].ipAddr,
static_cast<uint32>(unicastClients[i].clientPort));
ok = false;
}
}
}
return ok;
}
// ---------------------------------------------------------------------------
// AddStaticClient
// ---------------------------------------------------------------------------
bool UDPSServer::AddStaticClient(const char8 *ip, uint16 port_) {
if ((ip == NULL_PTR(const char8 *)) || (ip[0] == '\0')) {
return false;
}
// Check for duplicate
uint32 existing = FindUnicastClient(ip, port_);
if (existing < UDPS_SERVER_MAX_UNICAST_CLIENTS) {
unicastClients[existing].isStatic = true; // ensure it's marked static
return true;
}
// Find free slot
uint32 freeSlot = UDPS_SERVER_MAX_UNICAST_CLIENTS;
for (uint32 i = 0u; i < UDPS_SERVER_MAX_UNICAST_CLIENTS; i++) {
if (!unicastClients[i].active) {
freeSlot = i;
break;
}
}
if (freeSlot >= UDPS_SERVER_MAX_UNICAST_CLIENTS) {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
"UDPSServer: Unicast client table full, cannot add "
"static client %s:%u.",
ip, static_cast<uint32>(port_));
return false;
}
// Populate slot
uint32 ipLen = 0u;
while ((ipLen < 63u) && (ip[ipLen] != '\0')) {
unicastClients[freeSlot].ipAddr[ipLen] = ip[ipLen];
ipLen++;
}
unicastClients[freeSlot].ipAddr[ipLen] = '\0';
unicastClients[freeSlot].clientPort = port_;
unicastClients[freeSlot].lastSeenTicks = HighResolutionTimer::Counter();
unicastClients[freeSlot].active = true;
unicastClients[freeSlot].isStatic = true;
numUnicastClients++;
REPORT_ERROR_STATIC(ErrorManagement::Information,
"UDPSServer: Static client added: %s:%u.", ip,
static_cast<uint32>(port_));
return true;
}
// ---------------------------------------------------------------------------
// Accessors
// ---------------------------------------------------------------------------
uint32 UDPSServer::GetClientCount() const {
uint32 count = 0u;
for (uint32 i = 0u; i < UDPS_SERVER_MAX_UNICAST_CLIENTS; i++) {
if (unicastClients[i].active) {
count++;
}
}
for (uint32 i = 0u; i < UDPS_SERVER_MAX_TCP_CLIENTS; i++) {
if (tcpClients[i] != NULL_PTR(BasicTCPSocket *)) {
count++;
}
}
return count;
}
bool UDPSServer::HasClients() const { return (GetClientCount() > 0u); }
bool UDPSServer::IsMulticast() const { return useMulticast; }
uint16 UDPSServer::GetPort() const { return port; }
uint32 UDPSServer::GetMaxPayloadSize() const { return maxPayloadSize; }
// ---------------------------------------------------------------------------
// Private: SendFragmentedUDP
// ---------------------------------------------------------------------------
bool UDPSServer::SendFragmentedUDP(BasicUDPSocket &sock, InternetHost *dest,
uint8 type, uint32 counter,
const uint8 *payload, uint32 payloadSize) {
uint32 maxChunk = maxPayloadSize; // payload bytes per fragment (excl. header)
uint32 totalFrags =
(payloadSize == 0u) ? 1u : ((payloadSize + maxChunk - 1u) / maxChunk);
bool ok = true;
uint32 offs = 0u;
for (uint32 f = 0u; (f < totalFrags) && ok; f++) {
uint32 chunkSize = payloadSize - offs;
if (chunkSize > maxChunk) {
chunkSize = maxChunk;
}
UDPSBuildHeader(sendBuf, type, counter, static_cast<uint16>(f),
static_cast<uint16>(totalFrags), chunkSize);
if (chunkSize > 0u) {
(void)MemoryOperationsHelper::Copy(sendBuf + UDPS_HEADER_SIZE,
payload + offs, chunkSize);
}
uint32 sendSize = UDPS_HEADER_SIZE + chunkSize;
if (dest != NULL_PTR(InternetHost *)) {
(void)sock.SetDestination(*dest);
}
ok = sock.Write(reinterpret_cast<const char8 *>(sendBuf), sendSize);
if (!ok) {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
"UDPSServer: UDP fragment %u/%u write failed.",
f + 1u, totalFrags);
}
offs += chunkSize;
}
return ok;
}
// ---------------------------------------------------------------------------
// Private: SendFragmentedTCP
// ---------------------------------------------------------------------------
bool UDPSServer::SendFragmentedTCP(BasicTCPSocket &sock, uint8 type,
uint32 counter, const uint8 *payload,
uint32 payloadSize) {
uint32 maxChunk = maxPayloadSize;
uint32 totalFrags =
(payloadSize == 0u) ? 1u : ((payloadSize + maxChunk - 1u) / maxChunk);
bool ok = true;
uint32 offs = 0u;
for (uint32 f = 0u; (f < totalFrags) && ok; f++) {
uint32 chunkSize = payloadSize - offs;
if (chunkSize > maxChunk) {
chunkSize = maxChunk;
}
UDPSBuildHeader(sendBuf, type, counter, static_cast<uint16>(f),
static_cast<uint16>(totalFrags), chunkSize);
if (chunkSize > 0u) {
(void)MemoryOperationsHelper::Copy(sendBuf + UDPS_HEADER_SIZE,
payload + offs, chunkSize);
}
uint32 sendSize = UDPS_HEADER_SIZE + chunkSize;
ok = sock.Write(reinterpret_cast<const char8 *>(sendBuf), sendSize);
if (!ok) {
REPORT_ERROR_STATIC(ErrorManagement::Warning,
"UDPSServer: TCP fragment %u/%u write failed.",
f + 1u, totalFrags);
}
offs += chunkSize;
}
return ok;
}
// ---------------------------------------------------------------------------
// Private: EvictStaleUnicastClients
// ---------------------------------------------------------------------------
void UDPSServer::EvictStaleUnicastClients() {
uint64 now = HighResolutionTimer::Counter();
for (uint32 i = 0u; i < UDPS_SERVER_MAX_UNICAST_CLIENTS; i++) {
if (!unicastClients[i].active || unicastClients[i].isStatic) {
continue;
}
uint64 elapsed = now - unicastClients[i].lastSeenTicks;
if (elapsed >= clientTimeoutTicks) {
REPORT_ERROR_STATIC(ErrorManagement::Information,
"UDPSServer: Evicting stale client %s:%u.",
unicastClients[i].ipAddr,
static_cast<uint32>(unicastClients[i].clientPort));
EvictUnicastClient(i);
}
}
}
// ---------------------------------------------------------------------------
// Private: HandleUnicastConnect
// ---------------------------------------------------------------------------
void UDPSServer::HandleUnicastConnect(const InternetHost &src) {
StreamString srcAddrStr = src.GetAddress();
const char8 *srcAddr = srcAddrStr.Buffer();
uint16 srcPort = src.GetPort();
REPORT_ERROR_STATIC(ErrorManagement::Information,
"UDPSServer: CONNECT from %s:%u.", srcAddr,
static_cast<uint32>(srcPort));
// Check if this client is already known
uint32 existing = FindUnicastClient(srcAddr, srcPort);
if (existing < UDPS_SERVER_MAX_UNICAST_CLIENTS) {
// Refresh last-seen and resend CONFIG
unicastClients[existing].lastSeenTicks = HighResolutionTimer::Counter();
if (cachedConfig != NULL_PTR(uint8 *)) {
InternetHost dest(srcPort, srcAddr);
configCounter++;
(void)SendFragmentedUDP(uniSendSocket, &dest, UDPS_TYPE_CONFIG,
configCounter, cachedConfig, cachedConfigSize);
}
return;
}
// New client — find a free slot (or evict oldest non-static)
uint32 slot = UDPS_SERVER_MAX_UNICAST_CLIENTS;
for (uint32 i = 0u; i < UDPS_SERVER_MAX_UNICAST_CLIENTS; i++) {
if (!unicastClients[i].active) {
slot = i;
break;
}
}
if (slot >= UDPS_SERVER_MAX_UNICAST_CLIENTS) {
slot = FindOldestUnicastClient();
if (slot < UDPS_SERVER_MAX_UNICAST_CLIENTS) {
REPORT_ERROR_STATIC(ErrorManagement::Information,
"UDPSServer: Client table full — evicting %s:%u.",
unicastClients[slot].ipAddr,
static_cast<uint32>(unicastClients[slot].clientPort));
EvictUnicastClient(slot);
} else {
REPORT_ERROR_STATIC(
ErrorManagement::Warning,
"UDPSServer: All slots occupied by static clients; rejecting %s:%u.",
srcAddr, static_cast<uint32>(srcPort));
return;
}
}
// Fill slot
uint32 addrLen = 0u;
while ((addrLen < 63u) && (srcAddr[addrLen] != '\0')) {
unicastClients[slot].ipAddr[addrLen] = srcAddr[addrLen];
addrLen++;
}
unicastClients[slot].ipAddr[addrLen] = '\0';
unicastClients[slot].clientPort = srcPort;
unicastClients[slot].lastSeenTicks = HighResolutionTimer::Counter();
unicastClients[slot].active = true;
unicastClients[slot].isStatic = false;
numUnicastClients++;
// Send cached CONFIG if available
if (cachedConfig != NULL_PTR(uint8 *)) {
InternetHost dest(srcPort, srcAddr);
configCounter++;
(void)SendFragmentedUDP(uniSendSocket, &dest, UDPS_TYPE_CONFIG,
configCounter, cachedConfig, cachedConfigSize);
}
}
// ---------------------------------------------------------------------------
// Private: HandleUnicastDisconnect
// ---------------------------------------------------------------------------
void UDPSServer::HandleUnicastDisconnect(const InternetHost &src) {
StreamString srcAddrStr = src.GetAddress();
const char8 *srcAddr = srcAddrStr.Buffer();
uint16 srcPort = src.GetPort();
uint32 slot = FindUnicastClient(srcAddr, srcPort);
if (slot < UDPS_SERVER_MAX_UNICAST_CLIENTS) {
REPORT_ERROR_STATIC(ErrorManagement::Information,
"UDPSServer: DISCONNECT from %s:%u.", srcAddr,
static_cast<uint32>(srcPort));
EvictUnicastClient(slot);
}
}
// ---------------------------------------------------------------------------
// Private: HandleUnicastAck
// ---------------------------------------------------------------------------
void UDPSServer::HandleUnicastAck(const InternetHost &src) {
StreamString srcAddrStr = src.GetAddress();
const char8 *srcAddr = srcAddrStr.Buffer();
uint16 srcPort = src.GetPort();
uint32 slot = FindUnicastClient(srcAddr, srcPort);
if (slot < UDPS_SERVER_MAX_UNICAST_CLIENTS) {
unicastClients[slot].lastSeenTicks = HighResolutionTimer::Counter();
}
}
// ---------------------------------------------------------------------------
// Private: FindUnicastClient
// ---------------------------------------------------------------------------
uint32 UDPSServer::FindUnicastClient(const char8 *ip, uint16 port_) const {
for (uint32 i = 0u; i < UDPS_SERVER_MAX_UNICAST_CLIENTS; i++) {
if (!unicastClients[i].active) {
continue;
}
if (unicastClients[i].clientPort != port_) {
continue;
}
// String compare
bool match = true;
uint32 k = 0u;
while ((k < 63u) && match) {
if (unicastClients[i].ipAddr[k] != ip[k]) {
match = false;
}
if (ip[k] == '\0') {
break;
}
k++;
}
if (match) {
return i;
}
}
return UDPS_SERVER_MAX_UNICAST_CLIENTS;
}
// ---------------------------------------------------------------------------
// Private: FindOldestUnicastClient
// ---------------------------------------------------------------------------
uint32 UDPSServer::FindOldestUnicastClient() const {
uint32 oldest = UDPS_SERVER_MAX_UNICAST_CLIENTS;
uint64 oldestTick = 0xFFFFFFFFFFFFFFFFuLL;
for (uint32 i = 0u; i < UDPS_SERVER_MAX_UNICAST_CLIENTS; i++) {
if (!unicastClients[i].active || unicastClients[i].isStatic) {
continue;
}
if (unicastClients[i].lastSeenTicks < oldestTick) {
oldestTick = unicastClients[i].lastSeenTicks;
oldest = i;
}
}
return oldest;
}
// ---------------------------------------------------------------------------
// Private: EvictUnicastClient
// ---------------------------------------------------------------------------
void UDPSServer::EvictUnicastClient(uint32 idx) {
if (idx >= UDPS_SERVER_MAX_UNICAST_CLIENTS) {
return;
}
unicastClients[idx].active = false;
if (numUnicastClients > 0u) {
numUnicastClients--;
}
}
// ---------------------------------------------------------------------------
// Private: HandleMulticastTCPConnect
// ---------------------------------------------------------------------------
void UDPSServer::HandleMulticastTCPConnect(BasicTCPSocket *newClient,
uint32 idx) {
if (idx >= UDPS_SERVER_MAX_TCP_CLIENTS) {
return;
}
tcpClients[idx] = newClient;
numTCPClients++;
REPORT_ERROR_STATIC(ErrorManagement::Information,
"UDPSServer: Multicast TCP client connected (slot %u).",
idx);
// Send cached CONFIG over TCP
if (cachedConfig != NULL_PTR(uint8 *)) {
configCounter++;
bool sent = SendFragmentedTCP(*newClient, UDPS_TYPE_CONFIG, configCounter,
cachedConfig, cachedConfigSize);
if (!sent) {
REPORT_ERROR_STATIC(
ErrorManagement::Warning,
"UDPSServer: Failed to send CONFIG to new TCP client (slot %u).",
idx);
EvictTCPClient(idx);
}
}
}
// ---------------------------------------------------------------------------
// Private: EvictTCPClient
// ---------------------------------------------------------------------------
void UDPSServer::EvictTCPClient(uint32 idx) {
if (idx >= UDPS_SERVER_MAX_TCP_CLIENTS) {
return;
}
if (tcpClients[idx] != NULL_PTR(BasicTCPSocket *)) {
(void)tcpClients[idx]->Close();
delete tcpClients[idx];
tcpClients[idx] = NULL_PTR(BasicTCPSocket *);
if (numTCPClients > 0u) {
numTCPClients--;
}
}
}
// ---------------------------------------------------------------------------
// Private: CacheConfig
// ---------------------------------------------------------------------------
bool UDPSServer::CacheConfig(const uint8 *payload, uint32 payloadSize) {
if (cachedConfig != NULL_PTR(uint8 *)) {
delete[] cachedConfig;
cachedConfig = NULL_PTR(uint8 *);
cachedConfigSize = 0u;
}
if ((payload == NULL_PTR(const uint8 *)) || (payloadSize == 0u)) {
return true;
}
cachedConfig = new uint8[payloadSize];
if (cachedConfig == NULL_PTR(uint8 *)) {
REPORT_ERROR_STATIC(ErrorManagement::FatalError,
"UDPSServer: Failed to allocate cached CONFIG buffer.");
return false;
}
(void)MemoryOperationsHelper::Copy(cachedConfig, payload, payloadSize);
cachedConfigSize = payloadSize;
return true;
}
} // namespace MARTe