#ifndef UDPS_SERVER_H_ #define UDPS_SERVER_H_ /** * @file UDPSServer.h * @brief Multi-client UDPS session-management and fragmented-send helper. * * UDPSServer is a plain C++ helper (not a MARTe2 Object) that encapsulates: * - Unicast mode: up to UDPS_SERVER_MAX_UNICAST_CLIENTS simultaneous UDP clients * that self-register via CONNECT datagrams. * - Multicast mode: TCP listener for CONNECT + CONFIG delivery; UDP multicast * socket for DATA datagrams. * - Static clients: pre-configured destinations that never need to send CONNECT * (backward-compat for push-to-fixed-IP use cases). * * Threading model: UDPSServer is NOT thread-safe. All public methods must be * called from the same thread (the owner's Execute() thread). */ #include "BasicTCPSocket.h" #include "BasicUDPSocket.h" #include "InternetHost.h" #include "StreamString.h" #include "StructuredDataI.h" #include "UDPSProtocol.h" namespace MARTe { /** * @brief Multi-client / multicast UDPS server helper. * * Usage pattern: * @code * UDPSServer server; * server.Initialise(data); // in component Initialise() * server.Start(); // in PrepareNextState() / Start() * // inside Execute() loop: * server.ServiceClients(); // poll CONNECT / DISCONNECT / ACK * if (server.HasClients()) { * server.SendConfig(cfgBuf, cfgSize); // when config changes * server.SendData(counter, dataBuf, dataSize); * } * server.Stop(); // in StopCurrentStateExecution() * @endcode */ class UDPSServer { public: /** Maximum number of simultaneous unicast clients. */ static const uint32 UDPS_SERVER_MAX_UNICAST_CLIENTS = 16u; /** Maximum number of simultaneous multicast TCP control connections. */ static const uint32 UDPS_SERVER_MAX_TCP_CLIENTS = 8u; /** Default stale-client eviction timeout (seconds). */ static const uint32 UDPS_SERVER_DEFAULT_CLIENT_TIMEOUT_S = 30u; /** Default maximum UDP payload size (bytes, EXCLUDING the 17-byte header). */ static const uint32 UDPS_SERVER_DEFAULT_MAX_PAYLOAD = 1400u; UDPSServer(); ~UDPSServer(); /** * @brief Read configuration from a StructuredDataI node. * * Expected keys (all optional except Port for unicast): * - Port (uint16) UDP port to bind (unicast) or TCP listen port (multicast). * - MulticastGroup (char*) IPv4 multicast address; presence enables multicast mode. * - DataPort (uint16) UDP port used for DATA datagrams in multicast mode * (defaults to Port+1 if omitted). * - MaxPayloadSize (uint32) Max payload bytes per datagram, excluding header. * Defaults to UDPS_SERVER_DEFAULT_MAX_PAYLOAD. * - ClientTimeout (uint32) Seconds before a silent unicast client is evicted. * Defaults to UDPS_SERVER_DEFAULT_CLIENT_TIMEOUT_S. * Set to 0 to disable timeout-based eviction. */ bool Initialise(StructuredDataI &data); /** * @brief Open sockets and begin accepting clients. * @return true on success. */ bool Start(); /** * @brief Close all sockets and evict all clients. Frees cached CONFIG. */ bool Stop(); /** * @brief Non-blocking poll for incoming CONNECT / DISCONNECT / ACK messages. * * Must be called regularly from the owner's Execute() main loop. * In multicast mode also polls the TCP listener for new connections. */ void ServiceClients(); /** * @brief Send a CONFIG payload to all active clients. * * Also stores a copy as the cached CONFIG, which is automatically sent to * any new CONNECT client. * * @param payload Pointer to fully-assembled CONFIG payload (caller-owned). * @param payloadSize Byte count of payload. * @return true if at least one send succeeded (or no clients are connected). */ bool SendConfig(const uint8 *payload, uint32 payloadSize); /** * @brief Send a DATA payload to all active clients. * * @param counter Per-update sequence counter (same for all fragments). * @param payload Pointer to fully-assembled DATA payload (caller-owned). * @param payloadSize Byte count of payload. * @return true if all sends succeeded. */ bool SendData(uint32 counter, const uint8 *payload, uint32 payloadSize); /** * @brief Add a static unicast destination that never needs to send CONNECT. * * The entry is never evicted by the timeout mechanism. Duplicate * (ip, port) pairs are silently ignored. * * @param ip Destination IPv4 address string. * @param port Destination UDP port. * @return true if the client was added (or was already present). */ bool AddStaticClient(const char8 *ip, uint16 port); /** @return Number of currently active clients (unicast + TCP). */ uint32 GetClientCount() const; /** @return true if at least one client is active. */ bool HasClients() const; /** @return true if server is in multicast mode. */ bool IsMulticast() const; /** @return Configured port number. */ uint16 GetPort() const; /** @return Configured maximum payload size (excluding header). */ uint32 GetMaxPayloadSize() const; private: // ------------------------------------------------------------------------- // Per-client state (unicast) // ------------------------------------------------------------------------- struct UDPSClientEntry { char8 ipAddr[64]; ///< Client IPv4 address string uint16 clientPort; ///< Client source port uint64 lastSeenTicks; ///< HighResolutionTimer ticks at last contact bool active; ///< Slot is in use bool isStatic; ///< Never evicted by timeout; no CONNECT required }; // ------------------------------------------------------------------------- // Private helpers // ------------------------------------------------------------------------- /** * @brief Send a fragmented UDPS packet via a UDP socket. * * @param sock Socket to write through. * @param dest If non-NULL, SetDestination is called before each write * (unicast unconnected socket). If NULL, assumes the socket * is already connected (multicast dataSocket). * @param type UDPS_TYPE_DATA or UDPS_TYPE_CONFIG. * @param counter Sequence counter. * @param payload Payload bytes. * @param payloadSize Payload byte count. * @return true if all fragments were written without error. */ bool SendFragmentedUDP(BasicUDPSocket &sock, InternetHost *dest, uint8 type, uint32 counter, const uint8 *payload, uint32 payloadSize); /** * @brief Send a fragmented UDPS packet via a TCP socket. * * Used in multicast mode to push CONFIG to each TCP control client. */ bool SendFragmentedTCP(BasicTCPSocket &sock, uint8 type, uint32 counter, const uint8 *payload, uint32 payloadSize); /** Evict unicast clients that have been silent longer than clientTimeoutTicks. */ void EvictStaleUnicastClients(); /** Handle a CONNECT datagram received on serverSocket. */ void HandleUnicastConnect(const InternetHost &src); /** Handle a DISCONNECT datagram received on serverSocket. */ void HandleUnicastDisconnect(const InternetHost &src); /** Handle an ACK datagram received on serverSocket. */ void HandleUnicastAck(const InternetHost &src); /** * @brief Find a unicast client slot by IP + port. * @return Slot index, or UDPS_SERVER_MAX_UNICAST_CLIENTS if not found. */ uint32 FindUnicastClient(const char8 *ip, uint16 port) const; /** * @brief Find the oldest (lowest lastSeenTicks) non-static unicast client. * @return Slot index, or UDPS_SERVER_MAX_UNICAST_CLIENTS if none. */ uint32 FindOldestUnicastClient() const; /** Close slot @p idx and mark it inactive. */ void EvictUnicastClient(uint32 idx); /** Accept a new multicast TCP connection and send cached CONFIG. */ void HandleMulticastTCPConnect(BasicTCPSocket *newClient, uint32 idx); /** Close TCP client slot @p idx. */ void EvictTCPClient(uint32 idx); /** Cache a copy of a CONFIG payload for future CONNECT clients. */ bool CacheConfig(const uint8 *payload, uint32 payloadSize); // ------------------------------------------------------------------------- // Configuration // ------------------------------------------------------------------------- uint16 port; uint32 maxPayloadSize; StreamString multicastGroup; uint16 dataPort; bool useMulticast; uint64 clientTimeoutTicks; ///< 0 = disabled // ------------------------------------------------------------------------- // Unicast state // ------------------------------------------------------------------------- UDPSClientEntry unicastClients[UDPS_SERVER_MAX_UNICAST_CLIENTS]; uint32 numUnicastClients; BasicUDPSocket serverSocket; ///< Bound to port; receives CONNECT/DISCONNECT/ACK BasicUDPSocket uniSendSocket; ///< Unconnected; SetDestination before each Write // ------------------------------------------------------------------------- // Multicast state // ------------------------------------------------------------------------- BasicTCPSocket tcpListener; BasicTCPSocket *tcpClients[UDPS_SERVER_MAX_TCP_CLIENTS]; uint32 numTCPClients; BasicUDPSocket dataSocket; ///< Connected to multicastGroup:dataPort // ------------------------------------------------------------------------- // Shared state // ------------------------------------------------------------------------- uint8 *cachedConfig; ///< Last SendConfig() payload (heap-allocated) uint32 cachedConfigSize; uint8 *sendBuf; ///< Pre-allocated fragment TX buffer uint32 sendBufCapacity; ///< UDPS_HEADER_SIZE + maxPayloadSize uint32 configCounter; ///< Sequence counter for CONFIG packets bool started; }; } // namespace MARTe #endif // UDPS_SERVER_H_