WebRTC data channels

WebRTC data channels for high performance data exchange

HTML5 Rocks

커뮤니케이션, 게임, 혹은 파일 전송을 위한 두 브라우저 간의 데이터 전송은 상당히 복잡한 과정일 수 있습니다. 데이터를 중계(relay)하기 위해 서버를 설정하고 비용을 내야 하며, 아마도 이를 여러 데이터 센터로 확장할 필요가 있습니다. 이 시나리오는 높은 지연 시간(latency)의 가능성이 있고, 데이터를 비공개로 유지하는 것이 어렵습니다.

한 피어(peer)에서 다른 곳으로 직접 데이터를 전달하기 위해 WebRTC의 RTCDataChannel API를 사용하면 이러한 문제들을 완화할 수 있습니다. 이 글에서는 데이터 채널을 설정하고 사용하는 기본적인 방법뿐만 아니라 오늘날 웹에서의 일반적인 사용 사례(use cases)를 다룹니다.

이 글을 최대한 활용하려면 RTCPeerConnection API에 대한 지식과 STUN, TURN, 그리고 시그널링 작업에 대한 이해가 필요합니다. Getting Started With WebRTC를 살펴보는 것을 추천합니다.

왜 또 다른 채널인가요?

우리에게는 WebSocket, AJAX, 그리고 Server Sent Events가 있습니다. 왜 또 다른 커뮤니케이션 채널이 필요한가요? WebSocket은 양방향입니다. 하지만 이런 모든 기술은 서버로 혹은 서버로부터의 커뮤니케이션을 위해 디자인됩니다.

RTCDataChannel은 다른 접근법을 취합니다.

  • RTCPeerConnection API와 함께 동작하며, P2P(peer-to-peer) 연결을 가능하게 합니다. 이는 중개 서버가 없고 '홉(hops)'이 적어 지연 시간을 더 줄일 수 있습니다.
  • RTCDataChannel은 Stream Control Transmission Protocol (SCTP)을 사용하여, 비순차 전송(out-of-order delivery)이나 재전송 설정(retransmit configuration)과 같은 설정가능한 전송 시멘틱스(semantics, 역자주: 의미)를 허용합니다.

이제 RTCDataChannel은 데스크톱과 안드로이드의 크롬, 파이어폭스, 오페라에서 사용할 수 있으며 SCTP를 지원합니다.

경고: 시그널링, STUN과 TURN

비록 WebRTC가 P2P 커뮤니케이션을 가능하게 하지만 여전히 서버는 필요합니다.

  • 시그널링을 위해, 미디어와 네트워크 메타데이터를 교환할 수 있게 하여 피어 연결을 부트스트랩(Bootstrap, 역주: 초기 로딩)합니다.
  • 피어 사이의 가능한 최고의 네트워크 경로를 설정하려고 ICS 프레임워크를 사용하고, (각 피어의 공개적으로 접근 가능한 IP와 포트를 찾아내려고) STUN 서버와 (직접 연결이 실패하고 데이터 중계(relaying)가 필요하면) TURN 서버와 작업해서, NATs와 방화벽을 다룹니다.

HTML5 Rocks의 글 WebRTC in the real world: STUN, TURN, and signaling은 WebRTC가 시그널링과 네트워킹을 위해 서버와 작업하는 방법을 자세히 설명합니다.

특성

RTCDataChannel API는 유연한 데이터 타입 집합을 지원합니다. 이 API는 WebSocket을 완전히 모방하여 디자인했으며, RTCDataChannel은 문자열뿐만 아니라, Blob, ArrayBuffer, 그리고 ArrayBufferView와 같은 JavaScript의 바이너리 타입 일부를 지원합니다. 이 타입들을 파일 전송, 멀티플레이어 게임에서 사용하면 유용할 수 있습니다.

