Aller au contenu principal

TCP Transport

TCP-based transport for firewall-restricted environments, corporate networks, and cloud deployments without multicast support.

Not Interoperable

RTPS over TCP is not standardized and is HDDS-to-HDDS only. Each DDS vendor uses different framing. Use UDP for cross-vendor interop.

Overview

The TCP transport provides reliable, ordered delivery of RTPS messages over TCP connections. It is designed for environments where UDP is blocked, multicast is unavailable, or NAT traversal is required.

When to use TCP:

  • Corporate firewalls with TCP-only policies
  • Cloud/Kubernetes without multicast
  • NAT traversal where UDP hole punching fails
  • WAN connections with high packet loss

Configuration

TcpConfig

The main configuration struct controls all TCP transport behavior.

use hdds::transport::tcp::{TcpConfig, TcpRole};

// Basic: enable TCP with defaults
let config = TcpConfig::enabled();

// Custom configuration
let config = TcpConfig {
enabled: true,
listen_port: 7410,
role: TcpRole::Auto,
nodelay: true, // Disable Nagle (recommended for DDS)
max_message_size: 16 * 1024 * 1024, // 16 MB
..Default::default()
};

Configuration Options

OptionDefaultDescription
enabledfalseEnable TCP transport (opt-in)
listen_port0TCP listen port (0 = ephemeral)
listen_addressNoneBind address (None = all interfaces)
roleAutoConnection role (Auto, ServerOnly, ClientOnly)
nodelaytrueDisable Nagle's algorithm
max_message_size16 MBMaximum RTPS message size (anti-OOM)
connect_timeout5sOutbound connection timeout
reconnect_delay1sDelay before reconnection attempt
max_reconnect_attempts10Max reconnection attempts (0 = infinite)
keepalivetrueEnable TCP keep-alive probes
keepalive_interval30sKeep-alive probe interval
send_buffer_size256 KBApplication-level send buffer
recv_buffer_size256 KBApplication-level receive buffer
tls_enabledfalseEnable TLS encryption

Builder Methods

use hdds::transport::tcp::{TcpConfig, TcpRole};
use std::time::Duration;

let config = TcpConfig::enabled()
.with_port(7410)
.with_role(TcpRole::Auto)
.with_nodelay(true)
.with_max_message_size(8 * 1024 * 1024) // 8 MB
.with_connect_timeout(Duration::from_secs(10))
.with_keepalive(true, Duration::from_secs(30));

TCP Roles

The TCP role controls connection behavior between participants.

RoleListenConnectUse Case
AutoYesYesDefault. GUID tie-breaker ensures one connection per pair
ServerOnlyYesNoEdge devices behind NAT, known server topology
ClientOnlyNoYesMobile/embedded clients, firewall blocks inbound
use hdds::transport::tcp::{TcpConfig, TcpRole};

// Server: listen on port 7410, never initiate outbound connections
let server = TcpConfig::server_only(7410);

// Client: connect to known peers, never listen
let client = TcpConfig::client_only(vec![
"192.168.1.1:7410".parse().unwrap(),
"192.168.1.2:7410".parse().unwrap(),
]);

// TCP-only mode (no UDP discovery, requires initial_peers)
let tcp_only = TcpConfig::tcp_only(vec![
"192.168.1.1:7410".parse().unwrap(),
]);
Auto Role

In Auto mode, the participant with the "larger" GUID initiates the connection. This deterministic tie-breaking ensures exactly one TCP connection between any two participants, avoiding duplicate connections.

Transport Preferences

Control how TCP integrates with other transports.

PreferenceDiscoveryDataDescription
UdpOnlyUDPUDPStandard DDS (default)
TcpOnlyTCPTCPNo multicast, requires initial_peers
UdpDiscoveryTcpDataUDPTCPHybrid: multicast discovery, TCP data
ShmPreferredUDPSHM/UDPSHM local, UDP remote
ShmLocalTcpRemoteUDPSHM/TCPSHM local, TCP remote
use hdds::transport::tcp::TransportPreference;

// Hybrid mode: UDP multicast discovers peers, TCP for reliable data
let pref = TransportPreference::UdpDiscoveryTcpData;

// SHM for local processes, TCP for remote
let pref = TransportPreference::ShmLocalTcpRemote;

Initial Peers

For TCP-only mode or ClientOnly role, you must specify peers to bootstrap the mesh.

use hdds::transport::tcp::TcpConfig;

let config = TcpConfig::tcp_only(vec![
"10.0.0.1:7410".parse().unwrap(),
"10.0.0.2:7410".parse().unwrap(),
"10.0.0.3:7410".parse().unwrap(),
]);
ClientOnly Validation

