TCP 재전송으로 산업용 카메라 통신이 수 초 멈출 때 - RFC1323 타임스탬프와 재전송 대기의 구분
· 小村 豪 · TCP, 네트워크, 불량 조사, Windows 개발, 산업용 카메라
산업용 카메라나 장치 제어 통신에서는 평균적으로는 빠른데도 가끔 수 초 멈추는 현상이 가장 까다롭습니다. 재현율이 낮고 평소에는 아무것도 일어나지 않으므로 UI, 스레드, GC, 카메라 SDK, NIC, 스위치 모든 것이 조금씩 의심스러워집니다.
이번에 다루는 것은 산업용 카메라를 제어하는 앱과 호스트의 TCP 통신에서, 드물게 수 초간만 통신이 멈추는 사상입니다. 조사해 보니 정체는 앱의 정지가 아니라 패킷 로스에 기인한 TCP의 재전송 대기였습니다. 또한 RFC1323 계열의 타임스탬프 기능(현재의 정리로는 RFC 7323)을 유효화함으로써 이 계에서는 대기 시간을 최소한으로 줄일 수 있었습니다.
장치명이나 구성, 수치는 일반화했지만, 사고방식은 그대로 실무에서 쓸 수 있습니다.
1. 먼저 결론(한마디로)
- 드물게 수 초 멈추는 TCP 통신은 앱 정지가 아니라 패킷 로스 후의 재전송 대기가 정체인 경우가 있습니다
- 패킷 캡처에서
Retransmission과 큰 시간차가 보이고, 정지 시간이 RTO의 대기 방식과 맞물리면 꽤 의심스럽습니다 - TCP timestamps option은 RTT 측정과 PAWS를 위한 구조로, 재전송 시의 RTT 측정의 애매함도 제거할 수 있습니다
- 이 사례에서는 RFC1323 계열의 타임스탬프 기능을 유효화함으로써, RTO 추정이 오래되고 보수적인 상태로 남는 시간을 줄여 초 단위의 정지를 최소한으로 줄일 수 있었습니다
- 다만 이것은 로스 그 자체를 없애는 마법은 아닙니다. 물리층, NIC, 스위치, 중간 기기, 드라이버, 버퍼 설계의 재검토는 별도로 필요합니다
요컨대 「가끔 수 초 멈춘다」의 정체가 TCP 안의 대기 시간이라면, 앱의 재시도만 열심히 해도 핵심을 빗나갑니다. 먼저 wire를 보고 재전송 대기인지 아닌지를 확정하는 편이 빠릅니다.
2. 증상의 보이는 방식
2.1. 앱은 살아 있는데 응답만 수 초 멈춘다
처음 까다로운 것은 앱 전체가 굳은 것처럼은 보이지 않는다는 점입니다.
- UI는 완전히 죽어 있지 않다
- 프로세스도 죽지 않았다
- CPU도 붙어 있지 않다
- 다만 카메라 제어 커맨드의 응답만 가끔 수 초 빠진다
이런 증상은 앱 내의 deadlock이나 무한 루프와도 구별하기 어렵습니다. 더구나 장치 제어에서는 1회의 수 초 정지가 그대로 라인 정지의 인상이 됩니다. 평균값이 깨끗해도 현장의 체감은 꽤 나쁩니다.
2.2. 저빈도라서 로그만으로는 보이기 어렵다
이런 타입의 불량이 귀찮은 것은 발생 빈도가 낮다는 점입니다. 한 시간에 한 번, 반나절에 한 번, 조건이 겹쳤을 때만 같은 움직임을 합니다.
로그만으로 쫓아가면 다음과 같은 일이 일어납니다.
- 앱 로그상은 「송신했다」 「돌아오지 않는다」로 멈춘다
- 수신 측 로그는 「아무것도 오지 않았다」로 보인다
- 마침 같은 시간대에 다른 이벤트도 일어나 범인이 분산된다
이럴 때 앱 로그만으로 인과관계를 복원하려 하면 꽤 평범하게 수렁에 빠집니다. 통신 층까지 한 단 내려가는 편이 빠릅니다.
3. 무엇이 일어나고 있었는가(도식)
3.1. 패킷 로스에서 재전송 대기로 들어간다
이번의 시나리오는 단순합니다. 중간 어딘가에서 패킷이 떨어지고, 송신 측이 ACK를 기다리는데 오지 않으므로 RTO를 기다린 뒤에 재전송하고 있었습니다.
sequenceDiagram
participant Host as 호스트 앱
participant Net as 네트워크
participant Cam as 카메라 측
Host->>Net: 제어 커맨드 (Seq=N)
Note over Net: 여기서 로스
Note over Host: ACK가 오지 않아 대기
Note over Host: 이 요구의 회복에는 RTO 대기가 들어간다
Host->>Net: 제어 커맨드를 재전송
Net->>Cam: 재전송 패킷 도달
Cam-->>Net: ACK
Net-->>Host: ACK
Note over Host: 여기서 통신 재개
앱에서 보면 「수 초 멈췄다」처럼 보이지만, TCP적으로는 「아직 ACK가 오지 않았기 때문에 재전송 타이머의 만료를 기다리고 있었다」라는 것뿐입니다. 수수하지만 이런 멈춤 방식은 평범하게 있습니다.
이번 제어 통신은 작은 request/response가 많고, 한 번의 교환으로 대량의 미 ACK 데이터가 날아가지 않았습니다. 그래서 duplicate ACK를 충분히 모아 fast retransmit에 올라타기 전에 RTO 대기가 앞쪽에 나오기 쉬운 구성이었습니다.
3.2. 초 단위 정지와 RTO의 형태가 맞물렸다
TCP의 재전송 대기는 구현 차이는 있지만 보수적인 방식으로 대기합니다. RFC 6298에서는 초기 RTO는 1초가 기준으로, 계산 결과가 그보다 작으면 1초로 올림하고, 타임아웃이 일어나면 배증합니다.
flowchart LR
A[패킷 로스] --> B[ACK가 오지 않음]
B --> C[RTO 대기]
C --> D[재전송]
D --> E{ACK가 돌아오는가?}
E -- 예 --> F[통신 재개]
E -- 아니오 --> G[RTO를 배증]
G --> C
그래서 수백 밀리초로 끝나기를 바라는 국면에서도 조건이 나쁘면 1초, 2초, 4초 같은 대기 방식으로 보일 수 있습니다. 이번의 「가끔 수 초 멈춘다」는 이 형태와 꽤 솔직하게 맞물렸습니다.
4. 조사에서 본 포인트
4.1. 먼저 앱 내의 정지 요인을 제거한다
곧바로 TCP로 단정하지 않고, 먼저 앱 쪽의 전형 요인을 제거했습니다.
| 확인한 것 | 본 이유 | 이번의 결론 |
|---|---|---|
| UI 스레드 / 워커 스레드 | 행이나 상호 대기의 확인 | 주요 원인이 아니었다 |
| CPU 사용률 | 고부하에 의한 처리 지연의 확인 | 정지 시에도 붙어 있지 않았다 |
| GC / 메모리 압박 | 일시 정지의 확인 | 정지 시간의 형태가 맞지 않았다 |
| 카메라 SDK 호출 | SDK 내 대기의 확인 | wire상의 지연과 일치하지 않았다 |
| 패킷 캡처 | 통신 층의 재전송 확인 | 여기서 원인의 실마리가 보였다 |
여기서 중요한 것은 앱 로그의 시각만으로 범인을 정하지 않는 것입니다. 장치 제어 앱에서는 상위의 대기가 하위의 대기를 그저 비추고 있는 경우가 있습니다.
4.2. 패킷 캡처로 재전송을 확인한다
패킷 캡처를 취하자 정지된 시간대에서 TCP Retransmission이 보이고, 또 그 직전에 ACK가 돌아오지 않았음을 알 수 있었습니다.
봐야 할 점은 예를 들어 다음입니다.
- 같은
Seq의 재전송이 나오는가 - 재전송까지의 시간차가 정지 시간과 일치하는가
Dup ACK나Fast Retransmission이 아니라 RTO 만료 대기로 보이는가- 문제의 접속이 매번 같은
tcp.stream에 나오는가
여기가 맞으면 「앱이 멈춰 있다」가 아니라 「TCP가 재전송 대기를 하고 있다」가 꽤 진해집니다.
4.3. 협상된 TCP 옵션을 본다
다음으로 본 것은 접속 개시 시의 SYN / SYN-ACK입니다. 타임스탬프는 TCP 접속의 3-way handshake에서 협상되므로, 여기에 TSopt가 나와 있지 않으면 그 접속에서는 쓰이지 않습니다.
sequenceDiagram
participant Host as 호스트
participant Cam as 카메라 측
Host->>Cam: SYN + TSopt ?
Cam-->>Host: SYN/ACK + TSopt ?
Host->>Cam: ACK
Note over Host,Cam: 여기서 협상되어야 비로소 이후의 세그먼트에서 TSopt를 쓸 수 있다
여기를 보지 않은 채 OS 설정만 건드리면 「유효화했는데 안 듣는다」는 이 또한 수수한 사고가 됩니다. 설정값보다 wire의 사실이 더 강합니다.
5. RFC1323 타임스탬프가 듣는 이유
실무에서는 「RFC1323의 타임스탬프」라는 호칭이 아직 남아 있지만, 현행의 정리는 RFC 7323입니다. 이 글에서는 관용에 맞춰 RFC1323이라고 쓰면서, 의미로는 TCP timestamps option을 가리킵니다.
5.1. 타임스탬프는 RTTM과 PAWS를 위해
TCP의 timestamps option은 주로 다음 2가지를 위해 쓰입니다.
- RTTM(Round-Trip Time Measurement)
- PAWS(Protect Against Wrapped Sequences)
여기서 이번에 효과가 있었던 것은 RTTM 쪽입니다. 송신한 세그먼트의 TSval을 상대가 ACK의 TSecr로 돌려주는 것으로, 송신 측은 RTT를 더 세밀하고 정확하게 측정하기 쉬워집니다.
5.2. 재전송 시의 RTT 측정의 애매함을 제거할 수 있다
재전송이 들어가면 타임스탬프 없이는 「이 ACK는 처음의 송신에 대한 것인가, 재전송에 대한 것인가」가 애매해집니다. 이것이 이른바 Karn 알고리즘이 신경 쓰는 점입니다.
RFC 6298에서는 재전송된 세그먼트에서는 RTT 샘플을 취해서는 안 된다고 되어 있습니다. 이유는 어느 송신에 대한 ACK인지 모르기 때문입니다. 다만 timestamps option이 있으면 이 애매함을 제거할 수 있습니다. ACK에 들어오는 TSecr을 보면 어느 TSval을 가진 세그먼트가 도달했는지 식별할 수 있기 때문입니다.
sequenceDiagram
participant Host as 송신 측
participant Cam as 수신 측
Host->>Cam: Seq=N, TSval=1000
Note over Host,Cam: 이 세그먼트가 로스
Note over Host: ACK가 오지 않아 대기
Host->>Cam: Seq=N, TSval=2000을 재전송
Cam-->>Host: ACK, TSecr=2000
Note over Host: 어느 송신에 대한 응답인지 판별 가능
여기가 이번 개선의 핵심입니다.
5.3. 이 사례에서 대기 시간을 좁힐 수 있었던 이유
이 사례에서는 패킷 로스가 가끔 발생하고, 그때마다 RTT / RTO의 추정이 보수적으로 기울기 쉬운 상태였습니다. 타임스탬프를 유효화하면 재전송을 포함한 장면에서도 RTT의 견적을 갱신하기 쉬워져, RTO 추정이 오래된 채로 부풀어 계속되는 시간을 억제할 수 있습니다.
바꿔 말하면 이번에 한 것은 TCP를 빠르게 하는 마법이 아니라, TCP가 필요 이상으로 길게 동태를 살펴보는 시간을 줄이는 것입니다.
물론 RFC 7323도 「RTT 샘플이 늘면 뭐든지 깔끔하게 해결된다」고는 말하지 않습니다. RTO 최적화에 듣는 정도는 한정적인 면도 있습니다. 다만 재전송 시의 애매함을 제거할 수 있다는 점은 이번 같은 계에서는 솔직하게 듣는 경우가 있습니다.
주의점도 있습니다.
- 이것은 TCP stack의 구현에 의존하는 부분이 있습니다
- 타임스탬프만으로 패킷 로스 자체는 사라지지 않습니다
- 물리층이나 중간 기기가 나쁘면 근본 원인은 따로 있습니다
- SACK나 NIC 드라이버, 오프로드 설정, 스위치 쪽의 문제도 별도로 보는 편이 좋습니다
다만 이번처럼 「로스는 제로가 아니다」 「하지만 정말 아픈 것은 초 단위의 대기」인 계에서는 꽤 효과가 있는 경우가 있습니다.
6. 실제로 한 대책
6.1. 타임스탬프를 유효화한다
대책으로는 접속 양단에서 timestamps option을 협상할 수 있는 상태로 했습니다. Windows 계에서는 RFC 1323 옵션으로 다뤄지는 경우가 있으며, OS 설정이나 네트워크 설정의 영향을 받습니다.
다만 실무에서는 「설정 화면에서 유효화되어 있다」보다 「SYN / SYN-ACK의 실 패킷에 TSopt가 실려 있다」가 더 중요합니다. 여기는 정말 그렇습니다.
6.2. SYN / SYN-ACK에서 TSopt를 확인한다
유효화 후에는 다음 3점을 확인했습니다.
- 문제의 접속의 SYN에 TSopt가 있는가
- SYN/ACK 측도 TSopt를 돌려주는가
- 이후의 데이터 세그먼트와 ACK에도 TSopt가 계속 실리는가
여기를 확인할 수 있어야 비로소 「그 접속에서 timestamps가 실제로 쓰이고 있다」고 말할 수 있습니다.
6.3. 그래도 효과가 없을 때 볼 곳
타임스탬프를 유효화해도 다음과 같은 경우는 개선이 둔할 수 있습니다.
- 로스율 자체가 높다
- 중간 기기가 TCP option을 부수거나 떨어뜨리거나 변형한다
- NIC / 드라이버 / 오프로드 주변에 다른 문제가 있다
- 앱이 한 개의 동기 호출에 전체를 매달아 두어, 1회의 대기가 그대로 전 정지로 보인다
- 실제로는 TCP가 아니라 카메라 쪽의 처리 정지나 장치 내 큐 막힘이 주요 원인
따라서 대책의 순서로는 다음이 알기 쉽습니다.
- 먼저 wire에서 재전송 대기를 확인한다
- TSopt의 협상 유무를 본다
- timestamps를 유효화하고 개선 차분을 본다
- 아직 남으면 로스원과 앱 설계를 따로 따로 파고든다
7. Wireshark에서 보는 포인트
구분에 쓰기 쉬운 표시 필터는 예를 들어 다음입니다.
tcp.stream eq <대상 스트림>
tcp.analysis.retransmission
tcp.analysis.fast_retransmission
tcp.analysis.lost_segment
tcp.options.timestamp.tsval
tcp.options.timestamp.tsecr
보는 방식의 요령은 다음입니다.
tcp.stream으로 대상 접속만으로 좁힌다Time delta from previous displayed packet을 내고, 멈춘 초수를 그대로 본다- 문제의 순간에
Retransmission이 나오고 있는지 확인한다 - 접속 개시 시의 SYN / SYN-ACK에서 TSopt가 협상되고 있는지 확인한다
- ACK의
TSecr이 돌아오고 있는지 본다
로그와 패킷을 대조할 때는 앱 시각과 캡처 시각의 기준 차이에도 주의가 필요합니다. 여기가 어긋나면 다른 건을 범인 취급하기 쉽습니다.
8. 대략적인 구분 사용
| 증상 | 우선 의심할 것 | 먼저 할 일 |
|---|---|---|
| 수 초 단위로 가끔 멈춘다 | TCP의 RTO 대기 | 재전송과 시간차를 패킷으로 확인 |
| 매번 거의 같은 타이밍에 멈춘다 | 앱 내 대기, 장치 쪽 처리, 고정 타임아웃 | 스레드, SDK 호출, 장치 로그를 본다 |
| 고부하 시에만 악화된다 | CPU, GC, 큐 막힘 | CPU, 인터럽트, 메모리, 큐 길이를 본다 |
| 광범위한 접속에서 일제히 나쁘다 | 물리층, 스위치, 중간 기기 | NIC, 케이블, 포트 통계, 중간 기기 로그를 본다 |
| 설정 변경했는데 바뀌지 않는다 | TCP option이 협상되어 있지 않다 | SYN / SYN-ACK를 재확인한다 |
마지막 행은 정말 많습니다. 설정을 만진 만족감과 wire상에서 쓰이고 있는 사실은 별개입니다.
9. 정리
이번의 포인트:
- 「가끔 수 초 멈춘다」는 앱 정지가 아니라 TCP의 재전송 대기인 경우가 있다
- 정지 시간이 RTO의 대기 방식과 맞고,
Retransmission이 보이면 꽤 실마리가 좋다 - TCP timestamps option은 RTTM과 PAWS의 구조로, 재전송 시의 RTT 측정의 애매함을 제거할 수 있다
- 이 사례에서는 RFC1323 계열 타임스탬프를 유효화함으로써 RTO가 과도하게 보수화된 채로 남는 시간을 억제할 수 있었다
피하고 싶은 진행 방식:
- 앱 로그만으로 통신 정지의 범인을 정한다
- OS 설정만 보고 실 패킷을 보지 않는다
- 타임스탬프를 유효화하면 로스 원인까지 사라진다고 생각한다
실무에서 효과가 있는 진행 방식:
- 먼저 wire를 본다
- 재전송과 대기 시간의 형태를 확인한다
- TSopt의 협상을 확인한다
- 개선 후에도 로스원과 앱 설계는 따로 파고든다
즉, 이런 종류의 불량은 「빠르게 한다」보다 「어디서 기다리고 있는가를 맞힌다」가 먼저입니다. 여기를 빗나가지 않는 것만으로도 조사는 꽤 짧아집니다.
10. 참고 자료
- RFC 1323 - TCP Extensions for High Performance
- RFC 7323 - TCP Extensions for High Performance
- RFC 5681 - TCP Congestion Control
- RFC 6298 - Computing TCP’s Retransmission Timer
-
[Description of Windows TCP features - Windows Server Microsoft Learn](https://learn.microsoft.com/en-us/troubleshoot/windows-server/networking/description-tcp-features)
관련 기사
같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.
산업용 카메라 제어 앱이 1개월 뒤에 갑자기 떨어질 때(후편) - Application Verifier란 무엇인가와 이상계 테스트 기반의 만드는 방법
Application Verifier로 핸들 오용, 힙 파괴, 저리소스 시 fault injection을 강제로 일으켜 산업용 카메라 제어 앱처럼 장시간 동작하는 Windows 네이티브 앱의 이상계 테스트 기반을 만드는 방법을 정리합니다.
산업용 카메라 제어 앱이 1개월 뒤에 갑자기 떨어질 때(전편) - 핸들 누수의 발견 방법과 장기 가동용 로그 설계
Windows 산업용 카메라 제어 앱이 약 한 달 연속 가동 후에 떨어지는 사례를 통해, 핸들 누수의 발견 절차와 Handle Count 관측, create/close 쌍을 추적하는 로그 설계, 단시간에 재현하는 실패 경로 테스트의 진행 방식을 ...
Windows 앱의 크래시 덤프 수집 입문 - 우선 WER / ProcDump / WinDbg를 어떻게 구분해서 쓸까
Windows 앱의 재현 어려운 크래시를 쫓는 첫걸음으로, WER LocalDumps, ProcDump, MiniDumpWriteDump의 구분 사용과 미니/풀 덤프 선택, PDB 보관과 권한 설계, WinDbg에서 먼저 볼 포인트까지 입문자에게...
Windows에서의 NIC 상세 설정을 한꺼번에 정리 - Jumbo Packet, RSS, LSO, RSC, Flow Control, EEE, Wake on LAN까지
Windows NIC 고급 탭의 Jumbo Packet, RSS, RSC, LSO, Interrupt Moderation, Flow Control, EEE, Wake on LAN을 의미와 부작용까지 정리하고, 스루풋・레이턴시・CPU・절전・호환성 ...
COM 컴포넌트나 OCX / ActiveX 개발에서 빠지기 쉬운 것 - Visual Studio의 32bit / 64bit, 등록, 관리자 권한의 덫을 정리
COM 컴포넌트와 ActiveX, OCX 개발에서 자주 만나는 0x80040154나 0x80070005를 비트 수, 등록 방식, HKCU와 HKLM 스코프, 관리자 권한이라는 네 축으로 풀어 Visual Studio 2022의 64bit화 시대에...
관련 토픽
이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.
Windows 기술 토픽
Windows 개발, 장애 조사, 기존 자산 활용에 관한 KomuraSoft LLC 기사를 모은 토픽 허브입니다.
장애 조사 & 장기 가동 장애
간헐적 장애, 통신 진단, 장기 가동 크래시, 실패 경로 테스트 기반을 정리한 토픽 페이지입니다.
관련 사례
실제 정리와 개선 진행 방식이 가까운 사례 페이지입니다.
수 초 단위의 통신 정지를 분리한 사례
드물게 발생하는 통신 정지를 재전송 대기와 OS 측 조건으로 나누어 분리한 사례 페이지입니다.
이 주제와 연결되는 서비스
이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.
Windows 앱 개발
상주 처리, 장비 연동, 운영 로그, 유지 보수 가능한 구조가 필요한 Windows 데스크톱 애플리케이션을 지원합니다.
저자 프로필
기사 저자의 프로필 페이지입니다.
Go Komura
합동회사 코무라소프트 대표
Windows 소프트웨어 개발, 기술 상담, 장애 조사를 중심으로 재현이 어려운 장애 조사와 기존 자산이 남아 있는 프로젝트에 강점이 있습니다.
공개 링크