TCPUDPSCTP
신뢰성신뢰성 있음신뢰성 없음구성 가능
전달순차적비순차적구성 가능
전송바이트 순서메시지 기반메시지 기반
흐름 제어있음없음있음
혼잡 제어있음없음있음

(Ilya GrigorikHigh Performance Browser Networking에서)

RTCDataChannel은 (사용자 데이터그램 프로토콜(UDP)과 유사한) 신뢰성 없는 방식(mode)과 (전송 제어 프로토콜(TCP)과 유사한) 신뢰성 있는 방식 둘 중 하나로 동작할 수 있습니다. 두 방식은 간단한 차이가 있습니다.

  • 신뢰성 있는 방식은 메시지 전송과 메시지가 전달되는 순서를 보장합니다. 이것은 추가적인 오버헤드가 들어서, 잠재적으로 이 방식이 더 느리게 만듭니다.
  • 신뢰성 없는 방식은 모든 메시지가 상대편에 도달하는지와 어떤 순서로 도달하는지를 보장하지 않는다. 이는 오버헤드를 제거해서, 이 방식으로 동작하는 것이 훨씬 더 빠르게 합니다.

패킷 손실이 없을 때 두 방식의 성능은 거의 같습니다. 하지만 신뢰성 있는 방식에서 패킷 손실은 그 패킷 뒤의 다른 패킷을 막는 원인이 되고, 그 손실 패킷은 그것을 재전송하고 도착할 때까지 오래된(stale) 패킷일 수 있습니다. 물론 같은 애플리케이션에, 각각 자신의 신뢰할 수 있는(혹은 없는) 시멘틱스를 가진 다수의 데이터 채널을 사용할 수 있습니다.

아래에서 RTCDataChannel을 설정하여 신뢰성 있는 혹은 신뢰성 없는 방식을 사용하는 법을 살펴보겠습니다.

데이터 채널 설정하기

온라인에 몇 가지 간단한 RTCDataChannel 데모가 있습니다.

이 예제들에서 브라우저는 피어 연결을 자신에게 합니다. 그리고서 데이터 채널을 생성하고 피어 연결에 따라 메시지를 보냅니다. 결국 메시지가 페이지의 다른쪽 박스에 나타납니다!

이것을 시작하는 코드는 다음과 같이 짧습니다.

var peerConnection = new RTCPeerConnection();

// 여기에서 시그널링 채널을 사용하여 피어 연결을 설정합니다
var dataChannel =
  peerConnection.createDataChannel("myLabel", dataChannelOptions);

dataChannel.onerror = function (error) {
  console.log("Data Channel Error:", error);
};

dataChannel.onmessage = function (event) {
  console.log("Got Data Channel Message:", event.data);
};

dataChannel.onopen = function () {
  dataChannel.send("Hello World!");
};

dataChannel.onclose = function () {
  console.log("The Data Channel is Closed");
};

이미 설정한 피어 연결로부터 dataChannel 오브젝트를 생성합니다. 시그널링이 일어난 전후로 그 오브젝트를 생성할 수 있습니다. 그리고서 레이블을 전달해서 이 채널을 다른 것과 구분하고, 선택적인(optional) 환경 설정 세트는 다음과 같습니다.

var dataChannelOptions = {
  ordered: false, // 순서 보장 안함
  maxRetransmitTime: 3000, // 밀리초 단위
};

