#ifndef TCPS_SERVER_H_ #define TCPS_SERVER_H_ /** * @file TCPSServer.h * @brief TCP transport for the UDPS wire format (producer side). * * TCPSServer carries the normal UDPS framing (17-byte header + CONFIG / DATA * payload, totalFragments always 1) over TCP byte streams. It implements * UDPSServerI so UDPStreamer can publish through it unchanged. * * Threading model: NOT thread-safe, exactly like UDPSServer. Every call must * come from the owner's sender thread. There is no thread per client: sockets * are non-blocking and everything is driven from ServiceClients() / SendData(). * * Slow consumers: every client owns a bounded queue of ClientQueueDepth DATA * frames. When it is full the oldest queued DATA frame that is not already * (partially) on the wire is discarded and counted; other clients and the * producer are never affected. A client that makes no progress for * ClientStallTimeout seconds with data queued is disconnected. * * Ordering: a client always sees CONFIG before any DATA. A CONFIG is never * dropped. When a new CONFIG is published, the frames queued behind the one * currently on the wire are stale (encoded for the old layout) and are * discarded, so DATA is never decoded against the wrong CONFIG. */ #include "StructuredDataI.h" #include "UDPSProtocol.h" #include "UDPSTransportI.h" namespace MARTe { class TCPSServer : public UDPSServerI { public: /** Default TCP listen port. */ static const uint32 TCPS_DEFAULT_PORT = 44500u; /** Default maximum payload bytes per frame. */ static const uint32 TCPS_DEFAULT_MAX_PAYLOAD = 60000u; /** Hard ceiling on a frame payload (matches UDPSClient's packet limit). */ static const uint32 TCPS_MAX_FRAME_PAYLOAD = 1048576u; /** Default simultaneous clients. */ static const uint32 TCPS_DEFAULT_MAX_CLIENTS = 16u; /** Upper bound accepted for MaxClients. */ static const uint32 TCPS_MAX_CLIENTS_LIMIT = 1024u; /** Default DATA frames queued per client. */ static const uint32 TCPS_DEFAULT_QUEUE_DEPTH = 8u; /** Upper bound accepted for ClientQueueDepth. */ static const uint32 TCPS_QUEUE_DEPTH_LIMIT = 4096u; /** Default stall timeout (seconds); 0 disables. */ static const uint32 TCPS_DEFAULT_STALL_TIMEOUT_S = 5u; TCPSServer(); virtual ~TCPSServer(); /** * @brief Read configuration. * * Keys (all optional): * - Port (uint32) TCP listen port, default 44500. 0 picks an * ephemeral port (see GetPort() after Start()). * - MaxPayloadSize (uint32) 1..1 MiB, default 60000. Sizes Accumulate * batches in the owner; frames larger than 1 MiB are * refused by SendData(). * - MaxClients (uint32) 1..1024, default 16. Extra connections * are accepted and closed immediately. * - ClientQueueDepth (uint32) 1..4096, default 8. * - ClientStallTimeout (uint32) Seconds, default 5; 0 disables. * - SendBufferSize (uint32) SO_SNDBUF for client sockets; 0 (default) * keeps the OS default. */ virtual bool Initialise(StructuredDataI &data); /** Open the listening socket (idempotent). */ virtual bool Start(); /** Close every socket and free all buffers (idempotent). */ virtual bool Stop(); /** * Accept new clients, drop closed ones, flush queues, evict stalled * clients. Never blocks. */ virtual void ServiceClients(); /** * Cache the CONFIG and queue it on every connected client. New clients * receive the cached CONFIG on accept. */ virtual bool SendConfig(const uint8 *payload, uint32 payloadSize); /** * Queue a DATA frame on every connected client and try to flush. * @return false only if the frame is larger than TCPS_MAX_FRAME_PAYLOAD. */ virtual bool SendData(uint32 counter, const uint8 *payload, uint32 payloadSize); virtual bool HasClients() const; virtual bool IsMulticast() const; /** @return the bound port once started, otherwise the configured port. */ virtual uint16 GetPort() const; /** @return number of connected clients. */ uint32 GetClientCount() const; /** @return DATA frames discarded (all clients, all time) because a client queue was full. */ uint64 GetClientDrops() const; /** @return clients disconnected by the stall timeout (all time). */ uint32 GetStallEvictions() const; uint32 GetMaxPayloadSize() const; private: struct Slot; struct Client; bool CacheConfig(const uint8 *payload, uint32 payloadSize); void AcceptPending(); bool Enqueue(Client &c, bool isConfig, const uint8 *frame, uint32 frameSize); bool Flush(Client &c); bool PeerAlive(Client &c); void Evict(Client &c); bool BuildFrame(uint8 type, uint32 counter, const uint8 *payload, uint32 payloadSize); void ReportDrops(); uint32 port; uint16 boundPort; uint32 maxPayloadSize; uint32 maxClients; uint32 queueDepth; uint32 stallTimeoutS; uint32 sendBufferSize; int listenFd; bool started; Client *clients; uint32 numClients; uint8 *cachedConfig; uint32 cachedConfigSize; uint32 configCounter; uint8 *frameBuf; ///< scratch: header + payload built once per call uint32 frameCapacity; uint32 frameSize; uint64 stallTicks; uint64 totalDrops; uint64 loggedDrops; uint64 lastDropLogTicks; uint32 stallEvictions; }; } // namespace MARTe #endif // TCPS_SERVER_H_