simple_doip
An ISO 13400-2 (DoIP) implementation with a no_std, zero-copy protocol core
and optional async client and server.
ARCHITECTURE.md describes how the crate is put together —
the feature-gated layering, the sans-io framing/decode seam, the error taxonomy,
the relationship to automotive-wire-codec, and the known issues and deferred
refactors a new maintainer should read before changing anything.
Scope and limitations
The protocol core — framing, message encode/decode — is golden-vector tested
against the on-wire format. The async client and server cover the common
case, within these bounds:
- No TLS. Connections are in the clear on
TCP_PORT(13400);TCP_TLS_PORT(3496) is defined by ISO 13400-2 and unused here. - The server serves one TCP connection at a time. A second client cannot connect while the first is being served.
- No unsolicited UDP vehicle announcement. A tester learns of an entity only
by asking, and identification is answered on the UDP path only, by
Server::run_udp_responderon a socket the caller binds and drives. - The client requires the peer to acknowledge before it responds. A
DiagnosticMessagethat arrives while the client is waiting for the acknowledgement is dropped, so a peer that answers first appears never to answer at all.
ARCHITECTURE.md §7 has the mechanics behind each of these,
and the deferred work around them.
None of this constrains bare-metal or single-client use.
Quickstart
The protocol core needs no allocator and no I/O: frame a byte buffer with
try_frame, then decode the payload with Payload::decode. This block is the
doctest on try_frame in src/framer.rs, wrapped in a
fn main returning Result so it compiles as pasted (the doctest gets the
same Ok(()) from a hidden line instead). That doctest is the canonical,
CI-tested version; if the two ever drift, trust the doctest.
use ;
Feature flags
The protocol core is no_std and zero-copy by default. Everything that pulls in
alloc, std, or an async runtime is opt-in via Cargo features:
| Feature | Enables | Depends on |
|---|---|---|
alloc |
Allocator-backed helpers | — |
std |
std-backed I/O and error traits |
alloc |
codec |
The tokio-util Encoder/Decoder for DoIP frames |
std, tokio, tokio-util, bytes |
client |
The async DoIP client | codec, async-trait, futures |
server |
The async DoIP server | codec, async-trait, futures |
default = [], so bare-metal / embedded targets should build with
default-features = false to keep the crate no_std with no allocator or
runtime dependencies.
alloc is what gates messages::OwnedMessage and its owned mirrors of the
borrowed message types — the practical reason to enable it is that you need a
message to outlive the buffer it was decoded from (e.g. to move it across a
queue or task boundary).
For development and testing, enable client and server:
Examples
-
bare_metal_codec— encode, frame, and decode with no allocator and no I/O. Runs standalone: -
echo_serverandsimple_client— a matched pair, not standalone. The server listens onTCP_PORT(13400); the client dials127.0.0.1:13400. Run each in its own terminal, server first:The client's
ConnectorSocketrefuses anyserver_addresswhose port is notTCP_PORT(13400), returningError::InvalidPort— pointing a client at a non-standard port requires your ownConnectorimplementation.echo_serveranswers a diagnostic message the way DoIP prescribes: first a positive acknowledgement carrying the received bytes back in its previous-message-data field, then the echo itself as a separateDiagnosticMessage. Both are written into theResponseWriterthe handler is given, which is the shape a real UDS response takes.echo_servercallsrun_server, so it serves TCP only and does not answer UDP discovery probes; seeServer::run_udp_responderfor that half.
Relationship to automotive-wire-codec
The wire-level primitives — byte-level Decode/Encode, Incomplete,
TrailingBytes — come from the automotive-wire-codec
crate. simple_doip::wire re-exports what consumers need so that using this
crate does not require a direct dependency on automotive-wire-codec.
Because those re-exported types appear in this crate's public API (e.g. in
MessageError's variants and in every message type's trait impls),
automotive-wire-codec's semver is effectively part of simple_doip's own
semver: a breaking change in that crate is a breaking change here too.
MSRV
The minimum supported Rust version is 1.88, bound by let-chain syntax used in this crate.
Contributing
Pull requests, bug reports and questions are welcome — see
CONTRIBUTING.md. Security reports go through GitHub's
private vulnerability reporting; see SECURITY.md.
License
Licensed under either of MIT or Apache-2.0 at your option.