TransportManager is responsible for creating publishers and subscribers and for connecting them together. The actual byte-level delivery is handled by interchangeable transport plugins.
Architecture overview
Every Unit has oneTransportManager. The transport manager maintains:
- An
InprocTransportinstance — always present, handles same-process delivery. - A map of named
Transportplugins — typically justTcpTransport, registered as"tcp".
Advertise<T>(), the transport manager creates:
- An
InprocPublisher<T>(for same-process subscribers). - One
TransportPublisherper registered transport plugin (for cross-process subscribers).
Subscribe<T>(), the transport manager creates:
- An
InprocSubscriber<T>(to receive messages from same-process publishers). - One
TransportSubscriberper registered transport plugin (to receive messages from cross-process publishers).
PublisherInfo from each unit and distributes NetworkInfo to all subscribers. When a subscriber learns about a new publisher, it calls HandlePublisherInfo() on the appropriate TransportSubscriber, which establishes the connection.
TransportManager
TransportManager::Update() is called by Unit::Update() on every iteration. It polls each registered transport (for example, TcpTransport::Update() checks for new subscriber connections) and regenerates the publisher summary sent to the coordinator.
Inproc transport (zero-copy)
The inproc transport passesstd::shared_ptr<const T_MSG> directly between publishers and subscribers in the same process. There is no serialization, no copy, no queue, and no system call.
InprocConnector<T>
Each message type has a static InprocConnector<T> instance. It holds a map from topic name to a list of weak pointers to InprocSubscriber<T> instances. Expired weak pointers are cleaned up on the next publish.
HasSubscribersFast() lets Publisher<T>::Publish() skip serialization when no network subscribers exist and there are also no inproc subscribers — for example, during early startup before any other unit has subscribed.
Alternate inproc types
A publisher can carry an alternate inproc type alongside the primary message type. This allows a publisher to deliver a compact sensor-native format directly to co-located subscribers while converting to a standard format for network consumers. TheT_CONVERTABLE_INPROC template parameter on Publisher enables this.
TCP transport
The TCP transport (TcpTransport) handles cross-process delivery. It serializes messages into MessagePacket byte buffers and sends them over TCP connections.
Publisher side: TcpPublisher
TcpPublisher holds a TcpListenSocket. On each Update() call, CheckForNewSubscriptions() accepts pending connections and creates a TcpSender for each one. Each TcpSender runs a dedicated background thread that drains an outbound queue.
When the queue is full (controlled by SetMaxQueueSize()), older messages are dropped. A queue size of 0 means unlimited.
Subscriber side: TcpSubscriber
TcpSubscriber holds a map of TcpReceiver instances keyed by (address, port). When the coordinator delivers a NetworkInfo update, SubscriberBase::HandlePublisherInfo() calls Connect() on the matching TcpSubscriber, which establishes a TCP connection to the publisher.
Inbound data is received via a shared Epoll instance and dispatched to a ThreadPool for deserialization. The deserialized MessagePacket is handed to the type-erased subscriber callback, which then invokes the user’s typed callback.
Transport plugin registration
TcpTransport implements the Transport interface:
CreateStandardTransportManager() (called by Unit::CreateTransportManager()) does this automatically and also enables the inproc transport.
On-demand serialization
Serialization only happens when there is at least one network subscriber. This is enforced insidePublisher<T_MSG>::Publish():
RecorderInterface is attached to the transport manager, messages are written to disk only if the recorder accepted the topic during Advertise(). Raw struct types (using RawSerializer) are never recorded.
Controlling transports per input
In the unit YAML, each input can restrict which transports it accepts:allow_transports is a whitelist. deny_transports is a blacklist. If neither is specified, the input accepts delivery from any available transport.
QoS: queue depth
The unit YAML does not yet have a top-level QoS block, but queue depth is configurable in two places:- Publisher side: call
Publisher::SetMaxQueueSize(n)afterAdvertise(). When the outbound TCP send queue exceedsnpackets, older packets are dropped. - Subscriber side: pass
queue_depthtoSingleThreadedUnit::Subscribe(). This controls the depth of the per-subscriberSubscriberQueuethat buffers callbacks waiting to be executed on the main thread.
queue_depth of 0 means the queue is unbounded.
Schema management
TransportManager includes a SchemaManager that tracks the message schemas for all advertised topics. When a new message type is first advertised, SchemaManager::RegisterType<T_MSG, T_Serializer>() serializes the schema (for example, the protobuf FileDescriptorProto) and queues it to be sent to the coordinator. The coordinator then makes schemas available to tools and recorders.
Transport summary
| Transport | Delivery | Serialization | Cross-process | Use case |
|---|---|---|---|---|
| Inproc | std::shared_ptr | None | No | Same-process, zero-copy |
| TCP | Byte stream | On demand | Yes | Cross-process, cross-host |
Next steps
Pub-sub messaging
How publishers and subscribers use transport under the hood
Units
How a Unit creates and owns its TransportManager
Synchronizers
How the transport layer feeds messages into synchronizers
Recording
How messages are captured to MCAP files via the recorder interface