初探WebSocket

初探WebSocket

node websocket socket.iohtml


咱们日常开发的大部分web页面都是主动‘拉’的形式,若是须要更新页面内容,则须要“刷新”一个,但Slack工具却能主动收到信息,好像服务端能主动给客户端推送信息,请研究一下这是怎么实现的。html5

WebSocket

OSI七层模型

websocket是HTML5中新引进的一种 协议 ,它是一种协议就像(HTTP,FTP在tcp/ip协议栈中属于应用层)而不是简单的一个函数。它自己及基于TCP协议的一种新的协议。node

WebSocket的产生

websocket是基于web的实时性而产生的,说到这里就不得不要追溯一下web的历史了,在2005年(也就是ajax还没诞生)之前,咱们若是想要在一个页面显示显示不一样的内容,或者说页面内跳转,只能是经过点击而后路由跳转,在ajax诞生以后,网页开始变得动态了。可是全部的HTTP通讯还都是由客户端控制的,这就要须要长链接,按期轮询或者长轮询,来和服务器沟通来更新数据。git

WebSocket以前的服务器“推”的技术

  1. 按期轮询(ajax轮询):浏览器在特定的时间给服务器发送请求,查看服务器是否有信息数据。按期轮询

优势:后端程序编写比较容易。
缺点:请求中有大半是无用,浪费带宽和服务器资源。
实例:适于小型应用。github

  1. 长轮询:其实和上面的原理差很少,是对ajax轮询进行了改进和提升。客户端和服务端创建链接以后,一直保持通讯(阻塞模式),若是服务器没有新消息就一直保持通讯,直到服务器有新的消息,而后返回给客户端,客户端与服务器断开链接,此时客户端能够继续和服务器进行链接。长连接

优势:在无消息的状况下不会频繁的请求,耗费资源小。
缺点:服务器hold链接会消耗资源,返回数据顺序无保证,难于管理维护。
实例:旧的 WebQQ、Hi网页版、Facebook IM。web

  1. 流控制:一般就是在客户端的页面使用一个隐藏的窗口向服务端发出一个长链接的请求。服务器端接到这个请求后做出回应并不断更新链接状态以保证客户端和服务 器端的链接不过时。经过这种机制能够将服务器端的信息源源不断地推向客户端。好比在页面里嵌入一个隐蔵iframe,将这个隐蔵iframe的src属性设为对一个长链接的请求或是采用xhr请求,服务器端就能源源不断地往客户端输入数据。

SSE,Comet,使用长连接进行通信。流操做ajax

优势:消息即时到达,不发无用请求;管理起来也相对方便。
缺点:服务器维护一个长链接会增长开销。
实例:Gmail聊天编程

  1. Flash Socket:在页面中内嵌入一个使用了Socket类的 Flash 程序JavaScript经过调用此Flash程序提供的Socket接口与服务器端的Socket接口进行通讯,JavaScript在收到服务器端传送的信息后控制页面的显示。

优势:实现真正的即时通讯,而不是伪即时。
缺点:客户端必须安装Flash插件;非HTTP协议,没法自动穿越防火墙。
实例:网络互动游戏。后端

HTTP1.1和长连接

以上几种服务器“推”的技术中:长轮询和流控制其实都是基于长连接来实现的,也就是 http1.1 中所谓的 keep-alive。在一个TCP链接上能够传送多个HTTP请求和响应,减小了创建和关闭链接的消耗和延迟。设计模式

HTTP是无状态的,也就是说,浏览器和服务器每进行一次HTTP操做,就创建一次链接,但任务结束就中断链接。若是客户端浏览器访问的某个HTML或其余类型的Web页中包含有其余的Web资源,如JavaScript文件、图像文件、CSS文件等;当浏览器每遇到这样一个Web资源,就会创建一个HTTP会话

HTTP1.1和HTTP1.0相比较而言,最大的区别就是HTTP1.1默认支持持久链接(最新的 http1.0 能够显示的指定 keep-alive),但仍是无状态的,或者说是不能够信任的。

在向客户发送所请求文件的同时,服务器并无存储关于该客户的任何状态信息。即使某个客户在几秒钟内再次请求同一个对象,服务器也不会响应说:本身刚刚给它发送了这个对象。相反,服务器从新发送这个对象,由于它已经完全忘记早先作过什么。既然HTTP服务器不维护客户的状态信息,咱们因而 说HTTP是一个无状态的协议(stateless protocol)。

