RTC 품질 문제를 패킷 캡처로 확인하는 절차를 정리했습니다. TCP 재전송과 Zero Window, RTP sequence number와 timestamp, RTCP 품질 보고서, TLS·DTLS handshake를 Wireshark에서 찾는 방법을 설명하고 서버에서는 tcpdump로 캡처한 뒤 로컬에서 분석하는 흐름까지 다룹니다.
"화질이 나빠요", "소리가 끊겨요", "연결이 안 돼요" 같은 RTC 문제에서 packet capture는 중요한 증거입니다. 다만 캡처 위치·암호화·capture loss·NIC offload의 영향을 받으므로 Wireshark 한 화면만으로 원인을 확정할 수는 없습니다. 이 글에서는 관측 사실과 원인 추정을 구분해 분석하는 방법을 정리합니다.
먼저 알아야 할 것: 네트워크 스택
Wireshark가 보여주는 것은 패킷입니다. 패킷이 뭔지 이해하려면 네트워크 스택을 알아야 합니다.
하나의 패킷을 Wireshark에서 열면:
Ethernet II (Link)
└── Internet Protocol Version 4 (Network)
└── User Datagram Protocol (Transport)
└── Real-Time Transport Protocol (Application)
└── H.264 NAL Unit (Payload)
TCP vs UDP — 트러블슈팅 관점에서의 차이
TCP: 연결 지향, 순서 보장, 재전송
TCP 3-Way Handshake (연결 수립):
Client Server
│ │
├── SYN ────────────▶│ "연결하고 싶어요"
│◀── SYN+ACK ────────┤ "좋아요, 나도"
├── ACK ────────────▶│ "확인"
│ │
│ 연결 수립 완료 │
TCP의 핵심 메커니즘:
1. Sequence Number — 바이트 단위로 "어디까지 보냈는지" 추적
2. ACK — "여기까지 받았어" 확인
3. Retransmission — ACK 안 오면 재전송
4. Window Size — "나 지금 이만큼 받을 수 있어" (흐름 제어)
Wireshark에서 TCP 문제 찾기:
필터: tcp.analysis.retransmission
→ TCP 분석기가 재전송으로 분류한 packet. 실제 network loss뿐 아니라 capture loss·reordering 가능성도 함께 확인
필터: tcp.analysis.zero_window
→ 수신 측이 처리를 못 따라가서 "더 보내지 마" 상태
필터: tcp.analysis.duplicate_ack
→ 같은 ACK를 반복 → 패킷 유실 발생 징후
UDP: 비연결, 순서 보장 없음, 재전송 없음
UDP는 handshake가 없음:
Client Server
│ │
├── 데이터 ──────────▶│ "받아라" (확인 안 함)
├── 데이터 ──────────▶│ "또 받아라"
├── 데이터 ──────────▶│ "또"
│ │
│ 유실돼도 모름 │
UDP 자체로는 문제를 감지할 수 없음
→ RTP 레벨에서 봐야 함 (아래 RTP 섹션 참고)
필터: udp.port == 특정포트
→ 특정 미디어 스트림만 필터링
언제 뭘 쓰는가
WebRTC 미디어: SRTP + ICE. UDP 우선, 필요하면 TURN/TCP·TURN/TLS
WebRTC 시그널링: 표준이 transport를 정하지 않음. 애플리케이션이 HTTPS/WebSocket 등을 선택
RTMP/RTMPS: TCP 또는 TLS 기반 ingest가 일반적
HTTP: HTTP/1.1·2는 TCP, HTTP/3는 QUIC/UDP
TLS / DTLS — 암호화 레이어
TLS (TCP 위의 암호화)
TCP Handshake 완료 후:
Client Server
│ │
├── ClientHello ────────────────▶│ 지원하는 암호 목록 전달
│◀── ServerHello ────────────────┤ 선택된 암호 suite + 세션 파라미터
│◀── Certificate ────────────────┤ 서버 인증서
│◀── ServerHelloDone ────────────┤
├── ClientKeyExchange ──────────▶│ 키 교환
├── ChangeCipherSpec ───────────▶│ "이제부터 암호화"
├── Finished (encrypted) ───────▶│
│◀── ChangeCipherSpec ───────────┤
│◀── Finished (encrypted) ──────┤
│ │
│ 이후 모든 데이터 암호화 │
위 다이어그램은 RSA key exchange를 포함한 TLS 1.2의 한 예입니다. ECDHE cipher suite와 client authentication 여부에 따라 message가 달라집니다. TLS 1.3은 일반적인 full handshake를 1-RTT로 줄이고 message 구성을 바꿉니다. 실제 협상 버전은 capture에서 확인합니다.
Wireshark에서 TLS 트러블슈팅:
필터: tls.handshake.type == 1
→ ClientHello만 보기 (연결 시도)
필터: tls.handshake.type == 2
→ ServerHello만 보기 (서버 응답)
필터: tls.alert_message
→ TLS 에러 (인증서 만료, 버전 불일치 등)
# TLS 1.3 감지 (ClientHello의 legacy_version은 0x0303이므로)
필터: tls.handshake.extensions.supported_version == 0x0304
→ TLS 1.3을 지원하는 ClientHello
TLS application data를 복호화하려면 session secret이 필요합니다. SSLKEYLOGFILE 등으로 얻은 key log가 권장됩니다. 서버 RSA private key는 TLS 1.3이나 (EC)DHE처럼 forward secrecy를 쓰는 세션에는 사용할 수 없습니다.
DTLS (UDP 위의 암호화)
웹의 일반 TLS는 TCP 또는 QUIC 등과 함께 쓰이고,
WebRTC media key agreement에는 DTLS-SRTP가 사용됨
표준 WebRTC:
DTLS로 키 교환 → SRTP로 미디어 암호화
DTLS Handshake (UDP)
↓ 키 교환 완료
SRTP (암호화된 RTP)
DTLS는 TLS와 거의 동일하지만, UDP의 패킷 유실/재정렬을 처리할 수 있도록 설계되었습니다. Handshake 메시지에 자체 retransmission timer가 있습니다.
비디오 (30fps, 90kHz clock):
정상 timestamp 증가량 = 90000 / 30 = 3000 per frame
Timestamp: 90000 → 93000 → 96000 → 99000
+3000 +3000 +3000 ✅
timestamp 간격이 커진 예:
Timestamp: 90000 → 93000 → 99000 → 102000
+3000 +6000 +3000
▲
capture gap·가변 frame rate·의도적 frame skip 등 여러 가능성
오디오 (Opus, 48kHz, 20ms 프레임):
정상 timestamp 증가량 = 48000 * 0.02 = 960 per frame
Timestamp: 48000 → 48960 → 49920 → 50880
+960 +960 +960 ✅
실전 트러블슈팅 시나리오
시나리오 1: "소리가 끊겨요"
진단 순서:
1. 캡처 시작
$ sudo tcpdump -i any -w capture.pcap 'udp portrange 10000-60000'
또는 Wireshark에서 직접 캡처
2. RTP 스트림 확인
Telephony → RTP → RTP Streams
3. 오디오 스트림(Opus) 선택 → Stream Analysis
4. 확인할 것:
├── sequence gap이 반복되는가? → capture loss/재정렬/실제 경로 유실을 양쪽 캡처로 구분
├── jitter·delta spike가 playout underrun과 겹치는가?
├── Delta 급증 구간? → 특정 시점에 네트워크 끊김
└── 패킷이 아예 안 옴? → 방화벽/NAT 문제
패킷 유실이 원인인 경우:
Wireshark Stream Analysis:
시간 Seq Delta Jitter Status
0.00 1001 20ms 5ms OK
0.02 1002 20ms 5ms OK
0.04 ---- ---- ---- LOST ← 여기서 끊김
0.06 1004 40ms 15ms OK
0.08 1005 20ms 12ms OK
→ 관측: 이 capture 지점에서 sequence gap
→ 다음 확인: 송·수신 양쪽 capture와 RTCP/SDK stats를 대조. 지원되는 경우 FEC/RTX 설정 검토
Jitter가 원인인 경우:
시간 Seq Delta Jitter
0.00 1001 20ms 5ms
0.02 1002 20ms 5ms
0.04 1003 85ms 35ms ← delta 급증
0.06 1004 5ms 30ms ← 몰아서 도착
0.08 1005 20ms 25ms
→ 관측: packet 도착 간격 변동. Wi-Fi 간섭·경로 변경·sender pacing 등 후보를 추가 검증
→ 결과: jitter buffer 범위를 초과하면 끊김
→ 대응: access network 개선과 SDK가 공개한 jitter-buffer/latency 설정 범위 검토
시나리오 2: "영상이 깨져요 (모자이크)"
진단 순서:
1. 비디오 RTP 스트림에서 패킷 유실 확인
2. 유실된 패킷이 어떤 프레임 타입인지 확인
필터: rtp.marker == 1
→ 프레임의 마지막 패킷만 표시 (프레임 경계 확인)
IDR access unit 손실:
→ decoder refresh가 성공할 때까지 심각한 손상이 이어질 수 있음
reference picture의 packet 손실:
→ 이후 의존 picture까지 전파될 수 있으며 concealment·다중 reference 구조에 따라 영향이 다름
Wireshark에서 H.264 NAL type 확인:
필터: h264.nal_unit_type == 5
→ single-NAL packet 등에서 IDR slice NAL unit
필터: h264.nal_unit_type == 1
→ non-IDR slice NAL unit. P-frame으로 단정하지 않고 slice header의 `slice_type`을 파싱해 판별
FU-A로 분할된 경우 outer NAL type은 28이며, fragment header의 original NAL type을 별도로 확인해야 합니다.
키프레임 유실이 잦으면:
→ 키프레임 간격(GOP) 줄이기 또는 FEC 강화
시나리오 3: "연결이 안 돼요"
진단 순서:
1. TCP handshake 확인
필터: tcp.flags.syn == 1
SYN만 보이고 SYN+ACK가 없으면:
→ 이 capture 지점에서는 응답이 관측되지 않음
→ DNS 결과·routing·방화벽·서버 listen 상태를 송수신 양쪽에서 확인
2. TLS handshake 확인
필터: tls.handshake
ClientHello만 보이고 ServerHello가 없으면:
→ TCP 연결 이후 TLS 응답이 관측되지 않음
→ 서버 로그와 반대편 capture로 TLS policy·middlebox·서버 상태를 구분
3. DTLS handshake 확인 (WebRTC/RTC)
필터: dtls.handshake
DTLS ClientHello가 안 보이면:
→ ICE candidate pair가 선택되었는지와 capture 위치를 먼저 확인
→ UDP 경로가 막힌 경우에는 TURN/TCP·TURN/TLS fallback을 검토
4. STUN binding 확인
필터: stun
STUN Binding Request만 보이고 Response가 없으면:
→ 해당 STUN transaction의 응답이 이 지점에서 관측되지 않음
→ transaction ID·재시도·ICE state를 대조하고 relay candidate 사용 여부 확인
5. TURN relay 트래픽 확인
필터: stun.type == 0x0016 || stun.type == 0x0017
TURN을 경유하는 경우, 미디어가 ChannelData나
Send/Data indication으로 캡슐화되어 보입니다.
일반 RTP와 다르게 보이므로 혼동 주의.
시나리오 4: "RTMP CDN 송출이 끊겨요"
RTMP는 TCP 기반이므로 TCP 분석이 핵심:
1. RTMP handshake 확인
필터: tcp.port == 1935
→ TLS나 비표준 포트를 쓰면 endpoint·TCP stream 기준으로 추적
2. TCP 재전송 확인
필터: tcp.analysis.retransmission && tcp.port == 1935
→ capture loss·reordering을 배제한 뒤 경로 유실 또는 지연 후보로 판단
3. TCP Window Size 확인
필터: tcp.analysis.zero_window && tcp.port == 1935
→ window를 광고한 수신 endpoint의 receive buffer 또는 application 처리 지연 후보
4. RST 패킷 확인
필터: tcp.flags.reset == 1 && tcp.port == 1935
→ 누가 RST를 보냈는지는 알 수 있지만 stream key·codec 같은 application 원인은 서버 로그로 확인
자주 쓰는 Wireshark 필터 모음
프로토콜별 기본 필터
# 특정 IP만 보기
ip.addr == 192.168.1.100
# 특정 포트만 보기
tcp.port == 443
udp.port == 5004
# 프로토콜별
rtp
rtcp
stun
dtls
tls
tcp
udp
# RTP이면서 특정 IP에서 온 것
rtp && ip.src == 192.168.1.100
# TCP 재전송인데 특정 포트
tcp.analysis.retransmission && tcp.port == 1935
# UDP이지만 RTP가 아닌 것 (STUN, DTLS 등)
udp && !rtp && !stun
# 특정 시간 범위만
frame.time >= "2024-01-15 14:30:00" && frame.time <= "2024-01-15 14:35:00"
RTCP — 품질 모니터링의 핵심
RTP와 함께 반드시 알아야 할 것이 **RTCP(RTP Control Protocol)**입니다. RTP가 미디어를 전송한다면, RTCP는 "전송이 잘 되고 있는가"를 리포트합니다.
수신 통계 (fraction/cumulative loss, extended sequence, jitter, LSR/DLSR)
SDES
202
Source Description
SSRC에 대한 메타데이터 (CNAME 등)
BYE
203
Goodbye
"나 나갈게"
RTPFB
205
Transport Feedback
NACK (재전송 요청), TMMBR (대역폭 요청)
PSFB
206
Payload-Specific FB
PLI·FIR 등 payload-specific feedback
Wireshark에서 RTCP 분석
Receiver Report에서 볼 수 있는 정보:
┌─────────────────────────────────────────────┐
│ RTCP Receiver Report │
├─────────────────────────────────────────────┤
│ SSRC of source: 0xA1B2C3D4 │
│ Fraction lost: 12 (4.7%) ← 유실률! │
│ Cumulative lost: 847 │
│ Extended highest seq: 18234 │
│ Interarrival jitter: 1823 ← RTP timestamp 단위 │
│ Last SR timestamp: 0x83AB12 │
│ Delay since last SR: 15234 ← 1/65536초 단위 │
└─────────────────────────────────────────────┘
fraction lost는 직전 reporting interval의 손실 비율입니다. 절대적인 합격선으로 단정하지 말고 codec·bitrate·burst pattern·FEC/RTX·concealment·사용자 체감과 함께 봅니다. interarrival jitter도 millisecond가 아니라 해당 RTP stream의 clock 단위이므로 clock rate로 환산해야 합니다. RTT는 SR/RR의 LSR·DLSR과 report 도착 시각을 조합해 추정할 수 있으며 RR 한 필드가 RTT를 직접 담는 것은 아닙니다.
tcpdump — 서버에서 패킷 캡처하기
Wireshark는 GUI 도구이므로 서버에서 직접 쓰기 어렵습니다. 서버에서는 tcpdump로 캡처하고, 로컬에서 Wireshark로 분석합니다.
기본 캡처 명령어
# 특정 인터페이스의 모든 UDP 패킷 캡처sudo tcpdump -i eth0 udp -w rtp_capture.pcap
# 특정 포트 범위 (RTP는 보통 동적 포트 사용)sudo tcpdump -i eth0 udp portrange 10000-60000 -w rtp_capture.pcap
# 특정 IP와의 통신만sudo tcpdump -i eth0 host 203.0.113.50 -w specific_host.pcap
# TCP 443 (TLS/HTTPS) 캡처sudo tcpdump -i eth0 tcp port 443 -w tls_capture.pcap
# RTMP (1935 포트) 캡처sudo tcpdump -i eth0 tcp port 1935 -w rtmp_capture.pcap
# 시간 제한 캡처 (60초)sudo tcpdump -i eth0 udp -w capture.pcap -G 60 -W 1
-G·-W의 종료·rotation 동작은 tcpdump 구현과 OS에 따라 차이가 있으므로 운영 환경의 man page에서 확인합니다.
1. 용량 관리
RTP 캡처는 빠르게 GB 단위가 됨
→ -c 10000 (패킷 수 제한) 또는 -G 60 (시간 제한) 사용
2. 권한
패킷 캡처에는 OS가 요구하는 capture 권한이 필요
→ sudo, Linux capability, capture group 등 조직의 최소 권한 정책 사용
3. 성능 영향
고트래픽 서버에서 전체 캡처하면 CPU/IO 부하
→ 필터를 최대한 좁혀서 캡처
네트워크 기초 수치 — 운영자가 알아야 할 기준값
대역폭 계산
오디오 (Opus):
예: payload bitrate 32 kbps, 20ms packetization = 50 packets/sec
RTP/UDP/IPv4 기본 header: 12 + 8 + 20 = 40 bytes
L3 header overhead: 40 × 8 × 50 = 16 kbps
→ 합계 약 48 kbps. IPv6·extension·SRTP·link/VPN overhead는 별도
비디오 (H.264, 720p30):
해상도만으로 bitrate를 정할 수 없음
content complexity·frame rate·quality target·encoder·loss protection에 따라 설정
packet header overhead도 평균 RTP payload 크기와 IP version에 따라 계산
1:1 화상통화:
→ 각 방향의 실제 send/receive bitrate와 overhead를 합산
그룹 통화 (4명, SFU):
송출: 선택한 simulcast/SVC layer들의 합
수신: 동시에 subscribe한 layer들의 합
→ participant 수만으로 고정 대역폭을 산출할 수 없음
Latency (지연) 기준
구간별 지연 분해 예시:
인코딩: 10~30ms
네트워크: 20~150ms (지역에 따라)
Jitter Buffer: 20~80ms
디코딩: 5~15ms
렌더링: 5~15ms
────────────────────
총: 60~290ms
ITU-T G.114의 일반 planning guidance:
0~150ms: 대부분의 user application에 수용 가능
150~400ms: application과 영향 인지 수준에 따라 수용 가능
400ms 초과: 일반적인 network planning에는 수용하기 어려움
이는 one-way mouth-to-ear delay 기준이며 서비스의 상호작용 형태에 따라 허용 범위가 달라집니다.
Jitter 기준
RFC 3550 interarrival jitter는 RTP timestamp 단위로 계산한 도착 간격 변동의 통계치입니다.
→ clock rate로 환산해 해석
→ playout deadline 이후 도착한 packet은 network에서 도착했어도 재생에는 늦을 수 있음
→ jitter buffer 크기와 적응 정책은 구현·latency mode에 따라 다름
Packet Loss 기준
평균 loss만 보지 말고 burst 길이와 위치를 함께 확인
FEC/RTX가 복구한 packet과 playout deadline 이후 도착한 packet을 구분
오디오는 PLC·in-band FEC 설정, 비디오는 reference 구조·keyframe 복구에 따라 영향이 달라짐
서비스의 SDK stats·MOS/QoE·사용자 시점 로그를 같은 시간축으로 대조
실전 운영 체크리스트
문제 리포트를 받았을 때 확인하는 순서:
1단계: 문제 분류
├── "연결이 안 돼요" → 시나리오 3 (Handshake 확인)
├── "소리가 끊겨요" → 시나리오 1 (Audio RTP 분석)
├── "영상이 깨져요" → 시나리오 2 (Video RTP + NAL type)
├── "CDN 송출 끊김" → 시나리오 4 (TCP 재전송/RST)
└── "지연이 심해요" → RTT + Jitter Buffer 분석
2단계: 캡처
├── 클라이언트 측: Wireshark 직접 캡처
├── 서버 측: tcpdump → pcap → Wireshark
└── 양쪽 동시 캡처가 이상적 (어디서 유실되는지 특정 가능)
3단계: 분석
├── RTP Streams (Telephony 메뉴)
├── Stream Analysis (유실/jitter 그래프)
├── RTCP 리포트 (Receiver Report의 fraction lost)
└── TCP 분석 (재전송, zero window, RST)
4단계: 가설 검증
├── sequence gap → 양쪽 capture로 capture loss·실제 경로 loss 구분
├── 높은 jitter → Wi-Fi·queueing·pacing·경로 변경 후보와 시간축 대조
├── handshake 실패 → DNS·routing·방화벽·서버·TLS 정책을 단계별 확인
└── TCP RST → 송신 endpoint 식별 후 application/server log로 이유 확인
정리
Wireshark는 추측을 packet-level 관측으로 바꿔주는 도구입니다. 원인 확정에는 양쪽 capture, endpoint log, SDK stats가 함께 필요합니다.