TCP stream services: three callbacks in the raw-byte world
Game gateways, IoT intake, private protocols — they don't speak HTTP. The tcp/tcps class hands you a raw stream; everything else is yours to define.
Overview
23.1 Intro
HTTP's value is "agreeing what a request looks like"; TCP's value is "agreeing on nothing". So the host's duty for TCP shrinks to three things: establish the stream, hand over data, report the terminal state — protocol design stays entirely yours. TLS (tcps) merely plugs the encryption layer into the stream; the callback surface is identical.
23.2 The view & callbacks
/* XS_StreamConn: exactly one of tcp/tls non-null (under tcps never bypass tls to write tcp) */
static void EventOpen(XS_HostInfo* pHost, XS_StreamConn* pConn);
static void EventData(XS_HostInfo* pHost, XS_StreamConn* pConn, xnetbuf* pBuffer);
static void EventClose(XS_HostInfo* pHost, XS_StreamConn* pConn,
xnetresult iResult, const xerror* pError);
- EventOpen: connection established — the moment for a welcome banner and session context
- EventData: data arrived.
pBufferis a zero-copy buffer chain (borrowed in the callback): size viaxrtNetBufSize(pBuffer), data consumed through xrt's buffer iteration APIs — it is not one contiguous memory block; don't treat it as char* - EventClose: the terminal — iResult gives the network verdict, pError the details (may be NULL)
- Sending:
xrtNetStreamSend(pConn->tcp, ...)/xrtTlsStreamSend(pConn->tls, ...)by whichever side is non-null
23.3 Banner & echo
static void EventOpen(XS_HostInfo* pHost, XS_StreamConn* pConn)
{
if ( pConn->tcp != NULL )
(void)xrtNetStreamSend(pConn->tcp, "hello\n", 6);
/* tcps: test pConn->tls + xrtTlsStreamSend instead (check written) */
}
static void EventData(XS_HostInfo* pHost, XS_StreamConn* pConn, xnetbuf* pBuffer)
{
/* echo: send the received data back the way it came.
* real implementation: walk each block of the chain and Send per segment;
* size query: xrtNetBufSize(pBuffer) */
}
$ telnet 127.0.0.1 9000 hello (every line you type is echoed back verbatim)
What just happened: banner-on-connect is the convention of many text protocols (SMTP/FTP/IRC); echo is the stream service's "hello world". A real protocol (frame header + length + body) adds a state machine on top — ch. 33's XTP2 is a ready-made framing library.
23.4 Lifecycle & limits
- The host owns the connection lifecycle: Accept takeover → registry → destruct on Close — the application never Destroys a connection (unlike the custom class where everything is manual)
- SNI: under tcps, host certificates build the SNI table; each connection gets its certificate by SNI
- idle_timeout: idle connections close server-side on expiry (func_test has a measured 4.5s-close case)
- recv_limit: below the default read block, the read size shrinks in step (preventing config-induced generic failures)
- Session context: isomorphic with WS — your own registry (connection → context), insert on Open, remove on Close
23.5 Contract
- Borrowing: pConn/pBuffer/pError are valid inside the callback; consume chain data as frames, never as persistent storage.
- Direction: under tcps never bypass tls to write tcp — plaintext bytes read as TLS-record noise on the other side.
- Lifecycle: connections belong to the host; your session context belongs to you, cleaned in the Close terminal.
Common mistakes
- Symptom
- Garbled reads / out-of-bounds access.
- Cause
- It is a buffer chain (a struct with Head/Tail/Size fields) — consumption must go through xrt's buffer iteration APIs block by block; there is no single "whole data pointer".
- Symptom
- Sending/receiving on the connection pointer from another thread crashes or garbles ordering.
- Cause
- Active I/O outside callbacks belongs to the "takeover" mode (HTTP's XS_TAKEOVER / your own threads need care); in normal mode your logic is event-driven — done within the Open/Data/Close trio.
Exercises
Implement 23.3's service and verify with telnet.
Frame by \n across multiple EventData rounds (half-line buffering) and build a mini KV with GET/SET/QUIT commands.
Enable a TLS port with a certificate; the same callbacks serve both plaintext and encrypted endpoints.
Summary
| Point | Conclusion |
|---|---|
| Callback surface | the Open/Data/Close trio; protocol design is all yours |
| Data shape | xnetbuf zero-copy chain, borrowed per callback, consumed block-wise |
| tcps | one-of-two view; never bypass tls; SNI per-connection certificates |
| Lifecycle | host owns connections; you own context (registry + Close cleanup) |
| Limits | idle_timeout closes idle; recv_limit shrinks read blocks |
- xrt reference · net — buffer-chain iteration and stream send APIs
- xrt reference · tcp_server — listen modes and Accept semantics