Skip to content

Instantly share code, notes, and snippets.

@voluntas
Last active February 14, 2024 12:05
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save voluntas/80bd7ed7f483ee4c5b3673369f6ed9bd to your computer and use it in GitHub Desktop.
Save voluntas/80bd7ed7f483ee4c5b3673369f6ed9bd to your computer and use it in GitHub Desktop.
WebRTC DataChannel コトハジメ

WebRTC DataChannel コトハジメ

更新

2021-06-23

作者

@voluntas

バージョン

2021.11

URL

https://voluntas.github.io/

目的

WebRTC DataChannel は複雑すぎるため、資料がとても少ない。 実際に DataChannel を 1 から実装していることもあり資料としてまとめることにした。

この資料で学べること

最低限の WebRTC 知識は必要になるため、もし WebRTC をあまり知らない人は以下の資料を読んでから、こちらの資料を読んでほしい。

WebRTC DataChannel の紹介

基本

DataChannel とは WebRTC で音声と映像 以外 を送受信する仕組み。といってもどのようなデータでも送ることができるので DataChannel で音声や映像を送っても問題ない。

利用できるデータは文字列とバイナリの2つで、JavaScript 的には String と ArrayBuffer/Blob となる。これらを双受信することができる。

プロトコル的には UDP を利用しており、さらに暗号化のために DTLS を利用している。DTLS はデータグラム向け TLS だ。

つまり DataChannel も MediaChannel 同様、常に暗号化された状態でデータのやり取りが行われる。

UDP は到達保証や順序保証が一切ない。 そのため DataChannel では SCTP というプロトコルを DTLS 上に構築することにより、到達保証や順序保証を実現している。

SCTP はかなりマニアックなプロトコルで、UDP と TCP のいいとこ取りのプロトコルだ。ではなぜ SCTP をそのまま使わないのか。 それは一般家庭で利用されるブロードバンドルータなどの NAT 機能は知らないプロトコルを破棄してしまう。 つまりインターネットでは TCP と UDP のプロトコルしか利用できないためだ。

そこで UDP 上に DTLS で暗号化されたトンネルをつくり、その上で SCTP を実現している。 さらに DataChannel では SCTP 上で DataChannel 拡張を用意することで、 DataChannel 確立プロトコルを利用し SCTP のストリームと Label を結びつける仕組みやストリーム事の順番保証や再送を諦める仕組みを実現している。

DataChannel で実現できる事

DataChannel の魅力はなんと行っても全て暗号化された状態で、超低遅延でストリーム事に HoL Blocking しないデータを双方向でやりとりできることだ。

繰り返しになるが UDP 上でやり取りしているため到達保証や順序保証を SCTP を利用することで実現している。

順序保証はその名の通りだが、到達保証はつまりリトライをどこまで頑張るかという話になる。 DataChannel ではかなり細かいところまでリトライの仕組みを指定できる。この指定はストリーム事で指定できるため、 順序と信頼性を保証するストリームと保証しないストリームを一つの RTCPeerConnection 上に実現できる。

  • 絶対に諦めない、つまり TCP と同じ
  • 指定した回数だけはリトライする仕組み
  • 指定した時間だけはリトライする仕組み

つまり、リトライの諦めを時間と回数で指定できる。これらは排他的でどちらかしか有効にならない。

DataChannel で定義されているのは以下の 6 種類だ。

  • DATA_CHANNEL_RELIABLE
    • 信頼性: 保証する
    • 順序: 保証する
  • DATA_CHANNEL_RELIABLE_UNORDERED
    • 信頼性: 保証する
    • 順序: 保証しない
  • DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT
    • 信頼性: 部分的に保証する
    • 順序: 保証する
    • 指定回数内の再送
  • DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT_UNORDERED
    • 信頼性: 部分的に保証する
    • 順序: 保証しない
    • 指定回数内の再送
  • DATA_CHANNEL_PARTIAL_RELIABLE_TIMED
    • 信頼性: 部分的に保証する
    • 順序: 保証する
    • 指定時間内の再送
  • DATA_CHANNEL_PARTIAL_RELIABLE_TIMED_UNORDERED
    • 信頼性: 部分的に保証する
    • 順序: 保証しない
    • 指定時間内の再送

再送の回数や時間は用途に応じて自分で指定することができる。 これは DataChannel の Label ごとに指定可能なので、信頼性も順序も保証する DataChannel と、 1000 ミリ秒だけは再送するがあとはすぐ諦めて順序を保証しない DataChannel を 1 つの RTCPeerConnection で実現することができる。

DataChannel における SCTP

DataChannel は SCTP というプロトコルを UDP 上で実現している。 SCTP 自体はカーネルレイヤーでも実装されているプロトコルだがそれをユーザランドに持ってきている。 UDP 上で実現していることもあり、1 メッセージは 1200 バイト以下に抑えるという前提が必要になる。

また暗号化を前提としているため、 UDP の上に DTLS というデータグラム向け TLS を用意し、その上で SCTP を利用している。

DataChannel の SCTP は到達保証あり、順番保証ありのプロトコルのため、 再送回数や再送時間、順不同といった仕組みは SCTP 拡張を利用する事で実現している。

Data Channel Establishment Protocol

DataChannel は SCTP over DTLS over UDP で構成されているが、さらに SCTP 上に Data Channel Establishment Protocol を用意している。簡単にいってしまえばこの SCTP 上の上では DataChannel を利用しますよというハンドシェイクだ。仕組みは至ってシンプルで Open/Ack というハンドシェイクだけだ。

後は DATA チャンクに UTF-8 または Binary のペイロードを含めて送る仕組みになっている。ちなみに DATA チャンクは空っぽのデータを送ることはできないのでわざわざ STRING_EMPTY と BINARY_ENPTY というタイプが用意されている。