maxRetransmits 옵션(실패하기 전에 시도하는 횟수)을 추가할 수도 있을 뿐만 아니라, maxRetransmits 혹은 maxRetransmitTime 중 하나를 설정할 수도 있습니다. UDP 시멘틱스를 위해, maxRetransmits를 0으로, orderedfalse로 설정합니다. 더 많은 정보는 RFC 4960 (SCTP)와 RFC 3758 (SCTP partial reliability)에서 확인하세요.

  • ordered: 데이터 채널이 순서를 보장하는지 여부
  • maxRetransmitTime: 실패한 메시지의 재전송을 시도하는 최대 시간 (신뢰성 없는 방식으로 강제함)
  • maxRetransmits: 실패한 메시지의 재전송을 시도하는 최대 횟수 (신뢰성 없는 방식으로 강제함)
  • protocol: 서브프로토콜을 사용하는 것을 허락하지만, 명시한 프로토콜을 지원하지 않으면 실패
  • negotiated: true로 설정하면, 다른 피어의 데이터 채널의 자동 설정을 제거하는데 이는 다른 쪽에 같은 id로 데이터 채널을 생성하는 자신만의 방법을 제공하는 것임
  • id: 채널에 대한 자신만의 ID를 제공하는 것을 허락

대부분의 사람이 필요해서 사용할 옵션은 처음 세 가지인 ordered, maxRetransmitTime, 그리고 maxRetransmits뿐입니다. (이제 WebRTC를 지원하는 모든 브라우저에서 사용하는) SCTP를 사용하면 신뢰성과 순차성을 기본으로 설정합니다. 애플리케이션 계층(layer)에서 완전한 제어를 원한다면 신뢰성이 완전히 없는(fully unreliable) 것을 사용하는 것이 이해되지만, 대부분 경우에는 부분적으로 신뢰성 있는(partially reliable) 것이 유용합니다.

WebSocket을 사용할 때처럼 RTCDataChannel은 연결이 되거나, 끊기거나, 에러가 발생할 때와 다른 피어에서 메시지를 받았을 때 이벤트를 발생합니다.

안전한가요?

암호화(Encryption)는 모든 WebRTC 컴포넌트에 필수입니다. RTCDataChannel을 사용하면 모든 데이터가 데이터그램 전송 계층 보안 (Datagram Transport Layer Security, DTLS)으로 안전해집니다. DTLS는 SSL에서 파생한 것으로, 표준 SSL 기반 연결을 사용하는 것만큼 데이터가 안전하다는 것을 의미합니다. DTLS는 표준화된 것이며 WebRTC를 지원하는 모든 브라우저가 DTLS를 내장합니다. DTLS에 대한 더 많은 정보는 Wireshark wiki에서 확인할 수 있습니다.

데이터에 대해 생각하는 방식을 바꾸세요

많은 양의 데이터를 다루는 것은 JavaScript에서 골칫거리(pain point)일 수 있습니다. Sharefest의 개발자들이 지적하듯, 새로운 방식으로 데이터를 생각할 필요가 있습니다. 사용 가능한 메모리양보다 더 큰 파일을 전송하려면, 정보를 저장하는 새로운 방식을 생각해야 합니다. 아래에서 보여주는 것처럼 FileSystem API 같은 기술이 움직임을 시작할 때입니다.

파일 공유 애플리케이션을 만들어 보세요

브라우저에서 파일을 공유할 수 있는 웹 애플리케이션을 만드는 것이 이제 RTCDataChannel을 사용하면 가능합니다. RTCDataChannel 기반으로 만드는 것은 전송한 파일 데이터를 암호화하고, 애플리케이션 제공자의 서버를 건드리지 않는다는 것을 의미합니다. 이 기능은 더 신속한 공유를 위해 다수의 클라이언트와 연결할 가능성과 결합하여 WebRTC 파일 공유를 웹을 위한 강력한 후보로 만듭니다.

성공적인 전송을 하려면 여러 단계가 필요합니다.

  1. JavaScript에서 File API를 사용하여 파일을 읽습니다.
  2. 클라이언트 간 피어 연결을 하는데 RTCPeerConnection을 사용합니다.
  3. 클라이언트 간 데이터 채널을 생성하는데 RTCDataChannel을 사용합니다.

