@hla4ts/transport
v0.1.1
Published
HLA 4 Federate Protocol transport layer with TLS and message framing
Downloads
263
Readme
@hla4ts/transport
HLA 4 Federate Protocol Transport Layer
This package provides the transport layer for the HLA 4 Federate Protocol, handling TCP/TLS connections, message framing, and low-level communication with HLA 4 RTIs.
Overview
The Federate Protocol uses a binary framing format over TCP or TLS connections. Each message has a 24-byte header followed by an optional payload. This package handles:
- Connection management - TCP and TLS socket connections
- Message framing - Encoding/decoding the 24-byte header
- Stream handling - Buffering partial messages, extracting multiple messages from a single chunk
Installation
bun add @hla4ts/transportQuick Start
import {
TlsTransport,
MessageType,
encodeMessage,
FEDERATE_PROTOCOL_VERSION,
} from '@hla4ts/transport';
// Create a TLS transport
const transport = new TlsTransport({
host: 'rti.example.com',
port: 15165,
});
// Set up event handlers
transport.setEventHandlers({
onMessage: (msg) => {
console.log('Received:', MessageType[msg.header.messageType]);
console.log('Payload size:', msg.payload.length);
},
onClose: (hadError) => {
console.log('Connection closed', hadError ? '(with error)' : '');
},
onError: (err) => {
console.error('Transport error:', err);
},
});
// Connect
await transport.connect();
// Send a CTRL_NEW_SESSION message
const payload = new Uint8Array(4);
new DataView(payload.buffer).setUint32(0, FEDERATE_PROTOCOL_VERSION, false);
const message = encodeMessage(
1, // sequence number
0n, // session ID (0 for new session)
0, // last received sequence number
MessageType.CTRL_NEW_SESSION, // message type
payload // payload
);
await transport.send(message);
// Later: disconnect
await transport.disconnect();Sequence Diagram
sequenceDiagram
participant RTI
participant Transport as Tcp/TlsTransport
participant Decoder as FrameDecoder
participant App as Federate Code
RTI-->>Transport: TCP/TLS bytes
Transport-->>Decoder: push(chunk)
Decoder-->>App: onMessage(header, payload)
App->>Transport: send(encoded message)
Transport-->>RTI: TCP/TLS bytesMessage Format
Every Federate Protocol message has a 24-byte header:
┌─────────────────────────────────────────────────────────────────┐
│ Offset │ Size │ Field │ Description │
├────────┼───────┼──────────────────────────┼─────────────────────┤
│ 0 │ 4 │ packetSize │ Total message size │
│ 4 │ 4 │ sequenceNumber │ Message sequence # │
│ 8 │ 8 │ sessionId │ Session identifier │
│ 16 │ 4 │ lastReceivedSequenceNum │ ACK for flow ctrl │
│ 20 │ 4 │ messageType │ Type of message │
├────────┴───────┴──────────────────────────┴─────────────────────┤
│ 24 │ var │ payload │ Message payload │
└─────────────────────────────────────────────────────────────────┘All integers are big-endian (network byte order).
Message Types
Control Messages (Session Management)
| Code | Name | Direction | Description |
|------|------|-----------|-------------|
| 1 | CTRL_NEW_SESSION | C → S | Request new session |
| 2 | CTRL_NEW_SESSION_STATUS | S → C | Session creation result |
| 3 | CTRL_HEARTBEAT | Both | Keep-alive ping |
| 4 | CTRL_HEARTBEAT_RESPONSE | Both | Keep-alive pong |
| 5 | CTRL_TERMINATE_SESSION | C → S | Request session end |
| 6 | CTRL_SESSION_TERMINATED | S → C | Session ended |
| 10 | CTRL_RESUME_REQUEST | C → S | Resume dropped session |
| 11 | CTRL_RESUME_STATUS | S → C | Resume result |
HLA Messages
| Code | Name | Direction | Description |
|------|------|-----------|-------------|
| 20 | HLA_CALL_REQUEST | C → S | RTI service call (protobuf CallRequest) |
| 21 | HLA_CALL_RESPONSE | S → C | RTI response (protobuf CallResponse) |
| 22 | HLA_CALLBACK_REQUEST | S → C | Federate callback (protobuf CallbackRequest) |
| 23 | HLA_CALLBACK_RESPONSE | C → S | Callback ACK (protobuf CallbackResponse) |
API Reference
Transports
TlsTransport
Secure TLS connection (recommended for production):
import { TlsTransport, Ports } from '@hla4ts/transport';
const transport = new TlsTransport({
host: 'rti.example.com',
port: Ports.TLS, // 15165 (default)
connectionTimeout: 10000, // 10 seconds (default)
noDelay: true, // TCP_NODELAY (default)
// TLS options
rejectUnauthorized: true, // Verify server cert (default)
ca: '/path/to/ca.pem', // Custom CA (optional)
cert: '/path/to/client.pem', // Client cert for mTLS (optional)
key: '/path/to/client-key.pem', // Client key for mTLS (optional)
serverName: 'rti.example.com', // SNI hostname (optional)
});TcpTransport
Plain TCP connection (for development/testing only):
import { TcpTransport, Ports } from '@hla4ts/transport';
const transport = new TcpTransport({
host: 'localhost',
port: Ports.TCP, // 15164 (default)
});Transport Interface
Both transports implement the Transport interface:
interface Transport {
connect(): Promise<void>;
disconnect(): Promise<void>;
isConnected(): boolean;
send(data: Uint8Array): Promise<void>;
setEventHandlers(handlers: TransportEvents): void;
getProtocolName(): string;
getRemoteAddress(): string | null;
}
interface TransportEvents {
onMessage?: (message: ReceivedMessage) => void;
onClose?: (hadError: boolean) => void;
onError?: (error: Error) => void;
}
interface ReceivedMessage {
header: MessageHeader;
payload: Uint8Array;
}Message Header Functions
import {
encodeHeader,
decodeHeader,
encodeMessage,
getPayloadSize,
HEADER_SIZE, // 24
NO_SESSION_ID, // 0n
NO_SEQUENCE_NUMBER, // 0
} from '@hla4ts/transport';
// Encode just the header (24 bytes)
const header = encodeHeader(
payloadSize, // payload size in bytes
sequenceNumber, // sequence number
sessionId, // session ID (bigint)
lastReceivedSequenceNum, // ACK
messageType // MessageType enum
);
// Encode header + payload
const message = encodeMessage(
sequenceNumber,
sessionId,
lastReceivedSequenceNum,
messageType,
payload // optional Uint8Array
);
// Decode a header
const header = decodeHeader(buffer); // throws on invalid data
console.log(header.packetSize);
console.log(header.sequenceNumber);
console.log(header.sessionId);
console.log(header.messageType);
// Get payload size from header
const payloadSize = getPayloadSize(header);Frame Decoder
For advanced use cases, you can use the FrameDecoder directly:
import { FrameDecoder } from '@hla4ts/transport';
const decoder = new FrameDecoder();
decoder.onMessage = (msg) => {
console.log('Complete message received');
console.log('Type:', msg.header.messageType);
console.log('Payload:', msg.payload);
};
decoder.onError = (err) => {
console.error('Decode error:', err);
};
// Feed data as it arrives (handles fragmentation automatically)
socket.on('data', (chunk) => {
decoder.push(new Uint8Array(chunk));
});
// Check buffered data
console.log('Bytes waiting:', decoder.bufferedBytes);
// Reset after connection reset
decoder.reset();Constants
import {
FEDERATE_PROTOCOL_VERSION, // 1
Ports,
Protocol,
NewSessionStatus,
ResumeStatus,
} from '@hla4ts/transport';
// Default ports
Ports.TCP // 15164
Ports.TLS // 15165
Ports.WS // 80
Ports.WSS // 443
// Protocol names
Protocol.TCP // "tcp"
Protocol.TLS // "tls"
Protocol.WS // "websocket"
Protocol.WSS // "websocketsecure"
// Session status codes
NewSessionStatus.SUCCESS // 0
NewSessionStatus.UNSUPPORTED_PROTOCOL_VERSION // 1
NewSessionStatus.OUT_OF_RESOURCES // 2
NewSessionStatus.BAD_MESSAGE // 3
NewSessionStatus.OTHER_ERROR // 99
// Resume status codes
ResumeStatus.SUCCESS // 0
ResumeStatus.SESSION_NOT_FOUND // 1
ResumeStatus.NOT_ALLOWED // 2Session Protocol Flow
┌─────────────┐ ┌─────────────┐
│ Federate │ │ RTI │
└──────┬──────┘ └──────┬──────┘
│ │
│──── TCP/TLS Connect ──────────────────────>│
│ │
│──── CTRL_NEW_SESSION (version=1) ─────────>│
│ │
│<─── CTRL_NEW_SESSION_STATUS (sessionId) ───│
│ │
│──── HLA_CALL_REQUEST (Connect) ───────────>│
│<─── HLA_CALL_RESPONSE ─────────────────────│
│ │
│──── HLA_CALL_REQUEST (JoinFederation) ────>│
│<─── HLA_CALL_RESPONSE ─────────────────────│
│ │
│ ... HLA calls and callbacks ... │
│ │
│<─── HLA_CALLBACK_REQUEST (Discover...) ────│
│──── HLA_CALLBACK_RESPONSE ────────────────>│
│ │
│──── CTRL_HEARTBEAT ───────────────────────>│
│<─── CTRL_HEARTBEAT_RESPONSE ──────────────│
│ │
│──── CTRL_TERMINATE_SESSION ───────────────>│
│<─── CTRL_SESSION_TERMINATED ──────────────│
│ │
│──── TCP/TLS Disconnect ───────────────────>│
│ │Error Handling
try {
await transport.connect();
} catch (err) {
if (err.message.includes('Connection timeout')) {
console.error('RTI not reachable');
} else if (err.message.includes('certificate')) {
console.error('TLS certificate error');
}
}
transport.setEventHandlers({
onError: (err) => {
console.error('Transport error:', err);
// Attempt reconnection...
},
onClose: (hadError) => {
if (hadError) {
console.error('Connection lost unexpectedly');
}
},
});Testing
cd packages/transport
bun testRelated Packages
@hla4ts/proto- Protocol buffer types@hla4ts/session- Session management@hla4ts/hla-api- High-level HLA API facade
References
- IEEE 1516-2025 - HLA 4 Standard
- Pitch FedProClient - Reference implementation
License
MIT