DataChannel における順不同

DataChannel で利用される SCTP 順番保証なプロトコルだが、順不同もチャンク単位で実現可能になっている。 DataChannel の順不同はこの順不同機能を利用している。

ordered

https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit-ordered

DataChannel における部分的な信頼性

DataChannel で利用されている SCTP は信頼性があるプロトコルである。そのため部分的な信頼性の仕組みは 相手に再送を諦めさせる ことで実現している。 SCTP は SACK の情報を利用することで再送を行っているが、 SCTP 拡張である Forward TSN を利用することで、 相手が保持している ここまでは受信した という情報を前方に移動させることで SACK に入ってくる情報を強制更新し再送を破棄する仕組み。

再送の仕組み自体は SCTP が提供しているが、再送回数や再送時間自体は DataChannel が独自に Forward TSN を利用して実現している。

maxRetransmits

https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit-maxretransmits

maxPacketLifeTime

https://www.w3.org/TR/webrtc/#dom-rtcdatachannelinit-maxpacketlifetime

DataChannel のサイズ

DataChannel では大きなサイズを送るには SCTP 拡張を利用する必要がある。 もともと SCTP 自体がシグナリング用途に作られており、大きなファイルを送る事を想定していなかったためである。

その方法が、通常の DATA チャンクを利用せず I-DATA チャンクを利用する仕組みだ。詳細については RFC 8260 - Stream Schedulers and User Message Interleaving for the Stream Control Transmission Protocol 日本語訳 を確認して欲しい。

そもそも SCTP が全てのメッセージを一つのキューで管理する仕組みになっているため、巨大なサイズのメッセージが来てしまうと、再送を無制限に行ってしまい、複数ストリームがあるにも関わらず HoL Blocking が発生してしまうという問題がある。

それを回避するために DATA チャンクではなく新しくフラグメントシーケンス番号とメッセージ ID を導入した I-DATA チャンクを用意して問題を解決している。

この機能に明確に対応しているのは Firefox のみとなっている。ただ libwebrtc の SCTP 実装は usrsctp から独自の dcsctp になり、今後は I-DATA チャンクへの対応を予定しているようだ。

DataChannel のクローズ処理

1 DataChannel は SCTP 上の 1 ストリームになる。SCTP にはこの SCTP ストリームを閉じるという仕組みがないため、SCTP 拡張である Reconfig を利用し、 指定したストリームをリセットするという仕組みを利用し、DataChannel のクローズを実現している。

ちなみにこのクローズ処理をせずに RTCPeerConnection を突然 close すると DataChannel の onerror で Transport channel closed というエラーが起きるので、 終了するときは必ず全ての DataChannel を終了させてからにするべきだ。

DataChannel の切断検知

DataChannel において切断検知は DTLS Close Notify 以外は一切対応していないので、異常終了に気付けない場合がある。

本来 SCTP であればエンドポイント障害検出を利用する事で相手側の切断を検知する仕組みがあるが、 DataChannel の SCTP では数分経過しても相手の切断に気付くことはない。

そのため「切断を検知できない」という前提で利用する必要がある。

DataChannel は今後どうなるのか

libwebrtc では usrsctp を使わず独自の dcsctp という実装が進められている。DataChannel は今まで脆弱性の対応が主だったが、ここに来て既存ライブラリを使わないという対応が進めている。

DataChannel の置き換えとして WebTransport が候補になる。 とはいえ DataChannel は WebRTC にべったりくっついたプロトコルのため、 既存のブラウザ全てで動作するし libwebrtc にも組み込まれている。

なにより SDP や ICE/TURN プロトコルとの組み合わせが前提とされているため、 WebRTC MediaChannel との相性も良い。

特に ICE/TURN プロトコルに対応しているため UDP が通らないが TCP や TLS は通るという経路でも何か特別な仕組みを用意する必要はない。

DataChannel が突然使えなくなるということはほとんどないと考えた方がいい。 あと 5 年は DataChannel はなくならないと考えて良いだろう。

そのため、DataChannel を採用するのは遅すぎると言うことはない。 むしろ枯れきっているため、小さなメッセージをリアルタイムに送りたい場合は最適なプロトコルと考えて良い。

DataChannel の検討をすべき所

シグナリング

シグナリングには WebSocket などが利用されることが多い。特にマルチストリームを利用している場合は参加や離脱の通知が必須になる。ただ WebSocket は HoL Blocking があったりといろいろ WebRTC との相性が悪い。

もともと SCTP がシグナリング用途として開発されたため、相性はいい。

リアルタイムメッセージング

基本的には WebSocket を利用するべきだが、HoL Blocking が気になる場合のみ採用しても良いと思う。 SFU/MCU といったものに DataChannel が搭載されている場合は使いたくなるだろう。実際に Google Duo for Web では通知周りは全て DataChannel が採用されていた。

TCP ではなく UDP を利用して HoL Blocking を避けたいメッセージングがあるのであれば採用するべきだ。

エンドツーエンドなメッセージング

ルーティングとしてのみメッセージを利用した場合には DataChannel を利用するのはお勧めしたい。

アプリレイヤーで再送や順番を管理したい

順序保証も到達保証もいらず、とにかく UDP を暗号経路、かつ複数ストリームで利用したい場合は採用を検討しても良い。

DataChannel を使ってはいけないところ

リップシンクしたいデータを送る

Insertable Streams API を利用すべき。

WebRTC Insertable Streams - Chrome Platform Status

もしくは全てを DataChannel に載せる。

HoL Blocking してもよいデータを送る

WebSocket を利用するべき。

データベースに記録したいデータ

サーバ経由でメッセージを送るべき。

資料

DataChannel

SCTP

DTLS

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment