LabHub
开始
学习 学习路径 课程

实时通信 — WebSocket、gRPC 流式调用与 WebRTC

WebRTC — 信令要自己做

在 LabHub 中继续学习

한국어 원문으로 표시합니다.

한 줄 요약

WebRTC 는 연결을 맺는 방법을 정하지 않으므로 SDP 를 건넬 시그널링을 직접 만들고, ICE 로 길을 찾고, DTLS 로 열쇠를 나눈 뒤, SRTP 로 미디어를, SCTP 로 데이터 채널을 나릅니다.

왜 이게 필요했나

두 기기가 서로 직접 말하게 하려면 풀어야 할 문제가 셋입니다. 서로의 주소와 능력을 어떻게 알리나(시그널링), 둘 다 NAT 뒤에 있을 때 어떤 길로 닿나(ICE·STUN·TURN), 그 길 위의 데이터를 어떻게 보호하나(DTLS-SRTP). RFC 8825 가 개괄하는 WebRTC 는 뒤의 둘은 정했지만 첫째는 일부러 비워 두었습니다. JSEP(RFC 9429) 는 브라우저가 SDP 제안과 응답을 만들어 주는 방법을 정할 뿐, 그것을 상대에게 어떻게 건넬지는 애플리케이션에 맡깁니다. 그래서 거의 모든 서비스가 WebSocket 같은 작은 시그널링 서버를 따로 둡니다.

어떻게 동작하나

SDP 제안과 응답. 제안하는 쪽은 무엇을 보낼지(오디오 트랙, 데이터 채널)를 정한 뒤 createOffer 로 SDP 를 만듭니다. 보낼 것을 정하지 않으면 m= 줄이 없는 빈 제안이 됩니다. SDP 에는 매체마다 m= 줄, 코덱(a=rtpmap), ICE 자격(a=ice-ufrag·a=ice-pwd), DTLS 인증서의 지문(a=fingerprint), DTLS 역할(a=setup:actpass·active·passive), 그리고 후보(a=candidate)가 담깁니다. 응답하는 쪽은 받은 제안을 setRemoteDescription 에 넣고 createAnswer 로 답합니다.

ICE 후보. ICE(RFC 8445) 는 가능한 주소를 모두 모아(후보 수집) 짝을 지어 연결 확인을 해 보고, 되는 짝 가운데 하나를 고릅니다. 후보는 세 종류입니다.

종류 어디서 오나
host 자기 인터페이스 같은 망 안이면 이것으로 충분하다
srflx STUN 서버가 본 주소 NAT 가 바꿔 준 바깥 주소
relay TURN 서버가 빌려준 주소 직접 닿지 않을 때 서버가 대신 나른다

STUN(RFC 8489) 서버는 Binding 요청에 "내가 본 당신의 주소" 를 XOR-MAPPED-ADDRESS 로 돌려줄 뿐 데이터를 나르지 않습니다. 대칭 NAT 나 UDP 가 막힌 망에서는 그 주소로도 닿지 않으므로 TURN(RFC 8656) 서버가 중계 주소를 빌려주고 모든 데이터를 대신 나릅니다. TURN 은 대역폭 비용이 들지만, 없으면 일부 사용자는 아예 연결되지 않습니다. UDP 가 막힌 곳을 위해 TCP·TLS 443 으로도 TURN 을 엽니다. 후보를 다 모은 뒤에 SDP 를 보내는 방식과, 모이는 대로 따로 보내는 trickle ICE 가 있습니다. 실습의 aiortc 는 앞의 방식이라, 닿지 않는 STUN 서버를 설정하면 그 응답을 기다리느라 제안이 몇 초 늦어집니다.

DTLS-SRTP 와 SCTP. 길이 정해지면 그 위에서 DTLS 핸드셰이크를 합니다. 상대가 보여 준 인증서의 해시가 시그널링으로 받은 a=fingerprint 와 같아야 하고, 이것이 중간자 공격을 막습니다. RFC 8827 은 모든 미디어를 암호화하도록 요구하고, 미디어 키는 DTLS 핸드셰이크에서 뽑아 SRTP 에 씁니다(RFC 5764). 데이터 채널은 DTLS 위의 SCTP 로 흐르고(RFC 8831), 채널마다 순서 보장 여부와 재전송 한도(maxRetransmits)나 수명(maxPacketLifeTime)을 따로 정할 수 있습니다. 음성 프레임처럼 20ms 뒤에는 쓸모없는 데이터는 순서 없이, 재전송 없이 보내는 편이 낫습니다.

미디어 트랙. 오디오는 RTP 로 흐릅니다. RFC 7874 는 WebRTC 가 반드시 구현할 오디오 코덱으로 Opus 와 G.711 을 정하고, RFC 7587 은 Opus 를 입력 표본율과 상관없이 SDP 에 opus/48000/2 로 적게 합니다.

현장에서 만나는 모습

지터와 손실. 음성은 재전송을 기다릴 여유가 없어서 받는 쪽이 지터 버퍼로 도착 간격의 흔들림을 흡수하고, 잃은 프레임은 앞뒤 소리로 메웁니다(손실 은닉). 버퍼가 크면 끊김은 줄지만 모든 소리가 그만큼 늦습니다. ITU-T G.114 는 한 방향 지연이 150ms 이하면 대부분의 대화에 무리가 없다고 봅니다. "회사 망에서만 연결이 안 된다" 는 대개 UDP 가 막혔는데 TURN 이 없거나 TURN 이 UDP 로만 열린 경우이고, "연결까지 5초" 는 닿지 않는 STUN 을 기다리는 경우입니다.

다음 실습에서 할 것

WebSocket 시그널링 서버를 만들고, aiortc 로 응답 피어와 제안 피어를 만들어 기준 피어와 데이터 채널로 메시지를 주고받습니다. 채널을 순서 없고 재전송 없는 것으로 바꾸고, 같은 파드 안의 STUN 서버로 srflx 후보가 생기는 모습과 막힌 STUN 이 수집을 늦추는 모습을 잽니다.