WebSocket 和 WebRTC

WebSocket

WebSocket的出现其实就是利用http协议,在web端来完成socket的任务,让客户端和服务端可以即时通讯,就像讲电话一样。

最早刚知道socket(套接字)的时候,我当时以为这个东西只有拿c/c++可以写,来写底层,后来发现自己还是太年轻…

关于具体详情可以参见wiki

一般在网上和web页面的后台交互,都是用的HTTP协议,HTTP是无状态的,就是说你每次必须告诉他你是谁,他才知道你是谁,如果没有header,或者没有session,cookie等各种信息,他是不知道你是谁的。

他会比HTTP1.1多出下面几个内容

Connection: "Upgrade"
Upgrade: "websocket"
Sec-WebSocket-Key: "sN9cRrP/n9NdMgdcy2VJFQ=="
Sec-WebSocket-Version: "13"

Upgrade 选项表示将HTTP转向该协议,Connection: Upgrade 表示如果能升级就升级吧
握手的Key值是一段随机的base64编码的16字节字符串

ws代表websocket协议,对应http协议,wss对应https

如何使用js创建websocket可以参见MDN的文档

这是他的constructor,你基本上能看出来他需要一个hosturl,和一个可选的协议列表

WebSocket WebSocket(
  in DOMString url,
  in optional DOMString protocols
);

WebSocket WebSocket(
  in DOMString url,
  in optional DOMString[] protocols
);

动作主要有 close() 关闭链接,和 send() 发送数据,下面是send的函数声明

void send(
  in DOMString data
);

void send(
  in ArrayBuffer data
);

void send(
  in Blob data
);

websocket通过监听几个事件来完成通讯,js可以用 onxxevent 来获取事件数据
onopenonmessageonerroronclose,意思已经很明显了,打开连接,接受数据,报错,关闭连接。

node.js有websocket的库可以建立websocket server
我是先接触到 socket.io 库,封装得比较好,socket.io会把不支持websocket的连接降级为long pull,可以理解为不停地ajax轮训。socket.io的后端库支持很多,php、node、python、django(扩展模块)都是支持的

总之你就可以理解为,浏览器里面用c++实现了另一种协议通讯,可以转换HTTP为WebSocket然后包在TCP上面传输

WebRTC

网络实时通(Web Real Time Communication),这是一个非常非常非常流弊的接口,可以帮助用户传输两个浏览器之间的音频,视频流信息,当然也能传别的啦,总之非常流弊。

WebRTC现在已经慢慢地被建立为标准,在美帝火了起来,希望在国内也迅速火起来好么。主要有MediaStream, RTCPeerConnection, RTCDataChannel这三个接口

MediaStream

这个是用来获取用户视频音频流,chrome每次会询问你是否要交出去,如果是https,你第一次确认获取媒体流的话,后面会默认获取不再询问你。

navigator.getUserMedia  = navigator.getUserMedia ||
                          navigator.webkitGetUserMedia ||
                          navigator.mozGetUserMedia ||
                          navigator.msGetUserMedia;

if (navigator.getUserMedia) {
    function successCallback(stream) {
      var video = document.querySelector("video");
      video.src = (window.URL) ? window.URL.createObjectURL(stream) : stream;
      // 成功就会把你的数据传给video标签
    }

    function errorCallback(error) {
      console.log("error:", error);
    }

    var contrains = {
      video: true,
      audio: true
    };
    navigator.getUserMedia(contrains, successCallback, errorCallback);
}

什么情况下会失败呢?首先你浏览器不支持webrtc的时候(比如weak ie),其次还有下面几种情况,他们会在errorCallback函数中以Error对象的code属性来返回给调用者

PERMISSION_DENIED:"用户拒绝提供信息"
NOT_SUPPORTED_ERROR:"浏览器不支持硬件设备"
MANDATORY_UNSATISFIED_ERROR:"无法发现指定的硬件设备"

