HTML5規範在傳統的web交互基礎上爲咱們帶來了衆多的新特性,隨着web技術被普遍用於web APP的開發,這些新特性得以推廣和使用,而websocket做爲一種新的web通訊技術具備巨大意義。javascript
什麼是socket?什麼是websocket?二者有什麼區別?websocket是僅僅將socket的概念移植到瀏覽器中的實現嗎?html
咱們知道,在網絡中的兩個應用程序(進程)須要全雙工相互通訊(全雙工即雙方可同時向對方發送消息),須要用到的就是socket,它可以提供端對端通訊,對於程序員來說,他只須要在某個應用程序的一端(暫且稱之爲客戶端)建立一個socket實例而且提供它所要鏈接一端(暫且稱之爲服務端)的IP地址和端口,而另一端(服務端)建立另外一個socket並綁定本地端口進行監聽,而後客戶端進行鏈接服務端,服務端接受鏈接以後雙方創建了一個端對端的TCP鏈接,在該鏈接上就能夠雙向通信了,並且一旦創建這個鏈接以後,通訊雙方就沒有客戶端服務端之分了,提供的就是端對端通訊了。咱們能夠採起這種方式構建一個桌面版的im程序,讓不一樣主機上的用戶發送消息。從本質上來講,socket並非一個新的協議,它只是爲了便於程序員進行網絡編程而對tcp/ip協議族通訊機制的一種封裝。html5
websocket是html5規範中的一個部分,它借鑑了socket這種思想,爲web應用程序客戶端和服務端之間(注意是客戶端服務端)提供了一種全雙工通訊機制。同時,它又是一種新的應用層協議,websocket協議是爲了提供web應用程序和服務端全雙工通訊而專門制定的一種應用層協議,一般它表示爲:ws://echo.websocket.org/?encoding=text HTTP/1.1,能夠看到除了前面的協議名和http不一樣以外,它的表示地址就是傳統的url地址。java
能夠看到,websocket並非簡單地將socket這一律念在瀏覽器環境中的移植,本文最後也會經過一個小的demo來進一步講述socket和websocket在使用上的區別。node
websocket的通訊原理和機制git
既然是基於瀏覽器端的web技術,那麼它的通訊確定少不了http,websocket自己雖然也是一種新的應用層協議,可是它也不可以脫離http而單獨存在。具體來說,咱們在客戶端構建一個websocket實例,而且爲它綁定一個須要鏈接到的服務器地址,當客戶端鏈接服務端的時候,會向服務端發送一個相似下面的http報文程序員
能夠看到,這是一個http get請求報文,注意該報文中有一個upgrade首部,它的做用是告訴服務端須要將通訊協議切換到websocket,若是服務端支持websocket協議,那麼它就會將本身的通訊協議切換到websocket,同時發給客戶端相似於如下的一個響應報文頭github
返回的狀態碼爲101,表示贊成客戶端協議轉換請求,並將它轉換爲websocket協議。以上過程都是利用http通訊完成的,稱之爲websocket協議握手(websocket Protocol handshake),進過這握手以後,客戶端和服務端就創建了websocket鏈接,之後的通訊走的都是websocket協議了。因此總結爲websocket握手須要藉助於http協議,創建鏈接後通訊過程使用websocket協議。同時須要瞭解的是,該websocket鏈接仍是基於咱們剛纔發起http鏈接的那個TCP鏈接。一旦創建鏈接以後,咱們就能夠進行數據傳輸了,websocket提供兩種數據傳輸:文本數據和二進制數據。web
基於以上分析,咱們能夠看到,websocket可以提供低延遲,高性能的客戶端與服務端的雙向數據通訊。它顛覆了以前web開發的請求處理響應模式,而且提供了一種真正意義上的客戶端請求,服務器推送數據的模式,特別適合實時數據交互應用開發。編程
在websocket以前,咱們在web上要獲得實時數據交互都採用了哪些方式?
1)按期輪詢的方式:
客戶端按照某個時間間隔不斷地向服務端發送請求,請求服務端的最新數據而後更新客戶端顯示。這種方式實際上浪費了大量流量而且對服務端形成了很大壓力。
2)comet技術
comet並非一種新的通訊技術,它是在客戶端請求服務端這個模式上的一種hack技術,一般來說,它主要分爲如下兩種作法
(1)基於長輪詢的服務端推送技術
具體來說,就是客戶端首先給服務端發送一個請求,服務端收到該請求以後若是數據沒有更新則並不當即返回,服務端阻塞請求的返回,直到數據發生了更新或者發生了鏈接超時,服務端返回數據以後客戶端再次發送一樣的請求,以下所示:
2)基於流式數據傳輸的長鏈接
一般的作法是在頁面中嵌入一個隱藏的iframe,而後讓這個iframe的src屬性指向咱們請求的一個服務端地址,而且爲了數據更新,咱們將頁面上數據更新操做封裝爲一個js函數,將函數名當作參數傳遞到這個地址當中,
服務端收到請求後解析地址取出參數(客戶端js函數調用名),每當有數據更新的時候,返回對客戶端函數的調用,而且將要跟新的數據以js函數的參數填入到返回內容當中,例如返回「<script type="text/javascript">update("data")</script>
」這樣一個字符串,意味着以data爲參數調用客戶端update函數進行客戶端view更新。基本模型以下所示:
能夠看到comet技術是針對客戶端請求服務器響應模型而模擬出的一個服務端推送數據實時更新技術。並且因爲瀏覽器兼容性不可以普遍應用。
固然並非說這些技術沒有用,就算websocket已經做爲規範被提出並實現,可是對於老式瀏覽器,咱們依然須要將它降級爲以上方式來實現實時交互和服務端數據推送。
到此爲止,咱們明白了websocket的原理,下面經過一個簡單的聊天應用來再次加深下對websocket的理解。
該應用需求很簡單,就是在web選項卡中打開兩個網頁,模擬兩個web客戶端實現聊天功能。
首先是客戶端以下:
client.html
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8"> <title></title> <style> *{ margin: 0; padding: 0; } .message{ width: 60%; margin: 0 10px; display: inline-block; text-align: center; height: 40px; line-height: 40px; font-size: 20px; border-radius: 5px; border: 1px solid #B3D33F; } .form{ width:100%; position: fixed; bottom: 300px; left: 0; } .connect{ height: 40px; vertical-align: top; /* padding: 0; */ width: 80px; font-size: 20px; border-radius: 5px; border: none; background: #B3D33F; color: #fff; } </style> </head> <body> <ul id="content"></ul> <form class="form"> <input type="text" placeholder="請輸入發送的消息" class="message" id="message"/> <input type="button" value="發送" id="send" class="connect"/> <input type="button" value="鏈接" id="connect" class="connect"/> </form> <script></script> </body> </html>
客戶端js代碼
var oUl=document.getElementById('content'); var oConnect=document.getElementById('connect'); var oSend=document.getElementById('send'); var oInput=document.getElementById('message'); var ws=null; oConnect.onclick=function(){ ws=new WebSocket('ws://localhost:5000'); ws.onopen=function(){ oUl.innerHTML+="<li>客戶端已鏈接</li>"; } ws.onmessage=function(evt){ oUl.innerHTML+="<li>"+evt.data+"</li>"; } ws.onclose=function(){ oUl.innerHTML+="<li>客戶端已斷開鏈接</li>"; }; ws.onerror=function(evt){ oUl.innerHTML+="<li>"+evt.data+"</li>"; }; }; oSend.onclick=function(){ if(ws){ ws.send(oInput.value); } }
這裏使用的是w3c規範中關於HTML5 websocket API的原生API,這些api很簡單,就是利用new WebSocket建立一個指定鏈接服務端地址的ws實例,而後爲該實例註冊onopen(鏈接服務端),onmessage(接受服務端數據),onclose(關閉鏈接)以及ws.send(創建鏈接後)發送請求。上面說了那麼多,事實上能夠看到html5 websocket API自己是很簡單的一個對象和它的幾個方法而已。
服務端採用nodejs,這裏須要基於一個nodejs-websocket的nodejs服務端的庫,它是一個輕量級的nodejs websocket server端的實現,實際上也是使用nodejs提供的net模塊寫成的。
server.js
var app=require('http').createServer(handler); var ws=require('nodejs-websocket'); var fs=require('fs'); app.listen(80); function handler(req,res){ fs.readFile(__dirname+'/client.html',function(err,data){ if(err){ res.writeHead(500); return res.end('error '); } res.writeHead(200); res.end(data); }); } var server=ws.createServer(function(conn){ console.log('new conneciton'); conn.on("text",function(str){ broadcast(server,str); }); conn.on("close",function(code,reason){ console.log('connection closed'); }) }).listen(5000); function broadcast(server, msg) { server.connections.forEach(function (conn) { conn.sendText(msg); }) }
首先利用http模塊監聽用戶的http請求並顯示client.html界面,而後建立一個websocket服務端等待用戶鏈接,在接收到用戶發送來的數據以後將它廣播到全部鏈接到的客戶端。
下面咱們打開兩個瀏覽器選項卡模擬兩個客戶端進行鏈接,
客戶端一鏈接:
請求響應報文以下:
能夠看到此次握手和咱們以前講的一模一樣,
客戶端二的鏈接過程和1是同樣的,這裏爲了查看咱們使用ff瀏覽器,兩個客戶端的鏈接狀況以下:
發送消息狀況以下:
能夠看到,雙方發送的消息被服務端廣播給了每一個和本身鏈接的客戶端。
從以上咱們能夠看到,要想作一個點對點的im應用,websocket採起的方式是讓全部客戶端鏈接服務端,服務器將不一樣客戶端發送給本身的消息進行轉發或者廣播,而對於原始的socket,只要兩端創建鏈接以後,就能夠發送端對端的數據,不須要通過第三方的轉發,這也是websocket不一樣於socket的一個重要特色。
最後,本文爲了說明html5規範中的websocket在客戶端採用了原生的API,實際開發中,有比較著名的兩個庫socket.io和sockjs,它們都對原始的API作了進一步封裝,提供了更多功能,都分爲客戶端和服務端的實現,實際應用中,能夠選擇使用。