SlideShare a Scribd company logo
WebブラウザでP2Pを実現する、
WebRTCのAPIと周辺技術
2014/04/19
Updated
2014/04/19 初版
2015/02/21 表記間違い修正
Intro
Yoshiaki Sugimoto
CyberAgent.inc
Developer
JavaScript / Node.js / CreateJS
PHP / Linux
WebRTCの概要
WebRTCのJavaScript実装
P2Pの仕組みとNAT Traversal
STUN / TURN
Appendix
Agenda
トランスポート層の話
TCP / UDP その他プロトコル
NAT関連
Attention
WebRTCの概要
Web Real-Time Communication
リアルタイムコミュニケーションを行うための
API実装
ビデオチャット・ファイル共有 ( )
周辺技術はオンラインゲームにも
Description
WebSocket
サーバを介して(TCP)データを送受信
WebRTC
端末同士が直接(UDP)データを送受信
WebSocket?
WebSocket
transport
WebSocket Server
Local Machine Local Machine
TCP TCP
broadcast
unicast
WebRTC(P2P)
transport
UDP / unicast
Local Machine Local Machine
Signaling
Server
STUN TURN
Server
データの整合性や順序・再送や結果の保証
(タイムアウトなども含む)を考慮すべき通信
-> WebSocket
オーバーヘッドが少なく、多少パケットロスがあっ
ても問題のないシンプル且つ高速なデータ通信
-> WebRTC
WebSocket
vs
WebRTC
WebRTC
Services
https://www.sharedrop.io
https://vline.com
JavaScript API
RTCPeerConnection
RTCSessionDescription
RTCIceCandidate
navigator.getUserMedia
JavaScript
API
WebRTC関連のAPIは合計3つ
Media Stream関連のAPIから1つ
RTCPeerConnection
Peer(ピア)を生成するメインAPI
このオブジェクトを介してP2P通信を行う
いくつかのメソッド・イベントインターフェース
webkitRTCPeerConnection
mozRTCPeerConnection
JavaScript
API
RTCSessionDescription
Session Description Protocolに関する
データオブジェクト
このデータを双方で共有することでP2P接続が確立される
RTCSessionDescription
mozRTCSessionDescription
JavaScript
API
JavaScript
API
RTCIceCandidate
ICEによる接続経路候補(Candidate)
に関するオブジェクト
host / srflx / relay など
RTCIceCandidate
mozRTCIceCandidate
JavaScript
API
navigator.getUserMedia
ブラウザからMedia Streamに関する
接続を可能にするAPI
現在はカメラ・マイクデバイスの入力のみサポート
navigator.webkitGetUserMedia
navigator.mozGetUserMedia
JavaScript
Library
PEERJS
http://peerjs.com/
JavaScript
Library
PEERJS
ベンダープレフィックスやブラウザの
実装差異を吸収しつつ、最適な方法で
通信を行うようサポートしているライブラリ
イベントベースの扱いやすいインターフェース
SkyWay ( http://nttcom.github.io/skyway/ )はこれをforkし
たライブラリを使用している
P2P接続のフローとその実装
- ビデオチャット -
Demonstration
https://github.com/ysugimoto/
RTCPeerConnectionSample
P2P
Connection
P2Pの接続確立フロー
Peer(ピア)の生成
データ・ビデオストリームの接続
Session Description Protocolの交換
Offer / Answer
Signaling
P2P通信経路候補の共有(IceCandidate)
データ送信の共有 or ビデオストリームの共有
var peer = new webkitRTCPeerConnection({
    "iceServers": [{"url": "stun:stun.l.google.com:19302"}]
});
Peerの生成
“iceServers”というプロパティを持つオブジェクトを引数に渡す
var websocket = new WebSocket(‘ws://www.xxx.yyy.zzz’);
Signalling用にWebSocketの接続も行う
メディア接続
// メディアに関する設定
var constraint = {
audio: true,
video: true
};
navigator.webkitGetUserMedia(
    constraint,
    successCallback,
    errorCallback
);
constraint:
audio: マイクを使うかどうか
video: カメラを使うかどうか
successCallback:
メディア接続成功時のコールバック
errorCallback:
メディア接続失敗時のコールバック
自分のストリーム接続
// 自分のストリームはMediaStream APIから取得する
navigator.webkitGetUserMedia(
    { audio: true, video: true },
    function(stream) {
        // video要素取得
        var video = document.getElementById('localVideo');
        // srcにBlob URLを指定するとカメラの画像がストリームで流れる
        video.src = window.webkitURL.createObjectURL(stream);
        // 自分のpeerにカメラストリームを接続させる
        peer.addStream(stream);
    },
    function(err) {
        console.log(err.name + ': ' + err.message);
    }
);
相手のストリーム接続
// 相手のストリームはP2Pのイベントから取得する
peer.onaddstream = function(evt) {
    // 自分のリモートにセット
    var video = document.getElementById('remoteVideo');
    video.src = window.webkitURL.createObjectURL(evt.stream);
};
onaddstreamイベントハンドラで接続
Offer / Answer Offer
Answer
Session Description Protocolの生成
Local / RemoteのSDPをセットする
電話での通話イメージに近い
Offer / Answer
Telephone
Bさんに電話しよう Aさんから着信だ
電話に出ようつながった
Phone Carrier Server
Offer送信
Local Descriptionセット Offer着信
Remote Description着信
Answer送信
Remote Descriptionセット
Local Descriptionセット
Answer着信
Remote Descriptionセット
① ②
③④
Signaling
P2P
Bさんと通信しよう Aさんからcallだ
応答しようつながった
Signaling Server
(WebSocket)
Offer送信
Local Descriptionセット Offer着信
Remote Description着信
Answer送信
Remote Descriptionセット
Local Descriptionセット
Answer着信
Remote Descriptionセット
① ②
③④
Offerの送信(発信者)
// Offer送信
peer.createOffer(function(sdp) {
    // 引数のSDPは自分用
    peer.setLocalDescription(sdp, function() {
        // セット完了したら、相手に自分のSDPを送る(signaling)
        websokcet.send(JSON.stringify({
"sdp": sdp,
"uuid": uuid
}));
    });
});
Signaling
Offer
Bさんと通信しよう Aさんからcallだ
応答しようつながった
Signaling Server
(WebSocket)
Offer送信
Local Descriptionセット Offer着信
Remote Description着信
Answer送信
Remote Descriptionセット
Local Descriptionセット
Answer着信
Remote Descriptionセット
① ②
③④
Offerの受け取りとAnswerの生成(応答者)
// websocketのメッセージイベントで受け取る
websocket.onmessage = function(evt) {
    var message = JSON.parse(evt.data),
        sdp;
    if ( message.sdp ) {
        sdp = new RTCSessionDescription(message.sdp);
        // 相手用(remote)にセット
        peer.setRemoteDescription(sdp, function() {
            // 自分へのOffer-SDPだったらAnswerを返す
            if ( sdp.type === "offer" ) {
                peer.createAnswer(function(sdp) {
peer.setLocalDescription(sdp, function() {
// セット完了したら、相手に自分のSDPを送る
 websokcet.send(JSON.stringify({
"sdp": sdp,
"uuid": uuid
}));
}));
});
}
        });
    }
};
------ ②・④
------------ ③
Signaling
Signaling
Bさんと通信しよう Aさんからcallだ
応答しようつながった
Signaling Server
(WebSocket)
Offer送信
Local Descriptionセット Offer着信
Remote Description着信
Answer送信
Remote Descriptionセット
Local Descriptionセット
Answer着信
Remote Descriptionセット
① ②
③④
Signaling Server
var ws = require(‘websocket.io’);
var server = ws.listen(8124);
// 接続イベント
server.on(‘connection’, function(socket) {
socket.on(‘message’, function(data) {
console.log(‘Message received:’ + data);
     // 接続者全員にブロードキャスト
server.client.forEach(function(client) {
client && client.send(data);
});
});
});
シンプルな
WebSocket
$ node server.js
SDP
Session Description Protocol(SDP)
クライアントPeerの接続情報に関する
テキストデータ
送信元IP、Mediaはこれを使う、Audioをこれを使う、など
シグナリングで交換する
自分のSDPをLocal Description、相手のSDPを
Remote Descriptionと呼ぶ
SDP
SDP Sample
v=0
o=- 6636874602225569115 2 IN IP4 127.0.0.1
...
m=audio 1 RTP/SAVPF 111 103 104 0 8 106 105 13 126
...
m=video 1 RTP/SAVPF 100 116 117
...
a=sctpmap:5000 webrtc-datachannel 1024
a=ssrc:3712708814 cname:dgbXDOpXGNofSBNB
a=ssrc:3712708814 msid:RTCDataConnection RTCDataConnection
a=ssrc:3712708814 mslabel:RTCDataConnection
a=ssrc:3712708814 label:RTCDataConnection
通信経路の共有(発信者/応答者)
// STUNサーバから経路候補が見つかるたび発火
peer.onicecandidate = function(evt) {
    var candidate;
    // evt.candidateプロパティにデータが入っているので、WebSocketで送信
    if ( evt.candiate ) {
        websocket.send(JSON.stringify({"candidate": evt.candidate}));
    }
};
// websocketのメッセージハンドラ内で送信されてきたデータを復元してセットする
websocket.onmessage = function(evt) {
    var message = JSON.parse(evt.data),
        candidate;
    // evt.candidateがあればCandidateの共有
    if ( evt.candidate ) {
        candidate = new RTCIceCandidate(evt.candidate);
        peer.addIceCandidate(candidate);
    }
};
IceCandidate
IceCandidate
STUN(TURN)サーバにリクエストを
送り、自分のNAT環境から通信可能な
経路候補(Candidate)を取得する
候補が見つかるとPeerに通知されるので、
これをSignalingと同様に相手と共有する
経路は複数見つかる(使用できるかどうかは別)
IceCandidate
STUN Server TURN Server
C1 C1
C1: 同一 NAT内(host)
IceCandidate
STUN Server TURN Server
C1 C1
C2 C2
C1: 同一 NAT内(host)
C2: STUNによるNAT Traversal(srflx)
IceCandidate
STUN Server TURN Server
C1: 同一 NAT内(host)
C1 C1
C2 C2
C2: STUNによるNAT Traversal(srflx)
C3 C3
C3: TURNによるNAT Traversal(relay)
IceCandidate sample
IceCandidate
mid: video, candidate: a=candidate:1574647786 1 
udp 2113937151 xxx.xxx.xxx.xx 64219 
typ host generation 0
mid: video, candidate: a=candidate:4102338623 1 
udp 1845501695 yyy.yyy.yyy.yy 41073 
typ srflx raddr xxx.xxx.xxx.xx rport 64219 generation 0
Debug Tool
chrome://webrtc-internals
Peerセッションのイベントや
全てのコネクションデータが確認可能
P2Pの仕組みと
NAT Traversal
クライアントが相互に通信手段を共有
-> SDPの交換(Signaling)
通信経路候補の共有 (Candidate)
端末(Peer)同士が直接パケットを送受信する
(Connected)
P2P
Mechanism
Local Machine
G: 192.168.0.1
Local Machine
G: 192.168.0.2
Signaling
Server
STUN TURN
Server
P2P
Mechanism
Signaling
SDP SDP
Signaling
Server
STUN TURN
Server
P2P
Mechanism
Candidate
Candidate
Local Machine
G:192.168.0.1
Local Machine
G: 192.168.0.2
UDP / unicast
Signaling
Server
STUN TURN
Server
P2P
Mechanism
Connected
Local Machine
G: 192.168.0.1
Local Machine
G: 192.168.0.2
P2P
Mechanism通常は経路にNATを挟む
P2Pは送受信先の完全なアドレスとポートを知る必要がある
NATによって変換される前のプライベートアドレスに届ける
には、NATを越える必要がある
-> NAT Traversal
※正確には、相手端末にパケットが届くような
NATのAddress:Portを得る
Signaling
Server
STUN TURN
Server
P2P
Mechanism
Connected
Local Machine
P: 192.168.0.1
Local Machine
P: 192.168.0.2
G: 10.1.12.21G:10.1.12.20
UDP / unicast
NAT
TraversalNATによって見えない相手の端末に
直接パケットを届けるための技術
P2Pを用いるオンラインゲームではすでに
導入や研究が進んでいる
-> WebRTCにおいても仕組みは同じ
NATの特性によっては難しい場合もある
Full cone NAT
NATの種類
どのIP:Portからでもパケットを受信するNAT
Local Machine
P: 192.168.0.1:255
G:10.1.12.20:80
G:xxx.xxx.xxx.xx
G:yyy.yyy.yyy.yy
packet
Host A
Host B
Address-Restricted NAT
NATの種類LocalMachineがパケットを送ったことがある
アドレスからのパケットを受け取る
Local Machine
P: 192.168.0.1:255
G: 10.1.12.20:80
G:xxx.xxx.xxx.xx
G:yyy.yyy.yyy.yy
packet
Host A
Host B
Port-Restricted NAT
NATの種類Address-Restrictedに加え、送信元のポートも
一致した場合のみ受信する
Local Machine
P: 192.168.0.1:255
G: 10.1.12.20:80
G:xxx.xxx.xxx.xx
:60313
G:yyy.yyy.yyy.yy
packet
Host A
Host B
Symmetric NAT
NATの種類内部パケットは全て唯一のIP:Portにマップ
されるNAT
Local Machine
P: 192.168.0.1:255
G: 10.1.12.20:80
G:xxx.xxx.xxx.xx
:60313
G:yyy.yyy.yyy.yy
packet
Host A
Host B
内部からのパケットを受け取った外部ホストから
のみのパケットを受信する
Universal Plug and Play
デバイスを接続するだけでネットワークに参加できる仕組み
動的にNAT変換テーブルを制御可能
モデム側で対応していないといけない
UPnP
NATタイプによるP2P
NAT Type Full cone Address-Rest Port-Rest Symmetric
Full cone
Address-Rest
Port-Rest
Symmetric
P2P
Enables
STUN / TURN
Simple Traversal of UDP through NATs
RFC3489にて仕様策定
stun.l.google.com:19302
stun.services.mozilla.com
STUN
STUNの役割と動作
クライアントがセッションを開始する際に、
STUNサーバへリクエストを送信し、
NATの情報を取得する
STUNは実装されているアルゴリズムでNATタイプを調査し、
有効なNATアドレスを返却する
STUN
STUN
Algorithm
異なるIP:Portからのecho要求
Full cone NAT
受け取れた 受け取れなかった
同じIP:Portからのecho要求(サーバ#2)
PublicIP: xxx.xxx.xxx.xx:yy
PublicIP変わってないPublicIP変わった
Symmetric NAT同じIP:異なるPortからのecho要求
受け取れた 受け取れなかった
Address-Restricted NAT Port-Restricted NAT
: OK
: NG
var PublicIP = ‘xxx.xxx.xxx.xxx’;
if ( Stun.hasReceivedFrom(defferentAddr, differentPort) ) {
return Stun.FullConeNATs;
} else if ( Stun.hasReceivedFrom(sameAddr, samePort) ) {
if ( Stun.isConstantIP(PublicIP) ) {
return Stun.SymmetricNATs;
}
if ( Stun.hasReceivedFrom(sameAddr, differrentPort) ) {
return Stun.AddressRestrictedNATs;
} else {
return Stun.PortRestrictedNATs;
}
}
STUN
Algorithm
UDP Hole Punching
STUN
NATの内側から特定IP:Portにパケットを送信させ、
NAT側に送信元IP:Portで受信可能なマッピングを生成させる手法。
双方のクライアントはSTUNサーバから得られた互いの送信元
IP:Port同士にパケットを送り合い、受信可能な状態にする。
主にRestricted-NATに対してP2P接続が有効になる
STUN
UDP Hole Punching
Restricted-NATの特性を利用し、STUNサーバ(外部NAT)
に向けてパケットを送信したIP:Portを伝える
Local Machine A
P: 192.168.0.1:255
xxx.xxx.xxx.xx:3478
STUN
Server
①:パケット送信
②:この送信元IP:Portを
Local Machineに伝える
NAT-Bも同様に
IP:Port A
IP:Port B
NAT-A
STUN
UDP Hole Punching(1/3)
Local Machine A
P: 192.168.0.1:255
Local Machine B
P: 192.168.0.2:255
NAT-ANAT-B
IP:Port AIP:Port B
IP:PortA->IP:PortBへパケットを送信するが、
NAT-Bはこのパケットを破棄する
IP:PortB -> IP:PortA
へのパケットは
疎通可能になる(NAT-A)
STUN
UDP Hole Punching(2/3)
Local Machine A
P: 192.168.0.1:255
Local Machine B
P: 192.168.0.2:255
NAT-ANAT-B
IP:Port AIP:Port B
IP:PortB->IP:PortAへパケットを送信すると、
NAT-Aはこのパケットを受信する
IP:PortA -> IP:PortB
へのパケットは
疎通可能になる(NAT-B)
STUN
UDP Hole Punching(3/3)
Local Machine A
P: 192.168.0.1:255
Local Machine B
P: 192.168.0.2:255
NAT-ANAT-B
IP:Port AIP:Port B
IP:PortA->IP:PortBへ再度パケットを送信すると、
NAT-Bは今度はパケットを受信する
IP:PortA / IP:PortB
にUDPパケットの
穴が開いた状態
NATタイプによるP2P
NAT Type Full cone Address-Rest Port-Rest Symmetric
Full cone
Address-Rest
Port-Rest
Symmetric
P2P
Enables
Using UDP Hole Punching
Traversal Using Relay NAT
パケット送受信を外部サーバがRelay
することで、より完全なNAT越え問題を解決する
その特性より、主にSymmetric NATに対して有効で
あるプロトコル
RFC 5766にて仕様策定
TURN
TURNの役割と動作
内部からのパケットを受け取った外部ホストからのみの
パケットを受信する というSymmetric NATの特性を
解決できる(TURNサーバがRelayしてパケットを投げるため)
パケットのRelay機構が外部に存在するため、TURNサーバには大き
な負荷がかかり、サーバダウンによる障害も
ネットワーク経路としてはWebSocketに類似(UDP/TCP変換もサ
ポート)
TURN
TURN Relay Server
Local Machine
P: 192.168.0.1:5678
xxx.xxx.xxx.xx:34567 TURNTURN Server
Local Machine
P: 192.168.0.2:5678
Relay
NATタイプによるP2P
NAT Type Full cone Address-Rest Port-Rest Symmetric
Full cone
Address-Rest
Port-Rest
Symmetric
P2P
Enables
Using UDP Hole Punching
Using TURN Relay
Interactive Connectivity Establishment
STUN / TURNを含め、NAT Traversalへの最適な手法を
提供する規格
ICE
Appendix
- リアルタイム通信対戦-
Peer同士で任意のデータの送受信が可能
String / Blob / ArrayBuffer
SCTP - Reliable Mode
RTP - Non Reliable Mode
Data Channel API
older
DataChannel生成(1/2)
// SDPを生成する前に作成しておく必要がある
var dataChannel = peer.createDataChannel('RTCDataChannel');
// イベントなどを初期化
initializeDataChannel(dataChannel);
// 相手からのDataChannelの接続はイベントで監視
peer.ondatachannel = function(evt) {
// evt.channelにDataChannelが格納されている
    dataChannel = evt.channel;
initializeDataChannel(dataChannel);
};
// データ送信はsend()メソッド
dataChannel.send('some data');
function initializeDataChannel(dataChannel) {
dataChannel.onmessage = function(evt) {
var message = evt.data;
// do something
};
dataChannel.onopen = function() {
// do something
};
dataChannel.onclose = function() {
// do something
};
dataChannel.onerror = function() {
// do something
};
}
DataChannel生成(2/2)
WebSocketと
同じイベントI/F
https://github.com/ysugimoto/WebRTetris
Demonstration
Data Channel Transports
Reliable mode
SCTPプロトコルで転送
データの順序は保証され、再送制御も内部でかかる
Non-Reliable mode
RTPプロトコルで転送
データの順序は保証されず、再送制御も実装する必要がある
Channel間で互換性が保てない><
older
UDPの仕様上、IPv4では64KB/send の制限
チャンク化の送信 / 受信は実装しないといけない
間のデータ転送は16300Byte/send
に制限されてしまう ( via PeerJS )
Some Problems...
conclusion
先行実装ブラウザで大体の機能が使える
特にData Channel APIが動くようになった
DTMF Sender APIはまだ実装が怪しい
リアルタイム通信がWebSocketと2極化するかも
使い分けが大事
Present
プラグインレスで稼働するP2Pオンラインゲーム
サーバ負荷の低い大規模データ配信
いずれはスマートフォンでも利用可能に?
現在はAndroidのChrome29+のみ
Future
WebRTC - Overview
http://www.webrtc.org/
[PDF] ICE TURN/STUN tutorial
http://sdstrowes.co.uk/talks/20081031-ice-turn-stun-tutorial.pdf
WebRTC 1.0: Real-time Communication Between Browsers
http://www.w3.org/TR/webrtc/
WebRTC in the real world: STUN, TURN and signaling
http://www.html5rocks.com/en/tutorials/webrtc/infrastructure/
Interactive Connectivity Establishment (ICE):
A Protocol for Network Address Translator (NAT) Traversal for Offer/Answer Protocols
https://tools.ietf.org/html/rfc5245
SDP: Session Description Protocol
http://tools.ietf.org/html/rfc4566
STUN - Simple Traversal of User Datagram Protocol (UDP)
Through Network Address Translators (NATs
http://tools.ietf.org/html/rfc3489
Traversal Using Relays around NAT (TURN):
Relay Extensions to Session Traversal Utilities for NAT (STUN)
http://tools.ietf.org/html/rfc5766
Thanks!
Resources

More Related Content

WebブラウザでP2Pを実現する、WebRTCのAPIと周辺技術