WebRTCの基本とP2P通信が成立するまでを学ぶ


# はじめに
昨年新卒でSIer企業に入り、現在オンラインチャットアプリの開発に取り組んでいるのですが、
その過程でビデオチャットを取り扱う機会がありました。

WebRTCについての知識がないものでだいぶ苦戦したため、
一度WebRTCの基本的な仕組みを理解して「とりあえず動く」を脱しようと思い色々調べてみました。

# WebRTCって？
WebRTCとは、Web Real-Time-Communicationのこと。

HTML5で新しく策定されたAPIの規格で、
P2P通信でブラウザ間のリアルタイムコミュニケーションを実現するための仕組みです。

WebRTCにおけるAPIは次の2つの仕様で策定されているそうです。

- カメラ/デバイスへのアクセス
　**Media Capture and Stream**

- ビデオ/オーディオ/データ通信を行う
　**WebRTC 1.0: Real-time Communication Between Browsers**

これらはどちらもW3Cによって策定されており、W3CのサイトでAPIの詳細は見ることができます。

上記2点のAPIとCanvasやWebGL、Web audio APIなどを合わせることで
音声を変換したり、カメラから受け取った映像のキャプチャを拾い、画像処理をする。顔検出をする。
など様々な活用を行うことができます。

これらの規格は年々変わっているそうで、
ベンダープレフィックスが取れてきたり、
Promiseベースになったりと徐々に利便性が上がっているようです。

対応ブラウザはChrome、Firefox、Safari[^1]、Edge[^1]では問題なく動くようです。

[^1]: 一部の機能が限定的に使えます。

# Media Capture and Stream

これはブラウザやカメラにアクセスするためのAPI規格です。
これについては、
`getUserMedia()` というメソッドが分かっていればよさそうな感じでした。

以下のように記載することでPromiseベースでカメラ/マイクへの接続を許可します。

```js
navigator.mediaDevices.getUserMedia({
      video: true,
      audio: true
    }).then(function (stream) {
      document.srcObject = stream;

    }).catch(function (error) {
      console.error('getUserMedia() error ->', error);
      return;
    });
```

getUserMediaにvide/audioの設定を引数で渡し、アクセス成功時にはstreamが返ってくるため、それをHTMLのvideoタグに入れてあげるようにします。

また、videoやaudioには様々なオプションがあり、
高さや幅、フレームレートなどを指定してあげることが可能です。

```js
video: {
    width: 640,
    height: 480
    frameRate: { min: 10, max: 15}
 }
```

また、videoタグに対して何か実装を加えれば、
Canvasでキャプチャを拾ったり、
CSSでアニメーションや加工を施すことができますね。

# WebRTC 1.0: Real-time Communication Between Browsers

次に、WebRTC本体ともいえるP2P通信の仕組みについてです。

WebRTCでは、クライアントがサーバにデータの要求をする一般のクライアント/サーバモデルとは異なり、
複数端末での通信で、それぞれのPCがデータを保持し、他のPCに対して直接的に
データの送信・要求を行う**Peer to Peer**の形をとって通信します。

また、通信プロトコルには基本的に**TCP/IP**の代わりに**UDP/IP**を用いて
通信のリアルタイム性を担保しているのも特徴です。状況によってプロトコルを使い分けています。

### TCPとUDP
TCPプロトコルとUDPプロトコルの大きな違いは信頼性と速度です。
TCPにはパケット通信を行う他に、開始処理と終了処理がありますが、UDPは即座にパケットを相手に送ります。
また、TCPではパケットを受け取ると、受け取った合図を返して到達性を担保しますが、UDPではパケットを投げっぱなしにするため、相手が受け取ったかどうかはわかりません。

