FirmGod

모드버스 TCP 프레임 빌더

MBAP 헤더를 항목별로 해석하고 길이 필드를 검증합니다. 캡처한 스트림을 프레임 단위로 잘라 보여 줍니다.

16진수, 구분자는 무엇이든 됩니다. 해석은 이 페이지에서 합니다.

예시
Length agreesProtocol id 0
항목바이트값
Transaction id00 011 — the server echoes this back
Protocol id00 000 — Modbus
Length00 066 — unit id plus 5-byte PDU
Unit idFF255 — the TCP device itself, as the specification suggests
Function code033 — Read Holding Registers
Start address00 000 (0x0000)
Quantity00 011

The same request on a serial line

The MBAP header is replaced by the unit id as a slave address and a CRC. Useful for checking a gateway against the device behind it.

FF 03 00 00 00 01 91 D4

MBAP 헤더가 RTU와 다른 점

모드버스 TCP는 RTU의 PDU를 그대로 쓰되 앞에 7바이트 헤더를 붙이고 CRC를 뺍니다. 그 헤더를 MBAP이라고 부르며 네 개의 필드로 이루어집니다.

  • 트랜잭션 식별자 2바이트 — 요청과 응답을 짝짓는 번호
  • 프로토콜 식별자 2바이트 — 모드버스는 항상 0
  • 길이 2바이트 — 유닛 식별자부터 끝까지의 바이트 수
  • 유닛 식별자 1바이트 — 게이트웨이 뒤의 슬레이브 주소

CRC가 없는 것은 TCP가 이미 무결성을 책임지기 때문입니다. 프레임 끝에 CRC를 붙이면 길이 필드와 실제 바이트 수가 어긋나 상대가 다음 프레임의 시작을 잘못 잡습니다.

길이 필드를 세는 기준

길이 필드에 헤더 전체를 포함시키는 실수가 흔합니다. 세는 시작점은 유닛 식별자입니다. 앞의 6바이트는 빠집니다.

보유 레지스터 열 개를 읽는 요청이라면 PDU는 기능 코드 1 + 주소 2 + 개수 2 = 5바이트이고, 유닛 식별자 1바이트를 더해 길이는 6입니다. 전체 프레임은 12바이트가 됩니다.

TCP는 스트림입니다

가장 자주 나오는 구현 실수는 한 번의 read가 한 프레임을 준다고 가정하는 것입니다. TCP는 바이트 스트림이라 그런 보장이 없습니다.

  • 프레임 두 개가 한 번에 붙어 올 수 있습니다.
  • 한 프레임이 두 번 이상에 나뉘어 올 수 있습니다.
  • 헤더 7바이트조차 쪼개져 올 수 있습니다.

올바른 수신 코드는 버퍼에 쌓으면서 헤더 7바이트가 모였는지 보고, 길이 필드를 읽어 그만큼 더 기다린 다음 한 프레임을 꺼냅니다. 이 도구가 붙여넣은 캡처를 프레임 단위로 잘라 보여 주는 것은 그 과정을 눈으로 확인하기 위한 것입니다.

트랜잭션 식별자를 쓰는 이유

시리얼에서는 요청 하나에 응답 하나가 순서대로 오므로 짝을 맞출 필요가 없습니다. TCP에서는 응답을 기다리지 않고 여러 요청을 보낼 수 있어서, 돌아온 응답이 무엇에 대한 것인지 표시가 필요합니다.

클라이언트가 값을 정하고 서버는 그대로 복사해 돌려줍니다. 순차 증가가 보통이지만 규칙은 없고, 요청을 하나씩만 보내는 구현이라면 0으로 고정해도 동작합니다. 다만 캡처를 분석할 때 번호가 있는 편이 훨씬 읽기 쉽습니다.

포트와 게이트웨이

표준 포트는 502입니다. 장비가 다른 포트를 쓴다면 문서에 적혀 있습니다.

TCP 요청을 시리얼로 중계하는 게이트웨이를 쓴다면 유닛 식별자가 RTU 슬레이브 주소 역할을 합니다. 그런 구성에서 응답이 오지 않는다면 TCP 연결 문제인지 시리얼 쪽 문제인지부터 나눠 봐야 하고, 게이트웨이가 예외 0x0B(대상 장비 응답 없음)를 돌려주는 경우가 단서가 됩니다.

자주 묻는 질문

TCP에는 왜 CRC가 없나요?

TCP가 이미 체크섬으로 무결성을 보장하기 때문입니다. RTU의 CRC는 시리얼 회선에 그런 보호가 없어서 필요한 것이고 TCP에서는 중복입니다. TCP 프레임 끝에 CRC를 붙이면 MBAP의 길이 필드와 실제 바이트 수가 어긋나 상대가 프레임 경계를 잘못 잡습니다.

길이 필드에 무엇이 들어가나요?

유닛 식별자부터 프레임 끝까지의 바이트 수입니다. MBAP 헤더 7바이트 중 앞의 6바이트는 세지 않고 유닛 식별자 1바이트부터 셉니다. 그래서 PDU 길이에 1을 더한 값이 됩니다. 이 계산을 틀리면 상대가 다음 프레임의 시작 위치를 잘못 잡아 이후 통신이 전부 어긋납니다.

트랜잭션 식별자는 무엇에 쓰나요?

요청과 응답을 짝지으려고 씁니다. TCP는 여러 요청을 응답 없이 연달아 보낼 수 있어서 돌아온 응답이 어느 요청의 것인지 알아야 합니다. 클라이언트가 값을 정하고 서버는 그대로 되돌려 줍니다. 순차 증가가 일반적이지만 규칙은 없습니다.

유닛 식별자는 RTU의 슬레이브 주소와 같은가요?

게이트웨이를 거칠 때는 같은 역할을 합니다. TCP 요청을 시리얼로 중계하는 장비라면 유닛 식별자가 RTU 슬레이브 주소가 됩니다. 직접 연결된 TCP 장비라면 의미가 없어서 보통 0xFF나 0x01을 씁니다. 장비 문서가 지정하는 값을 쓰세요.

한 패킷에 프레임이 여러 개 들어올 수 있나요?

들어옵니다. TCP는 스트림이라 패킷 경계와 프레임 경계가 일치하지 않습니다. 한 번의 read로 프레임 두 개가 붙어 오거나 한 프레임이 두 번에 나뉘어 오기도 합니다. 그래서 수신 코드는 길이 필드를 읽어 프레임을 직접 잘라야 하고, 이 도구가 캡처를 프레임 단위로 나눠 보여 주는 이유도 그것입니다.