基于http协议的长链接减小了请求,减小了创建链接的时间,可是每次交互都是由客户端发起的,客户端发送消息,服务端才能返回客户端消息。由于客户端也不知道服务端何时会把结果准备好,因此客户端的不少请求是多余的,仅是维持一个心跳,浪费了带宽。

WebSocket

WebSocket简介

WebSocket 协议在2008年诞生,2011年成为国际标准。全部浏览器都已经支持了。WebSocket通讯协议于2011年被IETF定为标准RFC 6455,并被RFC7936所补充规范。

关于HTML5的故事不少人都是知道的,w3c放弃了HTML,而后有一群人(也有说是这些人供职的公司,不过官方的文档上是说的我的)创立了WHATWG组织来推进HTML语言的继续发展,同时,他们还发展了不少关于Web的技术标准,这些标准不断地被官方所接受。WebSocket就属于WHATWG发布的Web Application的一部分(即HTML5)的产物。

它的最大特色就是,服务器能够主动向客户端推送信息,客户端也能够主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。

  • 创建在 TCP 协议之上,服务器端的实现比较容易。

  • 与 HTTP 协议有着良好的兼容性。默认端口也是80和443,而且握手阶段采用 HTTP 协议,所以握手时不容易屏蔽,能经过各类 HTTP 代理服务器。

  • 数据格式比较轻量,性能开销小,通讯高效。

  • 能够发送文本,也能够发送二进制数据。

  • 没有同源限制,客户端能够与任意服务器通讯。

  • 协议标识符是ws(若是加密,则为wss),服务器网址就是 URL。

websocket

其中 Upgrade: websocket Connection: Upgrade 告诉服务器咱们发起的是一个 WebSocket 请求。Sec-WebSocket-Key 是一个 Base64encode 的值,这个是浏览器随机生成的,验证服务器是否是真的是Websocket助理。
而后,Sec_WebSocket-Protocol 是一个用户定义的字符串,用来区分同URL下,不一样的服务所须要的协议。
最后,Sec-WebSocket-Version 是告诉服务器所使用的WebsocketDraft(协议版本)。

HTML5 Web Socket API

详细接口文档:MDN WebSocket

建立对象:
var ws = new WebSocket(url,name);
url为WebSocket服务器的地址,name为发起握手的协议名称,为可选择项。

发送文本消息:
ws.send(msg);
msg为文本消息,对于其余类型的能够经过二进制形式发送。

接收消息:
ws.onmessage = (function(){...})();

错误处理:
ws.onerror = (function(){...})();

关闭链接:
ws.close();

咱们借助这个测试接口 wss://echo.websocket.org 来作一个小demo。

公用html(下面的代码基本也是这个结构):

<h1>客户端简单例子</h1>
<i>这里咱们走Kaazing WebSocket为咱们提供的接口,这个接口将完整返回咱们所发送的数据。</i>
<p>状态:<strong id="state"></strong></p>
<p>返回数据:<strong id="msg"></strong></p>
<input id="sendText" type="text" name="">
<button id="sendBtn">发送</button>
<button id="closeBtn">关闭</button>

JS:

var show = document.getElementById('state'),
    msg  = document.getElementById('msg'),
    st   = document.getElementById('sendText'),
    sb   = document.getElementById('sendBtn');

if ("WebSocket" in window) {
  var ws = new WebSocket('wss://echo.websocket.org');

  ws.onopen = function(e) { 
    show.innerText = 'WebSocket链接成功~';
    ws.send('Hello WebSockets!');
  };

  ws.onmessage = function(e) {
    msg.innerText = e.data;
  };

  ws.onclose = function(e) {
    show.innerText = 'WebSocket链接关闭~';
  }

  sb.addEventListener('click',function(){
    ws.send(st.value);
  })

}else{
  alert('你的浏览器不支持WebSocket');
}

demo1

nodejs-websocket

nodejs-websocket是一个nodeJs的模块,咱们能够用它来轻易地为咱们以前的代码单独搭建一个WebSocket的nodeJs服务端。

yarn add nodejs-websocket
var ws = require("nodejs-websocket")

