🍃このブログは移転しました。
3秒後、自動的に移動します・・・。

NodeJS製WebRTC DataChannel、NodeRTCのコードを読む Part.6

Part.1はこちら。

NodeJS製WebRTC DataChannel、NodeRTCのコードを読む Part.1 - console.lealog();

今回もSCTPの実装を読んでいく後編です。

だいぶ長丁場になってきたけど、そろそろゴールも見えてきたところ。

前編のおさらい

  • SCTPのコードを追ってた
  • エンドポイントの初期化を待って、SCTPのソケットを初期化してた

具体的には次のコード。

endpoint.on('association', association => {
  // Todo other params
  const socket = new Socket({ppid: this.ppid})
  socket.establish(endpoint, association)
  this.emit('connection', socket)
  this.debugger.debug('connect <- %s:%s', association.remoteAddress, association.remotePort)
})

この`Socket`と、`establish()`を見ていく。

class: Socket

  • extends `Duplex`
  • `constructor()`では特に何もしてない
  • `_read()`
    • 何もしてない
  • `_write()`
    • `this.association.SEND()`

`establish()`

  • `endpoint`と`association`を引数に取る
  • `association`で各イベントを購読してる
    • 基本的には受けてそのまま`this.emit()`してる
    • `on('DATA ARRIVE')`で`this.push()`
    • 当初の使われ方にあった`socket.on('data')`へ

すごいざっくりではあるけど、これで一通りの流れは追えたはず。(メインの導線のみやけど・・)

association.on('DATA ARRIVE', streamId => {
  const buffer = association.RECEIVE(streamId)
  if (!buffer) {
    return
  }

  this.debugger.debug('< DATA ARRIVE %d bytes on stream %d', buffer.length, streamId)

  if (this.listenerCount('stream') > 0) {
    if (!this.streamsReadable[streamId]) {
      this.streamsReadable[streamId] = new SCTPStreamReadable(this, streamId)
      this.emit('stream', this.streamsReadable[streamId], streamId)
    }
    this.streamsReadable[streamId].push(buffer)
  }

  this.bytesRead += buffer.length
  this.push(buffer)
})

真ん中あたりがキモで、`on('stream')`されてるなら、それ用のストリームを作って返してる。

これがSCTPのストリームで、`streamId`がそれぞれに割り振られる。

`SCTPStreamReadable`があるように`SCTPStreamWritable`もあって、それはどこかで作られるかというと、

  • `createStream(streamId)`で作られる
  • `SCTPStreamWritable`はソケットの参照を持つ
  • `_write()`で、`this.socket.association.SEND()`

このメソッド自体は、より上層で使われるはず。

次に読むDataChannelのEstablishment Protocolのあたりで。

その他

このSCTPのモジュールに含まれてて触れてないファイルもちらっと見ておく。

chunk.js

  • `endpoint`の`onPacket()`で届いたパケットをさばくための単位
  • 様々な`chunkType`がある
    • `init`, `init_ack`, `abort`, ...etc
    • RFCにずらずら書いてあるやつ
  • `fromBuffer()` / `toBuffer()`

defs.js

  • いわゆる定数など
  • さっきの`chunkType`ごとに決まってるパラメータなども

packet.js

  • さっきの`Chunk`を内包する単位
    • 個別に持ってるヘッダと`Chunk`をボディにしてまとめてる
  • `fromBuffer()` / `toBuffer()`

serial.js

  • RFC 1982 Serial Number Arithmetic」とのこと
  • 一連のやり取りにおける通し番号を管理するための単位っぽい

reassembly.js

  • extends `EventEmitter`
  • `association`が内部的に保持する
  • パケットを受け取った際に、それをどう処理するか決めるために参照される存在っぽい
    • 状態を管理しておく風
  • 前回見た`acceptRemote()`の先で`reassembly.init()`してる

これはがっつりRFC読まないと読めないコード・・。

読んでみて

  • 今までで一番重い
    • 息切れして記事がこんなに短くなるくらいに重い
  • RFCを理解してないと、この程度しかわからない
    • まぁこれは予想できてたことやし、理解するきっかけになればいいのでよし
  • この作者これ全部1人で書いてるの・・まじ・・

とりあえずなんとなくだいたいは読めたのでもうすぐゴールできそう。