RTCDataChannel로 파일을 보내려고 시도할 때 고려할 여러 사항이 있습니다.

  • 파일 크기: 파일 크기가 충분히 작고 하나의 Blob으로 저장하고 로딩할 수 있으면, File API로 메모리에 로딩하고 신뢰성 있는 채널로 그 파일을 그대로 전달할 수 있습니다. (비록 브라우저가 최대 전송 크기에 대한 제한을 부여하고 있다는 것을 명심하더라도) 파일 크기가 커짐에 따라, 작업들은 좀 더 교묘해집니다. 청킹(chunking) 메커니즘이 필요합니다. 파일 청크(chunk)를 로딩하여 다른 피어에 보냅니다. chunkId 메타데이터를 동반하여 피어가 청크를 인식할 수 있습니다. 이 경우에 청크를 우선 (예를 들면 FileSystem API를 사용하여) 오프라인 저장소에 저장할 필요가 있고, 그 파일 전체를 갖게 되면 사용자 디스크에 저장합니다.
  • 속도: (UDP 같이) 신뢰성 없는 전송 혹은 (TCP 같이) 신뢰성 있는 전송이 파일 전송 작업에 더 나은지에 대해서는 논란의 여지가 있습니다. 애플리케이션이 일대일 파일 전송을 한다면, 신뢰성 없는 데이터 채널의 사용은 ACK/재전송 프로토콜을 위한 디자인 노력을 약간 필요로 합니다. 이것을 여러분 스스로 구현할 필요가 있으며, 여러분이 잘 만든다고 해도 신뢰성 있는 전송을 사용하는 것보다 훨씬 빠르지는 않습니다. 신뢰성 있고 비순차적인 것은 두 방면에서 최고를 제공해야 하지만, 결과는 멀티 파티(multi-party) (메쉬(mesh)) 파일 전송을 고려하면 상황에 따라 다를 수 있습니다.
  • 청크 크기: 청크는 애플리케이션의 데이터에서 가장 작은 '원자'입니다. (앞으로 나올 버전의 데이터 채널에서는 수정되겠지만) 현재 보내는 크기에 제한이 있기 때문에 청킹이 필요합니다. 현재 추천하는 최대 청크의 크기는 16KB입니다.

파일을 완전히 다른 쪽으로 전송하면, 앵커(anchor) 태그를 사용해서 다운로드할 수 있습니다.

function saveFile(blob) {
  var link = document.createElement('a');
  link.href = window.URL.createObjectURL(blob);
  link.download = 'File Name';
  link.click();
};

pubnub.github.io/rtc-pubnub-filesharegithub.com/Peer5/ShareFest의 파일 공유 앱은 이 기술을 사용합니다. 둘 다 오픈소스이며, RTCDataChannel에 기반을 둔 파일 공유 애플리케이션의 좋은 기초를 제공합니다.

그래서 무엇을 할 수 있을까요?

RTCDataChannel은 파일 공유, 멀티플레이어 게임, 콘텐츠 전송을 위한 앱을 만드는 새로운 방식으로의 문을 열어줍니다.

  • 위에서 서술한 것처럼 P2P 파일 공유
  • 모질라의 Banana Bread에서 보듯이, WebGL 같은 다른 기술과 연결한 멀티플레이어 게임
  • 콘텐츠 전송: 웹 리소스를 P2P 데이터 통신으로 전달하는 프레임워크 (PeerCDN가 재발명된 것처럼)

애플리케이션을 개발하는 방식을 바꾸세요

RTCDataChannel을 통해 낮은 지연 시간 연결이라는 고성능을 사용하여, 이제 더 매력있는 애플리케이션을 제공할 수 있습니다. PeerJSPubNub WebRTC SDK 같은 프레임워크는 RTCDataChannel의 구현을 더 쉽게 합니다. 그리고 이제 여러 플랫폼에서 그 API를 널리 지원합니다.

RTCDataChannel의 출현은 브라우저에서 데이터를 생각하는 방식을 바꿀 수 있습니다.

더 살펴보기

이 글을 준비하는데 도움을 준 Hadar Weiss and Shachar Zohar에게 감사드립니다.

Comments

0