Add TCPStreamer/TCPStreamerClient (TCP transport behind the UDPSServerI/UDPSClientI seam), an optional single in-process TCPStreamService serving many streams over worker threads, multiplexed SUBSCRIBE (comma list, stream index in fragmentIdx), and TCPStreamJoin, which merges several streams into one sample-and-hold stream paced by a master. StreamHub takes a comma list of streams over one connection. Includes GTests, E2E scenarios s58-s73 and docs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
161 lines
5.5 KiB
C++
161 lines
5.5 KiB
C++
#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_
|