Part.1はこちら。
NodeJS製WebRTC DataChannel、NodeRTCのコードを読む Part.1 - console.lealog();
長かったこのシリーズもこれで最後です。
DataChannel
WebRTCのDataChannelは、
- UDP
- DTLS
- SCTP
で構成されてる風ではあるけど実は最後にもう1層、DataChannel自体の層があるイメージになってる。
NodeRTCのコードを読み始めた段階では実装されてなかったこの最後の1ピースが、最近実装されたのでそこを読んでいく。
いわゆるEstablishment Protocolの部分。
nodertc/datachannel
読んだバージョンは`1.0.0`です。
GitHub - nodertc/datachannel: WebRTC Data Channel Establishment Protocol
ディレクトリはこのように。
. ├── channel.js ├── constants.js ├── handshake.js ├── index.js └── protocol.js
ファイル数少なくて安心した。
使われ方
this.sctp.on('connection', socket => { console.log('[nodertc][sctp] got a new connection!'); socket.on('stream', sctpStreamIn => { console.log('[nodertc][sctp] got stream %s', sctpStreamIn.stream_id); const sctpStreamOut = socket.createStream(sctpStreamIn.stream_id); const channel = createChannel({ input: sctpStreamIn, output: sctpStreamOut, negotiated: true, }); channel.once('open', () => { this.emit('channel', channel); }); });
という感じ。
- SCTPのコネクションで`socket`を得る
- その`socket`の`on('stream')`で得たストリームを使ってChannelを作る
- 前回みたSCTPの`createStream()`はココで使う
- IN/OUTがあって、それぞれがSCTPのストリーム
- `channel`のAPIで、WebRTCのクライアントにつながる実装がされてる
- `write()`すれば送信できる
- `on('data')`で受信できる
`createChannel()`は`index.js`に定義されてるけど、`new Channel()`するだけなのでそっちから。
class: Channel
- extends `Duplex`
- `constructor()`
- `input` / `output`でそれぞれ`Readable` / `Writable`が必須
- オプションはDCの挙動を制御するもの(`reliable`とか)
- 実態は`HandshakeMachine`なるクラスが握ってる
- `pipeline(input, handshake)`してる
- `negotiated`のオプションを`true`にすると、先にHandshakeをはじめるために`opening()`を呼ぶ
- `_read()`は空っぽ
- `_write()`は`handshake`の状態に応じて、`output.write()`
今まで散々重い実装を読んできただけあって、すんなり理解できる。
class: HandshakeMachine
`negotiated`は`true`で初期化されるので、きっかけは`opening()`が呼ばれるところから。
- `emit('postopen')`
- 接続してるピア側に、`DATA_CHANNEL_OPEN`を送ってHandshakeを開始する
- ピア側から返されるであろう`DATA_CHANNEL_ACK`を受け取るはず
- ピア側(`input`)からのパケットは、`_transform()`で処理される
- Handshakeが終わってないはずなので、`_handshake()`が呼ばれる
- `_handshake()`
- `emit('final')` -> `channel.emit('open')`
これでピア側とDataChannelがつながった。今度こそ。
読んでみて
実装が薄いからか今まで重いのを呼んでたからか、あっさり読めて良かった。
これくらいの薄さならRFCの全文を読んでもすんなりいけたので、まあそういうのの積み重ねなんかなーと思った。
そしてやっぱりTypeScriptでやらなかった理由は何なんやろう・・。