![スクリーンショット 2018-02-04 2.53.46.png](https://qiita-image-store.s3.amazonaws.com/0/203817/0fc75e9d-aa1f-7ef1-ba55-e5ab83fceee5.png)

そのため、UDPはTCPに比べて通信の信頼性は下がりますが、通信は高速となり、WebRTCのようなリアルタイム性を求める通信に利用されています。

## P2P通信に必要な情報
P2P通信を行うためには、いくつかブラウザ間で共有しなければならない情報があります。

- Session Description Protocol(SDP)
　通信に必要な各ブラウザの情報を示す文字列。
　(セッションの属性、メディアの形式、IPアドレス、ポート番号、通信帯域など)
　片方のPCが他方PCに対しSDPをOfferし、それに対してAnswer SDPを返すという
　Offer/Answerモデルで通信を行います。
<br><br>

- Interactive Connectivity Establishment(ICE)
　相手ブラウザに到達する可能性のある通信経路に関する情報を示したもの。
　この通信経路を探す際、社内LANなど、環境によってはNATやファイアウォールなどで
　直接的にPC同士の情報を渡せないことがあります。
　
　そのため、STUNサーバやTURNサーバというものを用いて通信経路を見出し、
　それらの経路候補をICE Candidateとしてブラウザ間で共有しないといけません。

### STUNサーバとは？
NAT配下にあるPCがNATの外側から見た自身のIPアドレスを知るためのもの。

NAT配下にPCがある場合、P2P通信で相手に伝える必要がある自身のIPアドレスを
知ることができません。
そのため、P2PではSTUNサーバを経由することで、外側から見たIPアドレスを取得しています。

![スクリーンショット 2018-02-04 3.03.11.png](https://qiita-image-store.s3.amazonaws.com/0/203817/84ef00c7-49eb-1257-530f-77815516f129.png)


### TURNサーバとは？
PC同士の間に立って、データの中継をしてくれることでNAT越えを実現するサーバ。
NAT配下でP2P通信を実現するためにすべてのパケットをグローバルなサーバとしてパケットを代理で相手PCに届けます。

![スクリーンショット 2018-02-04 3.11.55.png](https://qiita-image-store.s3.amazonaws.com/0/203817/82071932-6061-3872-898e-b3d4abf8e6b9.png)


## 実際のP2P通信が成立するまでの流れ

では実際にP2P通信が成立するまでのやりとりを追っていきます。

P2P通信が成立するまでには3つのサーバーを介します。
- Webサーバー(HTTPで通信するWebページ)
- STUN + TURNサーバ(NATやファイアウォールを越えるために必要)
- シグナリングサーバ(WebSocket等を用いたサーバ。相手PCに情報を伝える仲介役)

![スクリーンショット 2018-02-04 2.37.10.png](https://qiita-image-store.s3.amazonaws.com/0/203817/1614b330-10d6-d18c-1122-de803410490e.png)


順序は以下の通りです。(仮に各PCはNAT配下にあるものとします。表記はPC1, PC2とします。)
⑴ PC1,PC2が互いにWebサーバとシグナリングサーバに接続。同様にWebSocketを開く。

⑵ PC1(接続する側)でSDPを作成し、自身に登録。登録後、PC2にSDPをシグナリングサーバを通じて送信する。相手PCは受け取り後、SDPを登録する。

![スクリーンショット 2018-02-04 10.31.18.png](https://qiita-image-store.s3.amazonaws.com/0/203817/078cd08d-14e3-aaf4-cd97-d2233562ca7e.png)


⑶ 同様に、PC２もSDPを発行する。自身に登録後、シグナリングサーバを通じてPC1にSDPを送る。PC1は受け取ったSDPを登録する。

![スクリーンショット 2018-02-04 10.30.59.png](https://qiita-image-store.s3.amazonaws.com/0/203817/26e78584-b307-a212-0a4f-7919cfbe08b1.png)

⑷ NAT配下の場合、自身のIP/PortをSTUNサーバを用いて取得する。
⑸ ポート制限等におけるNAT越え通信実現の際は、TURNサーバがデータを中継して送信する。

⑹ これまでの情報をもとPC1は自身に接続できそうな接続経路の候補をPC2に送る(ICE Candidate)。PC2は受け取ったICE Candidateを登録する。
⑺ 同様にPC2も経路情報をPC1に送信する。PC1は受け取ると登録。

![スクリーンショット 2018-02-04 10.31.26.png](https://qiita-image-store.s3.amazonaws.com/0/203817/e6db7d6f-4b57-9a35-05d1-384f7411e5e4.png)

⑻ 接続完了。P2P通信が成立する。

調べて理解した範囲の内容なので、多少異なる部分もあるかもしれませんが大体はあっているかと思います。
間違い等ありましたらお伝えお願いします。

## 参考になったサイト
今回WebRTCの理解をするにあたって以下のサイトを参考にさせて頂きました。

[WebRTCの技術解説 公開版](https://www.slideshare.net/nttwestcon/20140805-technical-descriptionofwebrtcpublicedition)
[WebRTC入門2016](https://html5experts.jp/series/webrtc2016/)
[【初心者向け】STUN/TURNサーバをざっくり解説してみた](https://qiita.com/okyk/items/a405f827e23cb9ef3bde)

この記事ではWebRTCの基本的な知識とP2P通信接続までの流れをお話ししましたが、実際の実装方法などは他のサイトで詳しく書かれているのでそちらをご覧ください。
WebRTCは覚えることが多く、仕様も年々変わっているようなので、定期的に調べて勉強しないといけなさそうですね。