In short: Modbus TCP wraps the classic Modbus request/response PDU in a 7-byte MBAP header and sends it over TCP port 502, dropping the CRC (TCP and Ethernet already protect the frame) and reusing the unit ID so a gateway can steer the request to a specific RTU slave on a serial line.
Why Modbus TCP exists
Modbus RTU was designed in 1979 for RS-485 multidrop wiring: one master, up to 247 slaves, every frame guarded by a 16-bit CRC and delimited by silent gaps of 3.5 character times. That works well on a 9600 or 19200 baud bus, but plants wanted PLCs, SCADA servers and HMIs on Ethernet. Rather than invent a new application layer, Schneider (the Modbus owner) kept the function codes and data model intact and defined a new transport mapping: Modbus TCP. If you already understand the differences between Modbus RTU and TCP, this article goes one layer deeper into the byte-level framing and gateway behaviour that interviewers love to probe.
Key fact: the Modbus PDU (function code + data) is identical in RTU and TCP. Only the wrapper, the "ADU", changes.
The MBAP header, byte by byte
The Modbus Application Protocol header is exactly 7 bytes, all multi-byte fields big-endian:
- Transaction ID (2 bytes): chosen by the client, echoed unchanged by the server. It lets a client keep several requests in flight and match replies.
- Protocol ID (2 bytes): always 0x0000 for Modbus. Receivers should reject anything else.
- Length (2 bytes): count of the bytes that follow, i.e. unit ID + function code + data.
- Unit ID (1 byte): the addressing byte inherited from the RTU slave address.
A worked example you can decode by hand
Read 10 holding registers starting at address 0x006B from unit 0x11 (function 0x03):
Request : 00 01 | 00 00 | 00 06 | 11 | 03 | 00 6B | 00 0A
txn=1 proto=0 len=6 unit fc addr qty=10
Response: 00 01 | 00 00 | 00 17 | 11 | 03 | 14 | 20 bytes of register data
Check the lengths: the request carries unit (1) + function (1) + address (2) + quantity (2) = 6. The response carries unit (1) + function (1) + byte count (1) + 20 data bytes = 23 = 0x17. Interviewers like to ask "what does the length field include?" and the common wrong answer is "the whole frame". It excludes the first six header bytes.
Key fact: an exception reply sets the top bit of the function code (0x03 becomes 0x83) followed by a one-byte exception code, so the length is 3.
Port 502 and the client/server model
A Modbus TCP server listens on well-known port 502 (IANA registered; secure Modbus/TLS uses 802). The master/slave vocabulary becomes client/server: the client opens a TCP connection, sends a request, and the server replies. Because TCP is a byte stream, the receiver must not rely on packet boundaries. Parse the first six bytes, read the length, then read exactly that many more bytes. Production code in libmodbus or pymodbus does precisely this. Embedded servers commonly cap concurrent connections at 4 to 16, so a SCADA server polling carelessly can starve an engineering laptop out of a PLC.
Pipelining is allowed by the transaction ID, but many cheap devices process one request at a time, so conservative clients keep a single request outstanding per connection and use a timeout of a few hundred milliseconds.
Why there is no CRC
RTU needs its CRC-16 because a noisy RS-485 line has no other integrity check. On Ethernet, every frame already ends with a 32-bit FCS checked by the NIC, and TCP adds a 16-bit checksum plus retransmission and ordering. A Modbus CRC on top would be redundant overhead. The trade-off: TCP guarantees the bytes arrive intact, but not that the application is sane. Also note that silent-interval framing (the 3.5 character gap) disappears entirely, replaced by the length field.
| Property | Modbus RTU | Modbus TCP |
|---|---|---|
| Transport | RS-485 / RS-232 | TCP, port 502 |
| Addressing | Slave address 1-247 | IP address + unit ID |
| Integrity | CRC-16 | TCP checksum + Ethernet FCS |
| Framing | 3.5-char silence | MBAP length field |
| Max frame | 256 bytes | 260 bytes |
| Matching replies | Strict one at a time | Transaction ID |
Unit ID and how gateways bridge to RTU
When you talk to a native TCP device, the unit ID is mostly irrelevant; many devices ignore it or expect 0xFF (or 0x01). It becomes essential at a gateway, where one IP address fronts a whole RS-485 bus. The unit ID then is the RTU slave address.
The gateway algorithm is straightforward:
- Accept the TCP request and remember the transaction ID.
- Drop the MBAP, take the unit ID as the RTU address, keep the PDU, and compute and append CRC-16 (polynomial 0xA001 reflected).
- Transmit on the serial line, honouring the inter-frame silence, and start a response timer (often 500 ms to 1 s at low baud rates).
- On a valid reply, check the CRC, strip it, and prepend a fresh MBAP with the stored transaction ID and the correct length.
- On timeout, return exception 0x0B (gateway target device failed to respond); if the serial path is unusable, 0x0A.
Because the serial bus is half-duplex, the gateway queues requests and serializes them. A burst of 20 clients each polling 10 devices at 9600 baud, with each transaction taking roughly 30 to 60 ms, will saturate the line. This is why gateway throughput, not Ethernet bandwidth, is the usual bottleneck. Broadcast requests (RTU address 0) get no response, so the gateway should reply immediately or after the bus quiet time.
Key fact: the CRC is created and destroyed at the gateway. It never crosses the Ethernet side, and the transaction ID never crosses the serial side.
Watching it move
Practical pitfalls and security
- No authentication or encryption: anyone who reaches port 502 can write coils. Segment the network, firewall 502, or use Modbus/TLS on 802. This is a classic ICS security interview topic.
- Address off-by-one: documentation says register 40001 but the wire address is 0.
- Byte and word order: 32-bit floats span two registers and vendors disagree on the word order.
- Stale connections: use TCP keepalive and reconnect logic, since many PLCs silently drop idle sockets.
The same protocol knowledge underpins SoC debug and test infrastructure, such as the board-management controllers and test-rig automation that the engineers at Avecas design services build around silicon bring-up. For a comparison with a bus built for deterministic realtime, read CAN bus basics.
Want hands-on practice with industrial and embedded protocols? Explore the CourseTron courses and learn by building. For instructor-led VLSI training, see ChipXpert.
FAQ
What port does Modbus TCP use?
TCP port 502 for standard Modbus TCP, and port 802 for Modbus/TLS (Modbus Security).
Why does Modbus TCP not have a CRC?
Ethernet already appends a 32-bit FCS and TCP adds its own checksum with retransmission, so the 16-bit Modbus CRC would be redundant.
What is the unit ID used for?
It carries the RTU slave address. A gateway uses it to choose which serial device receives the request; native TCP devices often ignore it or expect 0xFF.
How does a gateway convert Modbus TCP to RTU?
It strips the MBAP header, saves the transaction ID, uses the unit ID as the slave address, appends a CRC-16, transmits serially, then reverses the process on the reply.