docs/JAMULUS_PROTOCOL.md: message reference, directory flows, small fixes - #3794
docs/JAMULUS_PROTOCOL.md: message reference, directory flows, small fixes#3794mcfnord wants to merge 2 commits into
Conversation
softins
left a comment
There was a problem hiding this comment.
This looks good. Just a few comments and suggested changes.
|
Please squash the commits here. |
…ixes - Explain how receivers classify datagrams (protocol frame vs audio) and specify the CRC and the acknowledge/retransmit mechanism - Add a complete message-ID reference for connection-based and connectionless messages - Add a section on directory registration, server lists and NAT hole punching (registration refresh/timeout intervals, CLM message flows) - Fix message names to match protocol.h (REQ_CHANNEL_INFOS, CHANNEL_INFOS, REQ_CONN_CLIENTS_LIST) - Apply style guide capitalisation (Client, Server, Directory, Channel, Jitter Buffer) per https://jamulus.io/contribute/Style-and-Tone - Move SPECIAL_SPLIT_MESSAGE (2001) out of the connection-based table into its own paragraph explaining it's a transport container, not a message type - Document the raw (uncompressed PCM) audio option alongside OPUS/OPUS64: how it's advertised (FS_RAW_AUDIO in CLM_SERVER_FEATURES), disabled (--noraw), and how the Server tells it apart from OPUS (packet-size comparison, no distinct message type) Co-Authored-By: Peter L Jones <pljones@users.noreply.github.com> Co-Authored-By: Tony Mountifield <tony@mountifield.org> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
5d057a5 to
2386d21
Compare
|
|
||
| Jamulus uses connectionless UDP packets to communicate between the client and server, and additionally for directory server registration. The `src/protocol.cpp` file contains much of the details of the packets themselves, whereas this document is intended to form a higher-level view of the protocol interactions. | ||
| Some of the messages need to be acknowledged, some do not. If a message ID is less than 1000, the message must be acknowledged in under `SEND_MESS_TIMEOUT_MS` ms. | ||
| Jamulus uses connectionless UDP packets to communicate between the Client and Server, and additionally for registration with a Directory. The `src/protocol.cpp` file contains much of the details of the packets themselves, whereas this document is intended to form a higher-level view of the protocol interactions. |
There was a problem hiding this comment.
(Nit: "connectionless UDP packets" is a horrible phrase... UDP is a connectionless IP protocol, so "connectionless" is redundant. It's also a packet-based protocol (inherently), so "packets" is redundant. But there you are...)
There was a problem hiding this comment.
MY LLM WROTE:
Agreed on both counts — dropped to just "Jamulus uses UDP to communicate...". Fixed in be4b4e7.
| Jamulus uses connectionless UDP packets to communicate between the client and server, and additionally for directory server registration. The `src/protocol.cpp` file contains much of the details of the packets themselves, whereas this document is intended to form a higher-level view of the protocol interactions. | ||
| Some of the messages need to be acknowledged, some do not. If a message ID is less than 1000, the message must be acknowledged in under `SEND_MESS_TIMEOUT_MS` ms. | ||
| Jamulus uses connectionless UDP packets to communicate between the Client and Server, and additionally for registration with a Directory. The `src/protocol.cpp` file contains much of the details of the packets themselves, whereas this document is intended to form a higher-level view of the protocol interactions. | ||
| Messages with an ID below 1000 are connection-based: each one is acknowledged by an `ACKN (1)` message carrying the same sequence counter, and the sender retransmits it every `SEND_MESS_TIMEOUT_MS` ms until the acknowledgement arrives. Messages with an ID from 1000 to 1999 (`CLM_*`) are connectionless: they work without an established audio connection and are never acknowledged. |
There was a problem hiding this comment.
And then it has the nerve to go from saying "Jamulus uses connectionless..." to "connection-based"... and "... an established audio connection..." without any explanation. I think a little scene-setting is needed.
There was a problem hiding this comment.
MY LLM WROTE:
Fair — it was using "connection-based" as a classification before establishing what a connection is here. Added a short paragraph between the two: UDP gives no delivery guarantee and no connection, so Jamulus layers its own session semantics on top; a Client and Server count as connected once the Client is sending valid audio packets and the Server has assigned it a channel. The two message-class names now have something to refer to, and I say explicitly they are named relative to that session rather than to anything at the transport level.
| Jamulus uses connectionless UDP packets to communicate between the client and server, and additionally for directory server registration. The `src/protocol.cpp` file contains much of the details of the packets themselves, whereas this document is intended to form a higher-level view of the protocol interactions. | ||
| Some of the messages need to be acknowledged, some do not. If a message ID is less than 1000, the message must be acknowledged in under `SEND_MESS_TIMEOUT_MS` ms. | ||
| Jamulus uses connectionless UDP packets to communicate between the Client and Server, and additionally for registration with a Directory. The `src/protocol.cpp` file contains much of the details of the packets themselves, whereas this document is intended to form a higher-level view of the protocol interactions. | ||
| Messages with an ID below 1000 are connection-based: each one is acknowledged by an `ACKN (1)` message carrying the same sequence counter, and the sender retransmits it every `SEND_MESS_TIMEOUT_MS` ms until the acknowledgement arrives. Messages with an ID from 1000 to 1999 (`CLM_*`) are connectionless: they work without an established audio connection and are never acknowledged. |
There was a problem hiding this comment.
"the sender retransmits it every SEND_MESS_TIMEOUT_MS ms until the acknowledgement arrives"
Forever? I know it's not a full spec but given it's got this partly stated, it might as well put the upper bound in.
There was a problem hiding this comment.
MY LLM WROTE:
I went to look up the bound and there is not one — so the honest answer to "Forever?" is very nearly yes, and the sentence was hiding that rather than just being incomplete.
OnTimerSendMess() (protocol.h:332) is just SendMessage(), and SendMessage() restarts TimerSendMess unconditionally whenever the queue is non-empty (protocol.cpp:552-566). No retry counter anywhere. Retransmission stops on exactly two events: a matching ACKN pops the queue (protocol.cpp:711-716), or CProtocol::Reset() clears it (protocol.cpp:516) — called from channel.cpp at 167, 394 and 678, i.e. channel disabled, protocol disabled while sending, and channel time-out/disconnect.
So the real bound is the channel lifetime, not a retry cap. Documented it that way rather than inventing a limit, and added the concrete 400 ms. If you would rather this were a bounded retry, that is a code change and I am happy to raise it separately.
…, set the scene for connection-based vs connectionless, state the retransmit bound - "connectionless UDP packets" was doubly redundant: UDP is inherently both connectionless and packet-based. - The text used "connection-based" and "established audio connection" right after calling the transport connectionless, with no explanation. Added a sentence defining what counts as connected in Jamulus terms, so the two message-class names have something to refer to. - The retransmit sentence stopped at "until the acknowledgement arrives". There is in fact no retry counter: OnTimerSendMess() re-sends and SendMessage() restarts the timer while the queue is non-empty, so the loop is bounded only by an ACKN popping the queue or CProtocol::Reset() clearing it (disconnect, time-out, protocol disabled). Stated that, plus the concrete 400 ms value. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
|
||
| UDP offers no delivery guarantee and no notion of a connection, so Jamulus layers its own session and reliability semantics on top of it. A Client and Server count as *connected* once the Client is sending valid audio packets and the Server has assigned it a channel. The two message classes below are named relative to that session, not to anything at the transport level. | ||
|
|
||
| Messages with an ID below 1000 are connection-based: they apply to an established session, and each is acknowledged by an `ACKN (1)` message carrying the same sequence counter. Until that acknowledgement arrives, the sender retransmits the message every `SEND_MESS_TIMEOUT_MS` (400) ms. The protocol layer sets no retry limit: retransmission ends when the message is acknowledged, or when the channel clears the send queue via `CProtocol::Reset()` — on disconnect, on channel time-out, or when the protocol is disabled. |
There was a problem hiding this comment.
Mm, leaves me asking how big an exploit surface that is -- I don't think it's that large as there aren't very many protocol messages that a malicious client could send to a server after initiating a connection that require a response that could have the ACKN unsent. But a spoofable sender address would open this.
One file. Fills the main gaps in the protocol doc, verified against
src/protocol.cpp/src/socket.cpp/src/serverlist.cppon current main:CSocket::ProcessPacket()), the CRC parameters, and how acknowledge/retransmit actually works — previously the doc said only "must be acknowledged".protocol.h.CLM_SEND_EMPTY_MESSAGE/CLM_EMPTY_MESSAGENAT hole-punch flow — previously undocumented here despite being a third of the connectionless messages.protocol.h(REQ_CHANNEL_INFOS,CHANNEL_INFOS,REQ_CONN_CLIENTS_LIST).CHANGELOG: SKIP
🤖 Generated with Claude Code