게시일: 2013년 11월 4일
WebRTC는 P2P 통신을 지원합니다. 클라이언트가 메타데이터를 교환하고 시그널링이라는 프로세스를 통해 통신을 조정할 수 있도록 서버가 필요합니다. 또한 서버는 클라이언트가 네트워크 주소 변환기 (NAT) 및 방화벽을 처리하는 데 도움이 됩니다.
여기에서는 신호 서비스를 빌드하고 STUN 및 TURN 서버를 사용하여 실제 연결의 특이점을 처리하는 방법을 알아봅니다. 또한 WebRTC 앱이 다자간 통화를 처리하고 VoIP 및 PSTN (전화라고도 함)과 같은 서비스와 상호작용하는 방법도 이해할 수 있습니다.
신호란 무엇인가요?
시그널링은 통신을 조정하는 프로세스입니다. WebRTC 앱이 통화를 설정하려면 클라이언트가 다음 정보를 교환해야 합니다.
- 통신을 열거나 닫는 데 사용되는 세션 제어 메시지
- 오류 메시지
- 미디어 메타데이터(예: 코덱, 코덱 설정, 대역폭, 미디어 유형)
- 보안 연결을 설정하는 데 사용되는 키 데이터
- 외부에서 볼 수 있는 호스트의 IP 주소 및 포트와 같은 네트워크 데이터
이 신호 프로세스에는 클라이언트가 메시지를 주고받을 수 있는 방법이 필요합니다. 이 메커니즘은 WebRTC API에서 구현되지 않습니다. 대신 직접 빌드해야 합니다.
WebRTC에서 시그널링이 정의되지 않는 이유는 무엇인가요?
중복을 방지하고 기존 기술과의 호환성을 극대화하기 위해 신호 방식과 프로토콜은 WebRTC 표준에 지정되어 있지 않습니다. 이 접근 방식은 JavaScript 세션 설정 프로토콜 (JSEP)에 설명되어 있습니다.
JSEP 아키텍처는 브라우저가 상태를 저장하지 않아도 되도록, 즉 신호 상태 머신으로 작동하지 않아도 되도록 합니다. 예를 들어 페이지가 새로고침될 때마다 신호 데이터가 손실되면 문제가 발생할 수 있습니다. 대신 신호 상태를 서버에 저장할 수 있습니다.
JSEP는 피어 간에 미디어 메타데이터인 오퍼 및 응답을 교환해야 합니다. 제안 및 답변은 다음과 같은 세션 설명 프로토콜(SDP) 형식으로 전달됩니다.
v=0
o=- 7614219274584779017 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=msid-semantic: WMS
m=audio 1 RTP/SAVPF 111 103 104 0 8 107 106 105 13 126
c=IN IP4 0.0.0.0
a=rtcp:1 IN IP4 0.0.0.0
a=ice-ufrag:W2TGCZw2NZHuwlnf
a=ice-pwd:xdQEccP40E+P0L5qTyzDgfmW
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=mid:audio
a=rtcp-mux
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:9c1AHz27dZ9xPI91YNfSlI67/EMkjHHIHORiClQe
a=rtpmap:111 opus/48000/2
...
이 모든 SDP 전문 용어가 실제로 무엇을 의미하는지 알고 싶으신가요? 인터넷 엔지니어링 태스크포스 (IETF) 예시를 살펴보세요.
WebRTC는 SDP 텍스트의 값을 수정하여 로컬 또는 원격 설명으로 설정하기 전에 제안 또는 응답을 조정할 수 있도록 설계되었습니다. 예를 들어 appr.tc의 preferAudioCodec() 함수를 사용하여 기본 코덱과 비트 전송률을 설정할 수 있습니다. SDP는 JavaScript로 조작하기가 다소 까다로우며 향후 WebRTC 버전에서 대신 JSON을 사용해야 하는지에 관한 논의가 있지만 SDP를 계속 사용하는 데는 몇 가지 장점이 있습니다.
RTCPeerConnection API 및 시그널링: Offer, Answer, Candidate
RTCPeerConnection는 WebRTC 앱에서 피어 간 연결을 만들고 오디오 및 동영상을 전달하는 데 사용하는 API입니다.
이 프로세스를 초기화하기 위해 RTCPeerConnection에는 두 가지 태스크가 있습니다.
- 해상도 및 코덱 기능과 같은 로컬 미디어 조건을 확인합니다. 이는 제안 및 답변 메커니즘에 사용되는 메타데이터입니다.
- 앱 호스트의 잠재적인 네트워크 주소(후보라고 함)를 가져옵니다.
이 로컬 데이터가 확인되면 신호 메커니즘을 통해 원격 피어와 교환해야 합니다.
Alice가 Eve에게 전화를 걸려고 한다고 가정해 보겠습니다. 다음은 전체 응답 메커니즘을 자세히 설명한 것입니다.
- 앨리스가
RTCPeerConnection객체를 만듭니다. - 앨리스는
RTCPeerConnectioncreateOffer()메서드를 사용하여 제안 (SDP 세션 설명)을 만듭니다. - 앨리스는
setLocalDescription()를 호출하여 오퍼를 전달합니다. - 앨리스는 제안을 문자열화하고 신호 메커니즘을 사용하여 이브에게 전송합니다.
- 이브는 앨리스의 제안으로
setRemoteDescription()를 호출하여RTCPeerConnection가 앨리스의 설정을 알 수 있도록 합니다. - 이브는
createAnswer()를 호출하고 이에 대한 성공 콜백은 로컬 세션 설명인 이브의 응답을 전달합니다. - 이브는
setLocalDescription()를 호출하여 자신의 답변을 로컬 설명으로 설정합니다. - 그런 다음 이브는 신호 메커니즘을 사용하여 문자열화된 답변을 앨리스에게 보냅니다.
- 앨리스는
setRemoteDescription()를 사용하여 이브의 응답을 원격 세션 설명으로 설정합니다.
앨리스와 이브도 네트워크 정보를 교환해야 합니다. '후보 찾기'라는 표현은 ICE 프레임워크를 사용하여 네트워크 인터페이스와 포트를 찾는 프로세스를 의미합니다.
- 앨리스는
onicecandidate핸들러를 사용하여RTCPeerConnection객체를 만듭니다. - 네트워크 후보를 사용할 수 있게 되면 핸들러가 호출됩니다.
- 핸들러에서 Alice는 시그널링 채널을 통해 문자열화된 후보 데이터를 Eve에게 전송합니다.
- 이브는 앨리스로부터 후보 메시지를 받으면
addIceCandidate()를 호출하여 후보를 원격 피어 설명에 추가합니다.
JSEP는 ICE 후보 트리클링을 지원합니다. 이를 통해 발신자는 초기 제안 후 수신자에게 후보를 점진적으로 제공할 수 있으며, 수신자는 모든 후보가 도착할 때까지 기다리지 않고 통화에 대한 조치를 시작하고 연결을 설정할 수 있습니다.
신호용 WebRTC 코드 작성
다음 코드 스니펫은 전체 신호 프로세스를 요약하는 W3C 코드 예입니다. 이 코드는 일부 신호 메커니즘 SignalingChannel이 있다고 가정합니다. 신호는 뒷부분에서 자세히 설명합니다.
// handles JSON.stringify/parse
const signaling = new SignalingChannel();
const constraints = {audio: true, video: true};
const configuration = {iceServers: [{urls: 'stun:stun.example.org'}]};
const pc = new RTCPeerConnection(configuration);
// Send any ice candidates to the other peer.
pc.onicecandidate = ({candidate}) => signaling.send({candidate});
// Let the "negotiationneeded" event trigger offer generation.
pc.onnegotiationneeded = async () => {
try {
await pc.setLocalDescription(await pc.createOffer());
// send the offer to the other peer
signaling.send({desc: pc.localDescription});
} catch (err) {
console.error(err);
}
};
// After remote track media arrives, show it in remote video element.
pc.ontrack = (event) => {
// Don't set srcObject again if it is already set.
if (remoteView.srcObject) return;
remoteView.srcObject = event.streams[0];
};
// Call start() to initiate.
async function start() {
try {
// Get local stream, show it in self-view, and add it to be sent.
const stream =
await navigator.mediaDevices.getUserMedia(constraints);
stream.getTracks().forEach((track) =>
pc.addTrack(track, stream));
selfView.srcObject = stream;
} catch (err) {
console.error(err);
}
}
signaling.onmessage = async ({desc, candidate}) => {
try {
if (desc) {
// If you get an offer, you need to reply with an answer.
if (desc.type === 'offer') {
await pc.setRemoteDescription(desc);
const stream =
await navigator.mediaDevices.getUserMedia(constraints);
stream.getTracks().forEach((track) =>
pc.addTrack(track, stream));
await pc.setLocalDescription(await pc.createAnswer());
signaling.send({desc: pc.localDescription});
} else if (desc.type === 'answer') {
await pc.setRemoteDescription(desc);
} else {
console.log('Unsupported SDP type.');
}
} else if (candidate) {
await pc.addIceCandidate(candidate);
}
} catch (err) {
console.error(err);
}
};
제안-응답 및 후보 교환 프로세스를 실제로 보려면 simpl.info RTCPeerConnection을 방문하여 단일 페이지 영상 채팅 예시의 콘솔 로그를 확인하세요. Chrome의 about://webrtc-internals 페이지 또는 Opera의 opera://webrtc-internals 페이지에서 WebRTC 신호 및 통계의 전체 덤프를 다운로드할 수 있습니다.
피어 검색
이는 "대화할 사람을 어떻게 찾나요?"라는 질문을 멋지게 표현한 것입니다.
전화 통화의 경우 전화번호와 디렉터리가 있습니다. 온라인 영상 채팅 및 메시징의 경우 ID 및 상태 관리 시스템과 사용자가 세션을 시작할 수 있는 수단이 필요합니다. WebRTC 앱에는 클라이언트가 통화를 시작하거나 참여하고 싶다는 신호를 서로에게 보낼 수 있는 방법이 필요합니다.
피어 검색 메커니즘은 WebRTC에서 정의되지 않으며 여기서는 옵션을 다루지 않습니다. URL을 이메일이나 메시지로 보내는 것만큼 간단한 프로세스일 수도 있습니다. Talky, tawk.to, Browser Meeting과 같은 영상 채팅 앱의 경우 맞춤 링크를 공유하여 통화에 사용자를 초대합니다. 개발자 크리스 볼은 WebRTC 통화 참여자가 IM, 이메일, 비둘기 등 원하는 메시지 서비스를 통해 메타데이터를 교환할 수 있는 흥미로운 serverless-webrtc 실험을 만들었습니다.
신호 서비스를 빌드하려면 어떻게 해야 하나요?
다시 말하지만, 시그널링 프로토콜과 메커니즘은 WebRTC 표준에 의해 정의되지 않습니다. 어떤 방식을 선택하든 클라이언트 간에 신호 메시지와 앱 데이터를 교환하려면 중간 서버가 필요합니다. 안타깝게도 웹 앱은 인터넷에 "내 친구에게 연결해 줘!"라고 외칠 수 없습니다.
다행히 신호 메시지는 작고 대부분 통화 시작 시 교환됩니다. 영상 채팅 세션의 appr.tc를 사용한 테스트에서 총 약 30~45개의 메시지가 신호 서비스에 의해 처리되었으며 모든 메시지의 총 크기는 약 10KB였습니다.
WebRTC 신호 서비스는 대역폭 측면에서 비교적 요구사항이 적을 뿐만 아니라 메시지를 릴레이하고 연결된 클라이언트와 같은 소량의 세션 상태 데이터만 유지하면 되므로 처리 또는 메모리를 많이 사용하지 않습니다.
서버에서 클라이언트로 메시지 푸시
신호용 메시지 서비스는 클라이언트-서버 및 서버-클라이언트의 양방향이어야 합니다. 양방향 통신은 HTTP 클라이언트/서버 요청/응답 모델에 반하지만, 웹 서버에서 실행되는 서비스의 데이터를 브라우저에서 실행되는 웹 앱으로 푸시하기 위해 수년에 걸쳐 롱 폴링과 같은 다양한 해킹이 개발되었습니다.
EventSource API는 서버 전송 이벤트, 즉 HTTP를 통해 웹 서버에서 브라우저 클라이언트로 전송되는 데이터를 사용 설정합니다. EventSource는 단방향 메시징을 위해 설계되었지만 XHR과 함께 사용하여 신호 메시지를 교환하는 서비스를 빌드할 수 있습니다. 신호 서비스는 XHR 요청으로 전달된 발신자의 메시지를 EventSource를 통해 수신자에게 푸시하여 전달합니다.
WebSocket은 전이중 클라이언트-서버 통신을 위해 설계되었습니다. 즉, 메시지가 동시에 양방향으로 흐를 수 있습니다. 순수 WebSocket 또는 서버 전송 이벤트 (EventSource)로 빌드된 신호 서비스의 한 가지 장점은 이러한 API의 백엔드를 PHP, Python, Ruby와 같은 언어의 대부분의 웹 호스팅 패키지에 공통적인 다양한 웹 프레임워크에서 구현할 수 있다는 것입니다.
WebRTC를 지원하는 모든 브라우저는 WebSocket도 지원합니다. 모든 연결에 TLS를 사용하여 메시지가 암호화되지 않은 상태로 가로채이지 않도록 하고 프록시 순회 문제를 줄여야 합니다. WebSocket 및 프록시 순회에 대한 자세한 내용은 Ilya Grigorik의 고성능 브라우저 네트워킹에서 WebRTC 장을 참조하세요.
WebRTC 클라이언트가 Ajax를 통해 메시징 서버를 반복적으로 폴링하도록 하여 신호 처리를 처리할 수 있지만, 이로 인해 중복 네트워크 요청이 많이 발생하며, 이는 특히 휴대기기에서 문제가 됩니다. 세션이 설정된 후에도 피어는 다른 피어의 변경사항이나 세션 종료가 있는 경우 신호 메시지를 폴링해야 합니다. WebRTC Book 앱 예시에서는 폴링 빈도에 대한 몇 가지 최적화와 함께 이 옵션을 사용합니다.
확장 신호
시그널링 서비스는 클라이언트당 비교적 적은 대역폭과 CPU를 사용하지만, 인기 있는 앱의 시그널링 서버는 동시 실행 수준이 높은 여러 위치에서 많은 메시지를 처리해야 할 수 있습니다. 트래픽이 많은 WebRTC 앱에는 상당한 부하를 처리할 수 있는 신호 서버가 필요합니다. 여기서는 자세히 설명하지 않지만 대용량 고성능 메시징을 위한 여러 옵션이 있습니다.
eXtensible Messaging and Presence Protocol(XMPP)은 신호 전송에 사용할 수 있는 채팅용으로 개발된 프로토콜입니다. 서버 구현에는 ejabberd 및 Openfire가 포함됩니다. Strophe.js와 같은 JavaScript 클라이언트는 BOSH를 사용하여 양방향 스트리밍을 에뮬레이션하지만 BOSH는 WebSocket에 비해 비효율적이고 확장성이 떨어질 수 있습니다.
ZeroMQ (TokBox에서 Rumour 서비스에 사용) 및 OpenMQ(NullMQ는 WebSocket을 통해 STOMP 프로토콜을 사용하여 웹 플랫폼에 ZeroMQ 개념을 적용)와 같은 오픈소스 라이브러리
WebSocket을 사용하는 상용 클라우드 메시징 플랫폼(긴 폴링으로 대체될 수 있음)(예: Pusher 및 Kaazing)
상용 WebRTC 플랫폼(예: vLine)
Node에서 Socket.io로 신호 서비스 빌드
다음은 Node에서 Socket.io로 빌드된 신호 서비스를 사용하는 웹 앱의 코드입니다. Socket.io의 설계는 메시지를 교환하는 서비스를 더 쉽게 빌드할 수 있게 해주며, Socket.io는 내장된 방 개념으로 인해 WebRTC 신호에 특히 적합합니다. 이 예시는 프로덕션 용도가 아닌 비교적 적은 수의 사용자를 위해 설계되었습니다.
Socket.io는 AJAX 긴 폴링, AJAX 멀티파트 스트리밍, Forever Iframe, JSONP 폴링과 같은 대체와 함께 WebSocket을 사용합니다. 다양한 백엔드로 포팅되었지만 이 예시에서 사용된 Node 버전으로 가장 잘 알려져 있습니다.
이 예시에는 WebRTC가 없습니다. 웹 앱에 신호 기능을 빌드하는 방법을 보여주기 위해 설계되었습니다. 콘솔 로그를 확인하여 클라이언트가 방에 참여하고 메시지를 교환할 때 어떤 일이 발생하는지 확인하세요. 이 WebRTC Codelab에서는 이를 완전한 WebRTC 영상 채팅 앱에 통합하는 방법을 단계별로 안내합니다.
index.html 클라이언트는 다음과 같습니다.
<!DOCTYPE html>
<html>
<head>
<title>WebRTC client</title>
</head>
<body>
<script src='/socket.io/socket.io.js'></script>
<script src='js/main.js'></script>
</body>
</html>
다음은 클라이언트에서 참조되는 JavaScript 파일 main.js입니다.
const isInitiator;
room = prompt('Enter room name:');
const socket = io.connect();
if (room !== '') {
console.log('Joining room ' + room);
socket.emit('create or join', room);
}
socket.on('full', (room) => {
console.log('Room ' + room + ' is full');
});
socket.on('empty', (room) => {
isInitiator = true;
console.log('Room ' + room + ' is empty');
});
socket.on('join', (room) => {
console.log('Making request to join room ' + room);
console.log('You are the initiator!');
});
socket.on('log', (array) => {
console.log.apply(console, array);
});
완성된 서버 앱은 다음과 같습니다.
const static = require('node-static');
const http = require('http');
const file = new(static.Server)();
const app = http.createServer(function (req, res) {
file.serve(req, res);
}).listen(2013);
const io = require('socket.io').listen(app);
io.sockets.on('connection', (socket) => {
// Convenience function to log server messages to the client
function log(){
const array = ['>>> Message from server: '];
for (const i = 0; i < arguments.length; i++) {
array.push(arguments[i]);
}
socket.emit('log', array);
}
socket.on('message', (message) => {
log('Got message:', message);
// For a real app, would be room only (not broadcast)
socket.broadcast.emit('message', message);
});
socket.on('create or join', (room) => {
const numClients = io.sockets.clients(room).length;
log('Room ' + room + ' has ' + numClients + ' client(s)');
log('Request to create or join room ' + room);
if (numClients === 0){
socket.join(room);
socket.emit('created', room);
} else if (numClients === 1) {
io.sockets.in(room).emit('join', room);
socket.join(room);
socket.emit('joined', room);
} else { // max two clients
socket.emit('full', room);
}
socket.emit('emit(): client ' + socket.id +
' joined room ' + room);
socket.broadcast.emit('broadcast(): client ' + socket.id +
' joined room ' + room);
});
});
localhost에서 이 앱을 실행하려면 Node.js, Socket.IO, node-static이 있어야 합니다. Socket.IO 및 node-static을 설치하려면 앱 디렉터리의 터미널에서 npm을 실행합니다.
npm install socket.io
npm install node-static
서버를 시작하려면 앱 디렉터리의 터미널에서 다음 명령어를 실행합니다.
node server.js
브라우저에서 localhost:2013를 엽니다. 브라우저에서 새 탭 또는 창을 열고 localhost:2013을 다시 엽니다. 무슨 일이 일어나고 있는지 확인하려면 콘솔을 확인하세요. Chrome 및 Opera에서는 Ctrl+Shift+J (Mac의 경우 Command+Option+J)를 사용하여 Chrome 개발자 도구를 통해 콘솔에 액세스할 수 있습니다.
신호에 어떤 접근 방식을 선택하든 백엔드 및 클라이언트 앱은 이 예시와 유사한 서비스를 제공해야 합니다.
신호 관련 주의사항
setLocalDescription()가 호출될 때까지RTCPeerConnection는 후보 수집을 시작하지 않습니다. 이는 JSEP IETF 초안에 규정되어 있습니다.- Trickle ICE를 활용합니다. 후보가 도착하는 즉시
addIceCandidate()를 호출합니다.
사전 제작된 신호 서버
자체적으로 구현하지 않으려면 이전 예시와 같이 Socket.IO를 사용하고 WebRTC 클라이언트 JavaScript 라이브러리와 통합된 여러 WebRTC 신호 서버를 사용할 수 있습니다.
- webRTC.io는 WebRTC의 첫 번째 추상화 라이브러리 중 하나입니다.
- Signalmaster는 SimpleWebRTC JavaScript 클라이언트 라이브러리와 함께 사용하기 위해 만든 신호 서버입니다.
코드를 전혀 작성하지 않으려면 vLine, OpenTok, Asterisk와 같은 회사에서 제공하는 완전한 상용 WebRTC 플랫폼을 사용할 수 있습니다.
참고로 Ericsson은 WebRTC 초기에 Apache에서 PHP를 사용하여 신호 서버를 빌드했습니다. 지금은 다소 구식이지만 비슷한 것을 고려하고 있다면 코드를 살펴볼 가치가 있습니다.
Signal 보안
암호화는 모든 WebRTC 구성요소에 필수입니다.
그러나 신호 메커니즘은 WebRTC 표준에 의해 정의되지 않으므로 신호를 안전하게 만드는 것은 개발자의 몫입니다. 공격자가 신호 가로채기에 성공하면 세션을 중지하고, 연결을 리디렉션하고, 콘텐츠를 기록, 변경 또는 삽입할 수 있습니다.
보안 처리된 신호 보안에서 가장 중요한 요소는 보안 프로토콜을 사용하는 것입니다. HTTPS 및 WSS(예: TLS) 이렇게 하면 메시지가 암호화되지 않은 상태로 가로채기되지 않습니다. 또한 동일한 신호 서버를 사용하는 다른 발신자가 액세스할 수 있는 방식으로 신호 메시지를 브로드캐스트하지 않도록 주의하세요.
NAT 및 방화벽 처리
메타데이터 신호의 경우 WebRTC 앱은 중개 서버를 사용하지만, 세션이 설정된 후 실제 미디어 및 데이터 스트리밍의 경우 RTCPeerConnection는 클라이언트를 직접 또는 피어 투 피어로 연결하려고 시도합니다.
더 단순한 세상에서는 모든 WebRTC 엔드포인트가 직접 통신하기 위해 다른 피어와 교환할 수 있는 고유한 주소를 갖게 됩니다.
실제로 대부분의 기기는 하나 이상의 NAT 레이어 뒤에 있고, 일부 기기에는 특정 포트와 프로토콜을 차단하는 바이러스 백신 소프트웨어가 있으며, 많은 기기가 프록시와 회사 방화벽 뒤에 있습니다. 방화벽과 NAT는 실제로 가정용 Wi-Fi 라우터와 같은 동일한 기기에서 구현될 수 있습니다.
WebRTC 앱은 ICE 프레임워크를 사용하여 실제 네트워킹의 복잡성을 극복할 수 있습니다. 이 기능을 사용 설정하려면 앱에서 ICE 서버 URL을 RTCPeerConnection에 전달해야 합니다.
ICE는 피어를 연결하는 가장 좋은 경로를 찾으려고 시도합니다. 모든 가능성을 동시에 시도하고 작동하는 가장 효율적인 옵션을 선택합니다. ICE는 먼저 기기의 운영체제 및 네트워크 카드에서 가져온 호스트 주소를 사용하여 연결을 시도합니다. 이 작업이 실패하면 (NAT 뒤에 있는 기기의 경우 실패함) ICE는 STUN 서버를 사용하여 외부 주소를 가져오고, 이 작업이 실패하면 트래픽이 TURN 릴레이 서버를 통해 라우팅됩니다.
즉, STUN 서버는 외부 네트워크 주소를 가져오는 데 사용되고 TURN 서버는 직접 (피어 투 피어) 연결이 실패할 경우 트래픽을 릴레이하는 데 사용됩니다.
모든 TURN 서버는 STUN을 지원합니다. TURN 서버는 추가적인 중계 기능이 내장된 STUN 서버입니다. ICE는 NAT 설정의 복잡성도 처리합니다. 실제로 NAT 홀 펀칭에는 공개 IP:포트 주소 이상의 것이 필요할 수 있습니다.
STUN 및 TURN 서버의 URL은 RTCPeerConnection 생성자의 첫 번째 인수인 iceServers 구성 객체에서 WebRTC 앱에 의해 (선택적으로) 지정됩니다. appr.tc의 경우 이 값은 다음과 같습니다.
{
'iceServers': [
{
'urls': 'stun:stun.l.google.com:19302'
},
{
'urls': 'turn:192.158.29.39:3478?transport=udp',
'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
'username': '28224511:1379330808'
},
{
'urls': 'turn:192.158.29.39:3478?transport=tcp',
'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
'username': '28224511:1379330808'
}
]
}
RTCPeerConnection에 이 정보가 있으면 ICE 마법이 자동으로 실행됩니다. RTCPeerConnection는 ICE 프레임워크를 사용하여 피어 간의 최적의 경로를 찾고, 필요에 따라 STUN 및 TURN 서버와 함께 작동합니다.
STUN
NAT는 비공개 로컬 네트워크 내에서 사용할 IP 주소를 기기에 제공하지만 이 주소는 외부에서 사용할 수 없습니다. 공개 주소가 없으면 WebRTC 피어가 통신할 방법이 없습니다. 이 문제를 해결하기 위해 WebRTC는 STUN을 사용합니다.
STUN 서버는 공개 인터넷에 상주하며, NAT 뒤에서 실행되는 앱에서 들어오는 요청의 IP:포트 주소를 확인하고 해당 주소를 응답으로 다시 전송하는 한 가지 작업을 수행합니다. 즉, 앱은 STUN 서버를 사용하여 공개 관점에서 IP:포트를 검색합니다. 이 프로세스를 통해 WebRTC 피어는 공개적으로 액세스 가능한 주소를 가져온 다음 신호 메커니즘을 통해 다른 피어에 전달하여 직접 링크를 설정할 수 있습니다.
STUN 서버는 많은 작업을 수행하거나 기억할 필요가 없으므로 비교적 낮은 사양의 STUN 서버가 많은 요청을 처리할 수 있습니다.
대부분의 WebRTC 통화는 STUN을 사용하여 성공적으로 연결되지만 방화벽 및 복잡한 NAT 구성 뒤에 있는 피어 간의 통화는 더 적을 수 있습니다.
TURN
RTCPeerConnection는 UDP를 통해 피어 간에 직접 통신을 설정하려고 시도합니다.
이 방법이 실패하면 RTCPeerConnection는 TCP를 사용합니다. 실패하면 TURN 서버를 대체로 사용하여 엔드포인트 간에 데이터를 중계할 수 있습니다.
다시 한번 말씀드리지만, TURN은 피어 간에 오디오, 동영상, 데이터 스트리밍을 중계하는 데 사용되며 신호 데이터는 중계하지 않습니다.
TURN 서버에는 공개 주소가 있으므로 피어가 방화벽이나 프록시 뒤에 있더라도 피어에서 연결할 수 있습니다. TURN 서버에는 스트림을 중계하는 한 가지 작업이 있습니다. STUN 서버와 달리 본질적으로 많은 대역폭을 소비합니다.
이 다이어그램은 TURN의 작동 방식을 보여줍니다. 순수 STUN이 성공하지 못했으므로 각 피어는 TURN 서버를 사용합니다.
STUN 및 TURN 서버 배포
테스트를 위해 Google은 appr.tc에서 사용하는 공개 STUN 서버 stun.l.google.com:19302를 실행합니다. 프로덕션 STUN/TURN 서비스의 경우 rfc5766-turn-server를 사용합니다. STUN 및 TURN 서버의 소스 코드는 GitHub에서 제공되며, 여기에서 서버 설치에 관한 여러 정보 소스 링크도 확인할 수 있습니다. Amazon Web Services용 VM 이미지도 사용할 수 있습니다.
대체 TURN 서버는 restund이며 소스 코드 및 AWS에서도 사용할 수 있습니다. Compute에서 restund를 설정할 수 있습니다.
- tcp=443, udp/tcp=3478에 필요한 방화벽을 엽니다.
- 공개 IP, 표준 Ubuntu 12.06 이미지마다 하나씩 인스턴스 4개를 만듭니다.
- 로컬 방화벽 구성을 설정합니다 (모든 소스에서 모든 트래픽 허용).
- 도구를 설치합니다.
shell sudo apt-get install make sudo apt-get install gcc - creytiv.com/re.html에서 libre를 설치합니다.
- creytiv.com/restund.html에서 restund를 가져와 압축을 풉니다.
wgethancke.name/restund-auth.patch를 다운로드하고patch -p1 < restund-auth.patch로 적용합니다.- libre 및 restund에 대해
make,sudo make install를 실행합니다. restund.conf를 필요에 맞게 조정하고 (IP 주소를 바꾸고 동일한 공유 비밀번호가 포함되어 있는지 확인)/etc에 복사합니다.restund/etc/restund을/etc/init.d/로 복사합니다.- restund를 구성합니다.
LD_LIBRARY_PATH을 설정합니다.restund.conf을/etc/restund.conf로 복사합니다.restund.conf가 오른쪽 10을 사용하도록 설정합니다. 포드에 새 IP 주소가 할당됩니다
- restund 실행
- 원격 머신에서 stund 클라이언트를 사용하여 테스트:
./client IP:port
다자간 WebRTC
저스틴 우베르티가 제안한 TURN 서비스 액세스를 위한 REST API에 관한 IETF 표준도 참고하세요.
미디어 스트리밍에는 일대일 통화 외에도 다양한 사용 사례가 있습니다. 예를 들어 동료 그룹 간의 화상 회의 또는 한 명의 발표자와 수백 명 또는 수백만 명의 시청자가 참여하는 공개 이벤트가 있습니다.
WebRTC 앱은 여러 RTCPeerConnection을 사용하여 모든 엔드포인트가 메시 구성의 다른 모든 엔드포인트에 연결되도록 할 수 있습니다. 이것은 talky.io와 같은 앱에서 사용하는 접근 방식이며, 소수의 피어에 매우 잘 작동합니다. 그 이상은 특히 모바일 클라이언트의 경우 처리 및 대역폭 소비가 과도해집니다.
또는 WebRTC 앱이 하나의 엔드포인트를 선택하여 스타 구성의 다른 모든 엔드포인트에 스트림을 배포할 수 있습니다. 서버에서 WebRTC 엔드포인트를 실행하고 자체 재배포 메커니즘을 구성할 수도 있습니다(샘플 클라이언트 앱은 webrtc.org에서 제공).
한 RTCPeerConnection의 MediaStream를 다른
의 입력으로 사용할 수 있습니다. 이렇게 하면 웹 앱이 연결할 다른 피어를 선택하여 통화 라우팅을 처리할 수 있으므로 더 유연한 아키텍처를 사용할 수 있습니다. 실제 작동 모습을 보려면 WebRTC 샘플 피어 연결 릴레이 및 WebRTC 샘플 여러 피어 연결을 참고하세요.
다중 지점 제어 장치
엔드포인트가 많은 경우 더 나은 옵션은 다중 지점 제어 장치(MCU)를 사용하는 것입니다. 이는 많은 수의 참석자 간에 미디어를 배포하는 브리지 역할을 하는 서버입니다. MCU는 화상 회의에서 다양한 해상도, 코덱, 프레임 속도를 처리하고, 트랜스코딩을 처리하고, 선택적 스트림 전달을 수행하고, 오디오와 동영상을 혼합하거나 녹화할 수 있습니다. 다자간 통화의 경우 고려해야 할 문제가 여러 가지 있습니다. 특히 여러 동영상 입력을 표시하는 방법과 여러 소스의 오디오를 혼합하는 방법이 있습니다. vLine과 같은 클라우드 플랫폼도 트래픽 라우팅을 최적화하려고 시도합니다.
완전한 MCU 하드웨어 패키지를 구매하거나 직접 제작할 수 있습니다.
여러 오픈소스 MCU 소프트웨어 옵션을 사용할 수 있습니다. 예를 들어 Licode (이전 명칭: Lynckia)는 WebRTC용 오픈소스 MCU를 생성합니다. OpenTok에는 Mantis가 있습니다.
브라우저 외: VoIP, 전화, 메시지
WebRTC의 표준화된 특성 덕분에 브라우저에서 실행되는 WebRTC 앱과 전화 또는 화상 회의 시스템과 같은 다른 통신 플랫폼에서 실행되는 기기 또는 플랫폼 간에 통신을 설정할 수 있습니다.
SIP는 VoIP 및 화상 회의 시스템에서 사용하는 신호 프로토콜입니다. WebRTC 웹 앱과 SIP 클라이언트(예: 화상 회의 시스템) 간의 통신을 사용 설정하려면 WebRTC에 신호 처리를 중재하는 프록시 서버가 필요합니다. 신호는 게이트웨이를 통해 전달되어야 하지만 통신이 설정되면 SRTP 트래픽 (동영상 및 오디오)은 피어 투 피어로 전달될 수 있습니다.
공중 전화 교환망 (PSTN)은 모든 '기존' 아날로그 전화의 회선 교환 네트워크입니다. WebRTC 웹 앱과 전화 간의 통화의 경우 트래픽이 PSTN 게이트웨이를 통과해야 합니다. 마찬가지로 WebRTC 웹 앱은 IM 클라이언트와 같은 Jingle 엔드포인트와 통신하기 위해 중개 XMPP 서버가 필요합니다. Jingle은 메시지 서비스에 음성 및 동영상을 사용 설정하기 위해 Google에서 XMPP의 확장 프로그램으로 개발했습니다. 현재 WebRTC 구현은 Talk용으로 처음 개발된 Jingle 구현인 C++ libjingle 라이브러리를 기반으로 합니다.
많은 앱, 라이브러리, 플랫폼에서 외부 세계와 통신하는 WebRTC의 기능을 사용합니다. 예를 들면 다음과 같습니다.
자세히 알아보기
- WebRTC API 문서를 읽어보세요.
- WebRTC Codelab에서는 Node에서 실행되는 Socket.io 신호 서비스를 사용하여 동영상 및 텍스트 채팅 앱을 빌드하는 방법을 단계별로 안내합니다.
- 크리스 윌슨의 SFHTML5 프레젠테이션: WebRTC 앱 소개
350페이지 분량의 책 WebRTC: APIs and RTCWEB Protocols of the HTML5 실시간 웹에서는 데이터 및 신호 경로에 관한 많은 세부정보를 제공하며, 여러 가지 상세한 네트워크 토폴로지 다이어그램을 포함합니다.