TcpRole::ClientOnly requires at least one entry in initial_peers. The configuration will fail validation if initial_peers is empty.

TLS Encryption

TLS support requires the tcp-tls feature flag.

[dependencies]
hdds = { version = "0.1", features = ["tcp-tls"] }

Server-Side TLS

use hdds::transport::tcp::tls::TlsConfig;

let tls = TlsConfig::server()
.with_cert_file("server.crt")?
.with_key_file("server.key")?
.build()?;

Client-Side TLS

use hdds::transport::tcp::tls::TlsConfig;

// With system root certificates
let tls = TlsConfig::client()
.with_system_roots()
.build()?;

// With custom CA
let tls = TlsConfig::client()
.with_root_certs_file("ca.crt")?
.build()?;

Mutual TLS (mTLS)

use hdds::transport::tcp::tls::TlsConfig;

// Server requiring client certificates
let server_tls = TlsConfig::server()
.with_cert_file("server.crt")?
.with_key_file("server.key")?
.require_client_cert()
.build()?;

// Client with certificate for mTLS
let client_tls = TlsConfig::client()
.with_system_roots()
.with_cert_file("client.crt")?
.with_key_file("client.key")?
.build()?;

TLS Options

OptionDefaultDescription
verify_peertrue (client) / false (server)Verify peer certificate
require_client_certfalseRequire client certificate (mTLS)
enable_session_resumptiontrueTLS session ticket reuse
min_protocol_versionTLS 1.2Minimum TLS version
max_protocol_versionTLS 1.3Maximum TLS version
alpn_protocols["dds"]ALPN protocol negotiation
Testing Only

dangerous_disable_verification() skips certificate verification entirely. Never use this in production -- it defeats the purpose of TLS.

TCP + TLS Configuration

use hdds::transport::tcp::TcpConfig;

let config = TcpConfig::enabled()
.with_port(7410)
.with_tls(true)
.with_tls_config(tls_config);

// Verify TLS is properly configured
assert!(config.is_tls_ready());

Wire Format

TCP is a stream protocol, so RTPS messages are length-prefixed:

+----------------+-------------------+
| Length (4B BE) | RTPS Message |
+----------------+-------------------+

Each frame starts with a 4-byte big-endian length field followed by the RTPS message payload. The maximum message size is controlled by max_message_size (default: 16 MB).

C API

The C FFI provides builder-style configuration for TCP transport.

#include <hdds.h>

// Create participant config
HddsParticipantConfig* cfg = hdds_config_create("my_app");
hdds_config_set_domain_id(cfg, 0);

// Enable TCP on port 7410 (sets hybrid mode by default)
hdds_config_enable_tcp(cfg, 7410);

// Set TCP role
hdds_config_set_tcp_role(cfg, HDDS_TCP_ROLE_AUTO);

// Add initial peers for TCP-only mode
hdds_config_add_tcp_peer(cfg, "192.168.1.1:7410");
hdds_config_add_tcp_peer(cfg, "192.168.1.2:7410");

// Disable Nagle's algorithm
hdds_config_set_tcp_nodelay(cfg, true);

// Enable TLS
hdds_config_enable_tls(cfg);

// Set transport preference
hdds_config_set_transport_preference(cfg, HDDS_TRANSPORT_PREF_TCP_ONLY);

// Build participant (consumes cfg)
HddsParticipant* p = hdds_config_build(cfg);

C Enums

// TCP roles
typedef enum {
HDDS_TCP_ROLE_AUTO = 0,
HDDS_TCP_ROLE_SERVER_ONLY = 1,
HDDS_TCP_ROLE_CLIENT_ONLY = 2,
} HddsTcpRole;

// Transport preferences
typedef enum {
HDDS_TRANSPORT_PREF_UDP_ONLY = 0,
HDDS_TRANSPORT_PREF_TCP_ONLY = 1,
HDDS_TRANSPORT_PREF_UDP_DISCOVERY_TCP_DATA = 2,
HDDS_TRANSPORT_PREF_SHM_PREFERRED = 3,
HDDS_TRANSPORT_PREF_SHM_LOCAL_TCP_REMOTE = 4,
} HddsTransportPreference;

Limitations

LimitationDescription
HDDS-onlyTCP framing is vendor-specific, not cross-vendor interoperable
No multicastTCP cannot do multicast discovery; use UDP or initial_peers
Connection overheadTCP requires per-peer connections (vs UDP broadcast)
Head-of-line blockingSingle TCP stream means one lost packet blocks all messages

Next Steps