작은 UDS 요청만 테스트할 때는 DoIP가 꽤 안정적으로 보입니다.
- Session Control
- Read DID
- ECU Reset
이 정도는 대부분 잘 됩니다.
그런데 firmware download 들어가는 순간 분위기가 달라집니다.
- timeout 증가
- throughput 저하
- parser 오류
- reconnect 반복
- transfer abort
이런 문제가 갑자기 튀어나옵니다.
이유는 간단합니다.
평소에는 안 보이던 TCP stream 처리와 large payload 문제가 드러나기 시작하기 때문입니다.
시리즈 목차
- DoIP Diagnostic Message, 결국 중요한 건 이 패킷이다
- DoIP Alive Check, 연결은 살아 있는데 왜 끊겼을까
- DoIP Negative Acknowledge, UDS 에러랑은 다르다
- DoIP Firmware Download에서 갑자기 문제가 터지는 이유
평소 테스트는 너무 작다
예를 들어 ReadDataByIdentifier:
22 F1 90
몇 바이트 안 됩니다.
반면 firmware transfer는 다릅니다.
36 xx [large data block]
수 KB ~ 수 MB 데이터가 계속 흘러갑니다.
즉:
small request/reply
→ 간단한 parser로도 버팀
large continuous stream
→ transport 문제 드러남
이 흐름입니다.
TCP는 message protocol이 아니다
여기서 많이 무너집니다.
초기 구현에서는 보통 이런 가정을 합니다.
data = sock.recv(4096)
process_one_packet(data)
하지만 TCP는 packet 기반이 아닙니다.
예를 들면:
DoIP Packet A 절반
만 먼저 올 수도 있고,
Packet A + Packet B
가 한 번에 붙어서 올 수도 있습니다.
Firmware transfer에서는 이런 상황이 훨씬 자주 나옵니다.
그래서 parser 구조가 중요하다
안전한 흐름은 보통 이렇습니다.
1. exact header size read
2. payload length parse
3. exact payload read
4. message process
즉:
header = recv_exact(sock, 8)
payload_len = parse_length(header)
payload = recv_exact(sock, payload_len)
이 흐름이 핵심입니다.
중간에:
recv(4096)
결과를 바로 parser에 넣기 시작하면 큰 transfer에서 흔들립니다.
transfer block size도 영향이 있다
UDS TransferData 자체도 block 단위로 움직입니다.
예를 들어:
RequestDownload
→ TransferData #1
→ TransferData #2
→ TransferData #3
이렇게 이어집니다.
문제는 block size가 커질수록:
- TCP buffering 영향 증가
- ECU processing delay 증가
- timeout 민감도 증가
가 같이 올라간다는 점입니다.
그래서 무조건 큰 block이 좋은 건 아닙니다.
현장에서 자주 보는 문제
1) ECU 응답 대기 중 timeout
예시:
TransferData 송신
→ ECU flash write
→ 응답 지연
→ tester timeout
→ reconnect
실제로는 ECU가 죽은 게 아니라 flash 작업 중일 수 있습니다.
그래서 programming session timeout 정책은 일반 진단보다 길게 잡는 경우가 많습니다.
2) throughput 계산이 비현실적임
이론상 Ethernet은 빠릅니다.
하지만 실제 firmware transfer는:
- flash write 시간
- internal erase
- task scheduling
- gateway forwarding
영향을 다 받습니다.
즉:
Ethernet bandwidth != Flashing speed
입니다.
3) sender만 빠름
PC 쪽은 계속 밀어 넣는데 ECU가 못 따라가는 경우입니다.
예를 들어:
while True:
sock.sendall(block)
이런 구조는 위험합니다.
반드시:
request
→ response 확인
→ next block
흐름을 지켜야 합니다.
특히 TransferData sequence 관리는 중요합니다.
sequence counter도 자주 꼬인다
Firmware download에서 sequence counter mismatch가 꽤 자주 나옵니다.
예시:
TransferData #15 전송
응답 timeout
재전송
ECU는 이미 #16 기대 중
이 상태가 되면 recovery가 복잡해집니다.
그래서 timeout 발생 시:
- retry 가능한지
- session 재시작할지
- download restart할지
정책을 명확히 해야 합니다.
로그는 block 단위로 남기는 게 좋다
Firmware transfer에서는 payload 전체 dump보다 흐름 로그가 더 중요합니다.
예시:
[uds] transfer block=15 size=4096 tx
[uds] transfer block=15 ack rx
[uds] transfer block=16 size=4096 tx
추가로 좋았던 정보:
elapsed time
retry count
current throughput
flash state
이런 것들입니다.
Wireshark만으로 안 잡히는 경우
이것도 자주 겪습니다.
패킷은 정상처럼 보이는데 실제 flashing은 실패합니다.
왜냐하면 문제 원인이:
- ECU internal flash task
- watchdog reset
- storage latency
- application deadlock
일 수도 있기 때문입니다.
즉:
network trace 정상
≠ flashing 정상
입니다.
구현 시 추천하는 구조
Firmware download는 상태 머신으로 보는 게 편합니다.
예시:
IDLE
→ REQUEST_DOWNLOAD
→ TRANSFER_DATA
→ TRANSFER_EXIT
→ VERIFY
→ COMPLETE
여기에:
- retry
- reconnect
- timeout
- abort
를 추가하는 방식이 유지보수에 좋습니다.
오늘 포인트
DoIP firmware transfer 문제의 핵심은 Ethernet 속도가 아닙니다.
실제로 중요한 건:
- TCP stream 처리
- parser robustness
- timeout 정책
- flash latency
- retry state 관리
입니다.
작은 diagnostic request에서는 괜찮던 코드가 large transfer에서 무너지는 이유도 여기 있습니다.
다음 글에서는 DoIP Gateway 환경에서 ECU routing이 어떻게 동작하는지 이어서 다뤄보겠습니다.
추천 대상
- DoIP firmware flashing tool을 구현 중인 개발자
- large payload transfer에서 timeout 문제를 겪는 엔지니어
- TCP stream parser를 안정적으로 만들고 싶은 사람
한 줄 요약
DoIP firmware download에서는 단순 packet 송수신보다 TCP stream 처리와 timeout 상태 관리가 훨씬 중요하다.
추천 키워드
doip, firmware download, uds transferdata, automotive ethernet, tcp stream, flashing
DevBJ | No Bio, Just Log #오늘을살자