他能拿来干嘛?最简单的webcamera,上面有很多有趣的css滤镜,但居然要翻墙,╭(╯^╰)╮

window.AudioContext = window.AudioContext ||
                      window.webkitAudioContext;

var context = new AudioContext();

function onSuccess(stream) {
    var audioInput = context.createMediaStreamSource(stream);
    audioInput.connect(context.destination);
}

function onError(err) {
    // 注意一定要有 onError 回调啊,现在三个参数都是必须有的
}

navigator.getUserMedia({audio:true}, onSuccess, onError);

把上面的一些变量输出出来,你可以看到他们的类定义,从中发现些什么


MediaStream {
    onremovetrack: null, 
    onaddtrack: null, 
    onended: null, 
    ended: false, 
    id: "sLSymbzB1qaDDdhct5fEdoRI7UXT90yGCK0q"}

MediaStreamAudioSourceNode {
    mediaStream: MediaStream, 
    channelInterpretation: "speakers", 
    channelCountMode: "max", 
    channelCount: 2, 
    numberOfOutputs: 1}

AudioDestinationNode {
    maxChannelCount: 2,
    channelInterpretation: "speakers",
    channelCountMode: "explicit",
    channelCount: 2, numberOfOutputs: 0}

AudioContext {
    onstatechange: null,
    state: "running",
    listener: AudioListener,
    sampleRate: 48000,
    currentTime: 2.52,}

你一定发现了些什么吧,会不会猜到了它的实现呢?

RTCPeerConnection

RTCPeerConnection的作用是在浏览器之间建立数据的Peer To Peer连接,发送的数据经过信号处理、多媒体编码/解码、点对点通信、数据安全、带宽管理等等发送到了另一个客户端。

但要建立连接,是必须先有服务器,服务器获取二者的外网地址,然后发给对方,两个人才能建立p2p通信,服务器就好像中介,只在开始起作用。

不同客户端之间的音频/视频传递,是不用通过服务器的。但是,两个客户端之间建立联系,需要通过服务器。服务器主要转递两种数据。

我们需要中间穿插介绍一个 NAT穿透 的概念

NAT Traversal

NAT穿越(NAT traversal)涉及TCP/IP网络中的一个常见问题,即在处于使用了NAT设备的私有TCP/IP网络中的主机之间建立连接的问题。

会遇到这个问题的通常是那些客户端网络交互应用程序的开发人员,尤其是在对等网络和VoIP领域中。IPsec VPN客户普遍使用NAT-T来达到使ESP包通过NAT的目的。

NAT的行为是非标准化的。这些技术中的大多数都要求有一个公共服务器,这个服务器使用的是一个众所周知的、从全球任何地方都能访问得到的IP地址(如google的STUN server)。有的连接仅在建立连接时需要使用这个服务器,就是我们下面提的

两种常用的NAT穿越技术是:UDP路由验证和STUN。除此之外,还有TURN, ICE, ALG,以及SBC。

STUN

Simple Traversal of UDP Through NATs,即简单的用UDP穿透NAT。后来演变为Session Traversal Utilities for NAT,支持TCP穿透

TURN

Traversal Using Relays around NAT:Relay Extensions to Session Traversal Utilities for NAT,即使用中继穿透NAT:STUN的扩展(中间人穿透)

WebRTC协议没有规定与服务器的通信方式,但规定了客户端之间的通信方式 Session Description Protocol(SDP) 交换双方的元数据。

RTCDataChannel

RTCDataChannel它的作用是在点对点之间,传播任意数据。它的API与WebSockets的API相同


var pc = new webkitRTCPeerConnection(servers,
  {optional: [{RtpDataChannels: true}]});

pc.ondatachannel = function(event) {
  receiveChannel = event.channel;
  receiveChannel.onmessage = function(event){
    document.querySelector("div#receive").innerHTML = event.data;
  };
};

var sendChannel = pc.createDataChannel("sendDataChannel", {reliable: false});
sendChannel.send('some data'); // 发送给另一个receiveChannel