# DataChannel 経由のシグナリング

> **警告**
>
> DataChannel 経由のシグナリングは実験的機能のため、正式版では仕様が変更される可能性があります

## 概要

DataChannel は WebRTC の機能の一つでデータの送受信を行える機能です。
Sora では WebRTC 接続確立後に、シグナリングを WebSocket 経由から DataChannel 経由に切り替えることが可能です。

## 目的

WebSocket は TCP ベースのため Head of Line Blocking が存在し、不安定な回線などでパケットが詰まってしまうことがあります。
DataChannel は WebSocket とは異なり、パケットを並列でやりとりすることができるため、不安定な回線などでもパケットが詰まることが少なくなります。
シグナリングを WebSocket 経由から DataChannel 経由へ切り替える機能を提供することでより安定した接続が維持できます。

## SDK 対応状況

- 最新版の JavaScript SDK- 対応済みです
- 最新版の iOS SDK- 対応済みです
- 最新版の Android SDK- 対応済みです
- 最新版の Unity SDK- 対応済みです
- 最新版の C++ SDK- 対応済みです
- 最新版の Flutter SDK- 対応済みです

## 注意

DataChannel 経由のシグナリングを有効にした場合、ラベルが違うメッセージは並列に処理されます。
そのため、ラベルをまたいだメッセージ間の順序は保証されなくなります。

例えば、WebSocket 経由のシグナリングではマルチストリーム時の SDP 再交換である `"type": "re-offer"` によって上がる `ontrack` は、
参加通知 `"type": "notify", "event_type": "connection.created"` より先に発火することが多くなります。
DataChannel 経由のシグナリングでは `ontrack` と `"type": "notify", "event_type": "connection.created"` 順序は不定となります。

不明点がある場合はサポートまでお問い合わせください。

## 制限

- Sora の DataChannel 機能では、クライアント側から DataChannel を作成することはできません。- Sora 側で用意した DataChannel の Label のみを利用できます
- Sora の DataChannel 機能では、クライアント側からデータを送信する際には特定の条件を満たす必要があります。

### 推奨メッセージサイズ

DataChannel で利用している SCTP というプロトコルはもともと小さなメッセージを送るために設計されたため、大きなメッセージを送るのには向いていません。そのため 1 メッセージサイズは最大でも 64 kbyte 以内に収まるようにしてください。

64 kbyte 以上のメッセージを送る場合は、大きくても 64 kbyte 以下のメッセージなるように分割して送るようにしてください。










### SDP メッセージサイズ制限

DataChannel は送受信できるメッセージサイズがブラウザごとに制限されているため、
マルチストリーム利用時の SDP が最大メッセージサイズを超えた場合、挙動がおかしくなる場合があります。

現時点で 1 チャネルで多くのユーザーでマルチストリームを利用する場合は DataChannel 経由でのシグナリングは利用しないようにしてください。

今後 SDP を再利用する仕組みを導入することでこの問題を解決していく予定です。

## シグナリング切り替え

DTLS が確立したタイミングで、シグナリングでのやりとりを WebSocket 経由から DataChannel 経由に切り替える仕組みです。

> **重要**
>
> 切り替えが完了した場合でも、Sora 側からは WebSocket を切断しません。

