初探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

相關文章
相關標籤/搜索