diff --git a/ntcore/doc/domain-sockets.adoc b/ntcore/doc/domain-sockets.adoc new file mode 100644 index 00000000000..d2ffcc10b19 --- /dev/null +++ b/ntcore/doc/domain-sockets.adoc @@ -0,0 +1,63 @@ += DomainSockets Protocol Specification, Version 1.0 +WPILib Developers +Protocol Revision 1.0, 9/6/2024 +:toc: +:toc-placement: preamble +:sectanchors: + +An extensible transport inspired by WebSockets but designed for minimal overhead on a local machine domain socket or pipe connection. + +[[motivation]] +== Motivation + +WebSockets provides an extensible and flexible transport over TCP, with HTTP handshaking for web server compatibility. However, it is high overhead for machine-local connections, as it requires shading of one direction of communication, has complex support for fragmented frames, and has a fairly complex layered handshake for HTTP compatibility. None of these things are required for local domain or pipe connections. However, the subprotocol negotiation and extensibility are worthwhile features to preserve in that environment so that servers can offer multiple services or protocol versions on a single domain socket endpoint, and clients can similarly negotiate based on what services they need. + +[[frames]] +== Frames + +All transported data in both directions is packaged into tagged frames. Each frame consists of a 5-byte header followed immediately by up to 2^32-1 bytes of payload data. The first four bytes of the header is the length of the payload (e.g. excluding the header), in little endian order. The 5th byte of the header is the tag byte. The following tag bytes are defined by this specification. Subprotocols may define additional tag values if the WebSockets-style binary and text tags are insufficient. + +[cols="1,2,6",options="header"] +|=== +|Tag Value +|Description +|Payload + +|0 +|Hello +|`HELLO 1.0`, followed by an arbitrary UTF-8 string + +|1 +|Error +|Error message (UTF-8 string) + +|2 +|Subprotocol Request +|List of NUL-terminated subprotocol UTF-8 strings (in priority order) + +|3 +|Subprotocol Grant +|Subprotocol (UTF-8 string) + +|0x80 +|Binary +|Binary data (content as defined by subprotocol) + +|0x81 +|Text +|UTF-8 text data (content as defined by subprotocol) +|=== + +[[opening-handshake]] +== Opening Handshake + +The opening handshake is a simplified version of the WebSockets opening handshake. The general sequence is as follows: + +* The server sends an Hello frame +* The client responds with an Subprotocol Request frame containing the subprotocols the client desires to use, in priority order +* The server responds with an Subprotocol Grant frame containing the first requested subprotocol that the server supports, or if no requested protocol is available, an Error frame + +[[error-messages]] +== Error Frame Handling + +Either client or server can send an Error frame at any time. The pipe/socket should be closed after either sending or receiving an Error frame. diff --git a/ntcore/doc/networktables4.adoc b/ntcore/doc/networktables4.adoc index 318afac7f85..ef6f7bd86ea 100644 --- a/ntcore/doc/networktables4.adoc +++ b/ntcore/doc/networktables4.adoc @@ -54,6 +54,9 @@ Using WebSockets (RFC 6455) as the transport layer along with JSON and MessagePa [[networktables3]] * <> +[[DomainSockets]] +* <> + [[cristians-algorithm]] * Cristian's algorithm, https://en.wikipedia.org/wiki/Cristian%27s_algorithm @@ -806,6 +809,19 @@ An example double value update would be 17 bytes: For comparison, a double value update in NT 3.0 is 14 bytes (and does not contain a timestamp). +[[domain-socket]] +== Domain Socket Transport + +For domain socket or pipe transport between processes running on a single machine, a simplified wire protocol (see <>) is used instead of paying for the overhead of a full WebSockets implementation. + +The subprotocols used in DomainSockets are identical to those used in WebSockets, as are the text and binary frame payload contents. + +No time synchronization or aliveness checking is performed on domain sockets (the DomainSockets protocol does not support ping or pong messages). All wire timestamps should use the system monotonic clock (on Linux systems, `clock_gettime(CLOCK_MONOTONIC)` or equivalent) as the time base. If the server time base is different, the server is responsible for adjusting transmitted and received timestamps to its internal time base--note this is opposite of WebSockets transport, where server time is always used for wire transport and the client must adjust the time. + +Unlike WebSockets transport, clients and servers should in general not need to worry about rate limiting transmissions or efficiently packing data into frames. However, on most systems, it is more efficient to use fewer larger writes than many small ones, so implementations should perform appropriate buffering to minimize the number of small writes. + +This specification does not define what filesystem location or pipe name servers should use. + [[drawbacks]] == Drawbacks