diff --git a/deps/udpsclient/udps_client.c b/deps/udpsclient/udps_client.c index 8372f2a..7dea12e 100644 --- a/deps/udpsclient/udps_client.c +++ b/deps/udpsclient/udps_client.c @@ -198,6 +198,7 @@ struct udps_client { size_t vals_cap; uint8_t *rxbuf; + size_t rxbuf_cap; /**< bytes allocated for rxbuf */ udps_held_t held[UDPS_REORDER_SLOTS]; size_t held_bytes; /**< payload bytes currently held */ @@ -1256,7 +1257,7 @@ static int read_tcp_frame(udps_client_t *c) { if (udps_parse_header(c->rxbuf, UDPS_HEADER_SIZE, &h) != 0) { return -1; /* desynchronised: the stream cannot be resynced */ } - if (h.payload_bytes > (uint32_t)(UDPS_RX_BUF_BYTES - UDPS_HEADER_SIZE)) { + if (h.payload_bytes > c->rxbuf_cap - UDPS_HEADER_SIZE) { return -1; } if (h.payload_bytes > 0u && @@ -1350,7 +1351,13 @@ udps_client_t *udps_client_create(const udps_client_config_t *cfg) { return NULL; } - c->rxbuf = (uint8_t *)malloc(UDPS_RX_BUF_BYTES); + /* A UDP datagram is at most UDPS_MAX_DATAGRAM_BYTES; a TCP frame is not fragmented + * and may be as large as max_packet_bytes (MARTe's TCPStreamer allows 1 MiB). */ + c->rxbuf_cap = UDPS_RX_BUF_BYTES; + if (c->cfg.use_tcp && (size_t)c->cfg.max_packet_bytes + UDPS_HEADER_SIZE > c->rxbuf_cap) { + c->rxbuf_cap = (size_t)c->cfg.max_packet_bytes + UDPS_HEADER_SIZE; + } + c->rxbuf = (uint8_t *)malloc(c->rxbuf_cap); if (c->rxbuf == NULL) { free(c); return NULL; diff --git a/docs/claude/build-and-run.md b/docs/claude/build-and-run.md index f3971b0..edabfc8 100644 --- a/docs/claude/build-and-run.md +++ b/docs/claude/build-and-run.md @@ -276,8 +276,10 @@ is intentional. Six things were changed: `read_tcp_frame()` for the frames, the reorder window forced to 0 (TCP is ordered, so a counter gap is a server-side drop and is reported at once), multicast forced off, no DISCONNECT frame on close (a write to a closed peer would raise SIGPIPE), no keepalive ACK - and no datagram tap. A frame must fit `UDPS_MAX_DATAGRAM_BYTES` (the server's - `MaxPayloadSize` default of 60000 does). A stream name over 255 bytes is refused at create. + and no datagram tap. The receive buffer is `max_packet_bytes + header` (1 MiB by default, + matching the server's frame ceiling) in TCP mode, since a TCP frame is never fragmented and + a MARTe cycle of large arrays is one frame; it is `UDPS_MAX_DATAGRAM_BYTES` only for UDP. A + stream name over 255 bytes is refused at create. `deps/plotter` is upstream's own repository and this app's figure support was added there rather than worked around here: `Figure::set_min_y_gutter()` (a floor under the diff --git a/docs/claude/probe-readings.md b/docs/claude/probe-readings.md index b12c956..638f04a 100644 --- a/docs/claude/probe-readings.md +++ b/docs/claude/probe-readings.md @@ -2316,6 +2316,10 @@ run as `--source tcps --source-opt port=44500` against `tools/mock_tcpstreamer.p `verification.md`. A service run (`--stream fast` on both sides) reads the same two lines; the mock logs `SUBSCRIBE 'fast'`. With no mock the stream line reads `connected=0 signals=0 names= frames=0 ok=0`: a reading, not a failure (the retries are quiet on stderr). +A mock run with `--rate 1000000 --batch 20000` sends frames of about 560 KB; the stream line must still +read `frames>0 ok=1`. `frames=0` with the mock logging a new client every few hundred ms is the signature +of a receive buffer smaller than the server's frame (the client drops the connection, reconnects, and +gets CONFIG again, never DATA). - `stream: ok=1`: connected, the five names the mock sends (its `time` master channel is a signal of its own, so the list is `time,sine,square,ramp,noise`, not four), and frames counted. `connected=0` with a mock running is