UDP Message Format
Stream UDP Integration Protocol Specification
1. Overview
This document specifies the binary protocol used by the Stream platform for UDP-based data integration. It enables external systems to receive real-time tag data from Stream controllers via UDP packets.
Integrity: Each packet includes a CRC-16 checksum.
Conventions
- Byte order: Big-endian for all multi-byte values
- Strings: UTF-8 encoded
Important: All multi-byte integers are big-endian.
2. Packet Layout
Each UDP packet has three parts:
┌───────────────┬───────────────┬─────────┐
│ HEADER │ BODY │ CRC16 │
│ (28 bytes) │ (variable) │ (2 B) │
└───────────────┴───────────────┴─────────┘
Key specifications
- Maximum packet size: 1200 bytes
- CRC coverage: Header + Body (CRC not included)
3. Header (28 bytes)
|
Field |
Type |
Size |
Description |
|
Magic |
String |
4 |
Literal "SCTL" (ASCII) |
|
PacketType |
Byte |
1 |
0 = Data, 2 = Reserved |
|
Flags |
Byte |
1 |
Control flags (currently unused; set to 0) |
|
StreamId |
Int16 |
2 |
Stream identifier (big-endian) |
|
Sequence |
Int64 |
8 |
Packet sequence number (big-endian) |
|
BodyLength |
Int16 |
2 |
Length of the Body in bytes (big-endian) |
|
Reserved |
Bytes |
10 |
Reserved for future use (set to zeros) |
Validation checklist
- Magic is "SCTL"
- BodyLength matches actual parsed body length
4. Body
The Body contains a collection of items.
|
Field |
Type |
Size |
Description |
|
ItemCount |
UInt16 |
2 |
Number of items (big-endian) |
|
Items |
Item[] |
Variable |
Sequence of Item records |
4.1 Item
|
Field |
Type |
Size |
Description |
|
NameLength |
UInt16 |
2 |
Length of tag name in bytes (big-endian) |
|
NameUtf8 |
String |
Variable |
Tag name (UTF-8) |
|
ValueType |
Byte |
1 |
Data type identifier (see §5 Value Types) |
|
Timestamp |
Int64 |
8 |
UNIX timestamp in milliseconds (big-endian) |
|
Value |
Variant |
Variable |
Value payload; format depends on ValueType |
Timestamp precision: Milliseconds since Unix epoch (January 1, 1970).
5. Value Types
|
Type ID |
Name |
Value Format |
Description |
|
0 |
Bool |
1 byte |
0 = false, 1 = true |
|
1 |
Int16 |
2 bytes (big-endian) |
16-bit signed integer |
|
2 |
Real32 |
4 bytes (IEEE-754) |
32-bit floating-point |
|
3 |
String |
2 + N bytes |
Length-prefixed UTF-8 string |
|
4 |
Int32 |
4 bytes (big-endian) |
32-bit signed integer |
|
5 |
Int64 |
8 bytes (big-endian) |
64-bit signed integer |
5.1 String Value Format
|
Field |
Type |
Size |
Description |
|
Length |
UInt16 |
2 |
String length in bytes (BE) |
|
StringData |
String |
Variable |
UTF-8 string bytes |
Practical limits: ~1150 bytes per string or tag name (constrained by packet size).
6. CRC-16
A 16-bit CRC is appended at the end of the packet.
- Algorithm: CRC-16-CCITT
- Polynomial: 0x1021
- Initial value: 0xFFFF
- Covers: Header + Body (excludes the CRC field itself)
- Format: 2 bytes, big-endian
Tip: Compute CRC over exactly the first (Header + BodyLength) bytes.
7. Fragmentation
When data exceeds 1200 bytes, split across multiple packets.
- Each fragment:
- Has a unique Sequence number (increment for each fragment)
- Uses the same StreamId for the logical stream
- Is independently validated via CRC-16
Note: Packets may arrive out of order. Use Sequence and StreamId to detect gaps and associate fragments.
8. Example
Scenario: Two items
- Temperature = 23.5 (Real32) at 1672531200000
- Pressure = 1013 (Int32) at 1672531200001
Hex dump (annotated):
53 43 54 4C # Magic "SCTL"
00 # PacketType (Data)
00 # Flags
00 01 # StreamId = 1
00 00 00 00 00 00 00 01 # Sequence = 1
00 2D # BodyLength = 45
00 00 00 00 00 00 00 00 00 00 # Reserved (10 bytes)
00 02 # ItemCount = 2
# Item 1: Temperature
00 0B # NameLength = 11
54 65 6D 70 65 72 61 74 75 72 65 # "Temperature"
02 # ValueType = Real32
00 00 01 85 94 4E 00 00 # Timestamp = 1672531200000
41 BC 00 00 # Value = 23.5 (IEEE-754 float)
# Item 2: Pressure
00 08 # NameLength = 8
50 72 65 73 73 75 72 65 # "Pressure"
04 # ValueType = Int32
00 00 01 85 94 4E 00 01 # Timestamp = 1672531200001
00 00 03 F5 # Value = 1013
A1 B2 # CRC-16
9. Integration Guidelines
9.1 UDP Socket Setup
- Listen on the configured UDP port.
- Buffer size ≥ 1200 bytes.
- Anticipate packet loss and duplication (UDP is unreliable).
9.2 Packet Validation
- Verify Magic = "SCTL".
- Ensure BodyLength matches parsed body bytes.
- Verify CRC-16.
- Check timestamps for reasonable ranges (e.g., not far in future/past).
9.3 Data Processing
- Parse items sequentially.
- Respect variable-length fields (names and string values).
- Convert timestamps from UNIX milliseconds as needed.
- Parse Real32 using IEEE-754.
9.4 Ordering & Loss
- Use Sequence to detect gaps/missing packets.
- StreamId differentiates streams.
- Tolerate out-of-order arrival.
10. Constraints & Notes
- Maximum packet size: 1200 bytes
- Maximum items per packet: Depends on item sizes (names + values)
- Name & string limits: Practically ~1150 bytes each
- Timestamp precision: Milliseconds (Int64)
- Flags: Currently unused; set to 0 for compatibility