シグナリングの切り替えを含めた DataChannel 経由のシグナリングの処理の流れは、[DataChannel シグナリング](DATA_CHANNEL_SIGNALING.html#5626ed) のシーケンス図をご確認ください。

### シグナリングの切り替えを有効にする

シグナリングの切り替えを有効にするには、次の 3 つの方法があります。

- `"type": "connect"` 時に `data_channel_signaling` を `true` にする
- 認証成功時に `data_channel_signaling` を `true` で払い出す
- `sora.conf` の [default_data_channel_signaling](SORA_CONF.html#adceef) を `true` にする

Sora SDK を利用している限りは、これらの設定のみで、シグナリングの WebSocket 経由から DataChannel 経由への切り替えを利用できます。

### "type": "switched"

DataChannel へ完全に切り替わるタイミングで、 Sora は `"type": "switched"` を WebSocket 経由でクライアントへ送ります。

DataChannel に切り替わる前に WebSocket が閉じた場合、Sora はその接続が無効と判断して、接続を終了します。

### DataChannel へ切り替わった後（"type": "switched" 後）の WebSocket での "type": "ping" / "type": "pong" について

WebSocket から DataChannel に切り替わると、 WebSocket の `"type": "ping"` を送る間隔が 5 秒から 30 秒に引き延ばされます。
さらに Sora が `"type": "pong"` を期待しなくなります。

## WebSocket が閉じたことを無視する

Sora は、デフォルトでは接続の切断判定に WebSocket を利用しています。WebSocket が閉じると、Sora は接続が切断したと判断します。

ただし、WebSocket が閉じた場合でも、接続が切断したと判断しないようにしたい場面もあるため、`ignore_disconnect_websocket` という設定を用意しています。

この設定は `sora.conf` 、 `"type": "connect"` または認証成功時の払い出しで指定できます。

この設定を `true` にすることで、Sora は WebSocket が閉じてもその接続が切断したとは判断せずに、
WebRTC のやりとりを継続します。

> **注意**
>
> `"type": "disconnect"` を送らずに終了した場合、 DataChannel が切断に気付くまでには早くても 10 秒はかかります。

### クライアント側の切断タイミング

もし、 `ignore_disconnect_websocket` を `true` にして WebSocket を切断したい場合は、必ず Sora から `"type": "switched"` を受け取った後にしてください。

`ignore_disconnect_websocket` が `true` の場合でも、 `"type": "switched"` を受け取る前に WebSocket を切断すると、 Sora はその接続が無効と判断して接続を終了します。


## DataChannel への切り替え要否の判断

`"type": "offer"` に `"data_channels"` が含まれることを確認することで、DataChannel への切り替えが必要と判断することが可能です。

切り替えが不要な場合は `"data_channels"` は送られてきません。

```javascript
{
  "type": "offer",
  "data_channels": [
    {
      "compress": true,
      "label": "stats"
    },
    {
      "compress": false,
      "label": "e2ee"
    },
    {
      "compress": true,
      "label": "push"
    },
    {
      "compress": true,
      "label": "notify"
    },
    {
      "compress": true,
      "label": "signaling"
    }
  ]
}
```

## label 単位の圧縮

Sora は、DataChannel 経由のメッセージを `zlib/deflate` で圧縮した送受信を要求する場合があります。

メッセージの圧縮や展開は Label ごとに要求します。

メッセージの圧縮や展開を要求する Label の情報は、 `"type": "offer"` 時に `data_channels` で `compress` を `true` に指定してクライアントに送ります。

- Sora は圧縮時に zlib ヘッダーを付けて送ってきます
- Sora は展開時に zlib ヘッダーを必要とします
- Sora は圧縮レベルは `default` を利用します

### 圧縮されるデフォルト Label

- signaling
- notify
- push
- stats

### 圧縮されない Label

- e2ee

## 設定

### deafult_data_channel_signaling

シグナリングを WebSocket から DataChannel 経由に切り替えるかどうかの設定です。
デフォルトは `false` です。

### default_ignore_disconnect_websocket

シグナリングを DataChannel 経由に切り替えた際に、 WebSocket が閉じても、接続が切断したとみなさない設定です。

デフォルトは `false` です。

## label とシグナリングのタイプ

各 label とシグナリングのタイプについては、[DataChannel シグナリング](DATA_CHANNEL_SIGNALING.html#5626ed) の図も下記の説明と合わせてご確認ください。

### "label": "signaling"

**順番**: あり
**信頼性**: あり
**圧縮**: あり

WebRTC が確立する前の type: connect/offer/answer/candiate については、DataChannel 経由のシグナリングでは利用できません。

`"type": "re-offer"` は `"label": "signaling"` に送られてきます。

`"type": "re-answer"` 、 `"type": "disconnect"` は `"label": "signaling"` に送ります。

`"type": "ping"` と `"type": "pong"` は DataChannel では利用しません。

#### "type": "re-offer"

> **重要**
>
> "type": "update" の代わりに Sora からは "type": "re-offer" が送られてきます。

マルチストリーム利用時に SDP 再交換を行う際に、 Sora から送られてきます。

#### "type": "re-answer"

> **重要**
>
> "type": "update" の代わりに Sora へ "type": "re-answer" を送ります。

マルチストリーム利用時に SDP 再交換を行う際に、 Sora へ送ります。

#### "type": "disconnect"

接続を切断することを通知するために Sora へ送ります。

### "label": "notify"

**順番**: あり
**信頼性**: あり
**圧縮**: あり

`"type": "notify"` は `"label": "notify"` に送られてきます。

中身は WebSocket 経由の値と変わりません。

### "label": "push"

**順番**: あり
**信頼性**: あり
**圧縮**: あり

`"type": "push"` は `"label": "push"` に送られてきます。

中身は WebSocket 経由の値と変わりません。

### "label": "e2ee"

**順番**: あり
**信頼性**: あり
**圧縮**: なし

- WebSocket シグナリングのクライアントやサーバーが送るバイナリメッセージと同様です

### "label": "stats"

**順番**: あり
**信頼性**: あり
**圧縮**: あり

**DataChannel から新しく追加されたタイプです**

今まで `"type": "ping"` と `"type": "pong"` 内部でやりとりしていた `stats` は DataChannel 経由では独立しました。

Sora から `{"type": "req-stats"}` が送られてきたら、 `{"type": "stats", "reports": [{"id": "...", ...}, ...]}` で `reports` の中に、WebSocket 経由時には `"type": "pong"` で入れていた統計情報を入れて送り返してください。

#### "type": "req-stats"

Sora からクライアントに stats 情報を要求します。

#### "type": "stats"

クライアントから Sora に stats 情報を送ります。

## DataChannel 関連の設定

### data_channel_stats_timer_interval

シグナリングを DataChannel 経由に切り替えた際に、 Sora からクライアントへ統計情報の要求を送る間隔の設定です。

デフォルトは `5 s` です。

### dcsctp_heartbeat_interval

シグナリングを DataChannel 経由に切り替えた際に、 SCTP プロトコルのハートビートを送る間隔の設定です。

デフォルトは `30 s` です。

## シーケンス図


### 認証ウェブフックなし

seqdiag {
    default_fontsize = 15;
    edge_length = 300;

    クライアント ->> Sora [label = '"type": "connect"'];
    クライアント <<- Sora [label = '"type": "offer"'];
    クライアント ->> Sora [label = '"type": "answer"', noactivate];
    クライアント ->> Sora [label = '"type": "candidate"', noactivate];

    === WebRTC 確立 ===

        Sora -> アプリケーションサーバー [label = 'イベントウェブフック\n"type": "connection.created"'];
        Sora <-- アプリケーションサーバー [label = '200 OK'];

    === DataChannel 確立 ===

    クライアント <- Sora [label = '"type": "switched"'];

    === これ以降は DataChannel が利用される ===

}


### DataChannel シグナリング

seqdiag {
    default_fontsize = 15;
    edge_length = 350;

    === WebRTC 確立 ===

    === DataChannel 確立 ===

    クライアント <<- Sora [label = '"type": "switched"'];

    === これ以降は DataChannel が利用される ===

    クライアント <<- Sora [label = '"type": "re-offer"',
                           rightnote = 'label: signaling'];
    クライアント ->> Sora [label = '"type": "re-answer"',
                           leftnote = 'label: signaling'];

    クライアント <<- Sora [label = '"type": "notify"',
                           rightnote = 'label: notify',
                           noactivate];

    クライアント <<- Sora [label = '"type": "push"',
                           rightnote = 'label: push',
                           noactivate];

    クライアント <<- Sora [label = '"type": "req-stats"',
                           rightnote = 'label: stats']

    クライアント ->> Sora [label = '"type": "stats"',
                           leftnote = 'label: stats'];
}
