# TCPStreamService: joining streams (multiplex + merge) Date: 2026-11-06. Extends `2026-11-05-tcp-streamer-design.md` section 7. ## Goal Let one consumer take several service streams through a single, cheaper path. Two independent mechanisms were requested ("both"): 1. **Multiplexed connection**: one TCP socket carries N streams, each keeping its own CONFIG, rate and timeline. 2. **Merged stream**: the service builds one new stream whose CONFIG holds the signals of several source streams, emitted at the pace of a master source with sample-and-hold of the others. ## 1. Multiplexed connection ### Wire - `SUBSCRIBE` (type 5) payload becomes a comma separated list of stream names (`"fast,slow"`). One name is the old behaviour. Names may not contain `,`. Limits: 64 names, 4096 payload bytes, no duplicates. - The index of a stream is its position in the list. - Every server-to-client frame carries the stream index in `fragmentIdx` (`totalFragments` stays 1). Single-name connections always send 0, so existing clients are unchanged. - Any unknown name closes the whole connection (the client retries), exactly like the single-name case. ### Server - `TCPSConn` holds up to 64 stream attachments instead of one. - A stream's frame is built once; `Push` copies it into each subscriber's queue and stamps that connection's own index into `fragmentIdx`. - `TCPSFrameQueue` accounts per stream: `ClientQueueDepth` DATA frames *per stream*, drop-oldest of the same stream only, and a CONFIG only discards the stale frames of its own stream. A fast stream can therefore never evict a slow stream's frames. - `Flush` batches queued frames into one `writev()` (up to 16 frames), which is the optimisation for the many-small-frames case; it also applies to single-stream connections. - If a stream unregisters, every connection attached to it is closed. ### Client - `TCPSClient` accepts `StreamName = "a,b,c"`. It demultiplexes by `fragmentIdx` to one listener per stream and tracks the counter gap per stream. - `TCPSClient::GetStream(i)` returns a `UDPSClientI` facade for stream `i`. Facades reference-count `Start()`/`Stop()` of the shared connection, so existing consumers (StreamHub sessions) work unchanged. - `TCPStreamer` / `TCPStreamerClient` DataSources are single-stream: a comma in `StreamName` is rejected there. ### StreamHub - A source with `Transport="tcp"` and `StreamName="a,b,c"` creates one session per stream, all sharing one `TCPSClient`. Session labels are `