// Scream server example: "hi" -> "HI!!!"
var server = ws.createServer(function (conn) {
    console.log("New connection")
    conn.on("text", function (str) {
        console.log("Received "+str)
        conn.sendText(str.toUpperCase()+"!!!")
    })
    conn.on("close", function (code, reason) {
        console.log("Connection closed")
    })
}).listen(8001)

Socket.io

在某种程度上,socket.io就是websocket,其实socket.io与websocket不是一回事,并且websocket能够说是socket.io的一个子集,socket.io的底层实现其实有5种方式,websocket只是其中一种,只不过在默认的状况下,咱们创建的socket.io链接,底层也是调用websocket的实例。当咱们io.connect()创建一个socket链接的时候,返回的是namespace实例,namespace实例中有个socket实例,当新建一个链接,或者发送一条消息的时候,namespace->socket->transport->websocket(xhrpolling...),其实发送一条消息真正的发送者仍是底层的websocket或是xhrpolling或其余的几种,而socket.io只是一个组织者,当咱们须要创建链接的时候,它本身会在其内部挑选一种链接方式,而后实现链接。

Socket.io都实现了Polling中的那些通讯机制呢?

  • Adobe® Flash® Socket
  • AJAX long polling
  • AJAX multipart streaming
  • Forever Iframe
  • JSONP Polling

WebSocket和HTTP和Socket

应用层的协议,WebSocket在现代的软件开发中被愈来愈多的实践,和HTTP有一些类似的地方,并且有人也会把WebSocket和Socket混为一谈,那么他们之间到底有什么异同呢?

WebSocket和HTTP

咱们先看两个协议的截图来领会下。

websocket

http

相同点
  1. 都是基于TCP的应用层协议。
  2. 都使用Request/Response模型进行链接的创建。
  3. 在链接的创建过程当中对错误的处理方式相同,在这个阶段WS可能返回和HTTP相同的返回码。
  4. 均可以在网络中传输数据。
不一样点
  1. WS使用HTTP来创建链接,可是定义了一系列新的header域,这些域在HTTP中并不会使用。
  2. WS的链接不能经过中间人来转发,它必须是一个直接链接。
  3. WS链接创建以后,通讯双方均可以在任什么时候刻向另外一方发送数据。
  4. WS链接创建以后,数据的传输使用帧来传递,再也不须要Request消息。
  5. WS的数据帧有序。

WebSocket和Socket

其实就像Java和JavaScript同样,WebSocket和Socket并无太大的关系。

Socket能够有不少意思,和IT较相关的本意大体是指在端到端的一个链接中,这两个端叫作Socket。对于IT从业者来讲,它每每指的是TCP/IP网络环境中的两个链接端,大多数的API提供者(如操做系统,JDK)每每会提供基于这种概念的接口,因此对于开发者来讲也每每是在说一种编程概念。同时,操做系统中进程间通讯也有Socket的概念,但这个Socket就不是基于网络传输层的协议了。

Socket 其实并非一个协议。它工做在 OSI 模型会话层(第5层),是为了方便你们直接使用更底层协议(通常是 TCP 或 UDP )而存在的一个抽象层。

Socket是应用层与TCP/IP协议族通讯的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来讲,一组简单的接口就是所有,让Socket去组织数据,以符合指定的协议。

http

主机 A 的应用程序要能和主机 B 的应用程序通讯,必须经过 Socket 创建链接,而创建 Socket 链接必须须要底层 TCP/IP 协议来创建 TCP 链接。创建 TCP 链接须要底层 IP 协议来寻址网络中的主机。咱们知道网络层使用的 IP 协议能够帮助咱们根据 IP 地址来找到目标主机,可是一台主机上可能运行着多个应用程序,如何才能与指定的应用程序通讯就要经过 TCP 或 UPD 的地址也就是端口号来指定。这样就能够经过一个 Socket 实例惟一表明一个主机上的一个应用程序的通讯链路了。

而 WebSocket 则不一样,它是一个完整的 应用层协议,包含一套标准的 API 。

因此,从使用上来讲,WebSocket 更易用,而 Socket 更灵活。

浏览器支持

浏览器支持

websocket api在浏览器端的普遍实现彷佛只是一个时间问题了, 值得注意的是服务器端没有标准的api, 各个实现都有本身的一套api, 而且tcp也没有相似的提案, 因此使用websocket开发服务器端有必定的风险.可能会被锁定在某个平台上或者未来被迫升级。

本文相关的Demo已经放在做者的Github上:小楼兰的github