借助Nodejs探究WebSocket


文章導讀:

 

一、概述-what's WebSocket?

1.1 為什么我們需要WebSocket這樣的實時的通信協議?

WebSocket是web通信方式的一種,像我們熟知的HTTP協議也是web通信方式的一種。但是我們知道HTTP協議是一種無狀態的協議,其服務端本身不具備識別客戶端的能力,必須借助外部的一些信息比如說session和cookie,才能與特定的客戶端保持通信。也就是說我們所發送的每一個HTTP的請求都會帶上請求頭中一些相應的信息還有cookie,這明顯會增加我們傳輸的信息的體量從而帶來一定的網絡延遲,對於一些對通信的實時性要求比較高的應用來說就是不可忍受的了,比如說聊天程序或者是運行在瀏覽器中的實時小游戲。最郁悶的卻還是這些頭信息和cookie往往對於服務器響應客戶端的請求來說是多余的,也就是說雖然我每個請求都帶了這些信息,但是服務器與客戶端的交互過程中可能根本用不上這些信息。

為了改善HTTP請求的這種網絡延遲的情況,也出現了一些適應不同需求的其他的[web通信]方式,比如說:輪詢,長輪詢(long-polling),數據流,EventSouce等等,WebSocket便是其中一種。

實際上大多數基於因特網(或者局域網)的網絡鏈接通常都包含長連接和基於TCP套接字的雙向消息交換。但是TCP協議是屬於最底層的網絡通信協議了,讓一些不能信任的客戶端腳本去訪問底層的TCP套接字顯然是不太安全的,因此WebSocket實現了一種較為安全的方案,它允許客戶端腳本在客戶端和支持WebSocket協議的服務器之間創建雙向的套接字連接。從而使實時通信的某些網絡操作變得簡單。

1.2 WebSocket是如何工作的?

我們知道了WebSocket的主要作用是,允許服務器端與客戶端進行全雙工(full-duplex)的實時通信。這里有個例子特別好:HTTP協議像發電子郵件,發出后必須等待對方回信;WebSocket則是像打電話,服務器端和客戶端可以同時向對方發送數據,它們之間存着一條持續打開的數據通道。

我們先看一下一個基於WebSocket協議通信的請求頭和響應頭(下面簡單實例中的一個消息頭):

其中與WebSocket協議相關的信息:

1 Upgrade:websocket-------HTTP1.1協議規定,Upgrade頭信息表示將通信協議從HTTP/1.1轉向該項所指定的協議;
2 Connection:Upgrade------表示瀏覽器通知服務器,如果允許,就將通信協議升級到websocket協議;
3 Origin------------------用於驗證瀏覽器域名是否在服務器許可的范圍內;
4 Sec-WebSocket-Key-------則是用於握手協議的密鑰,是base64編碼的16字節隨機字符串;
5 Sec-WebSocket-Accept----是服務器在瀏覽器提供的Sec-WebSocket-Key字符串后面,添加“258EAFA5-E914-47DA-95CA-C5AB0DC85B11” 字符串,然后再取sha-1的hash值。瀏覽器將對這個值進行驗證,以證明確實是目標服務器回應了webSocket請求;
6.Sec-WebSocket-Location--一般情況下還有這個響應消息頭用來表示進行通信的WebSocket網址,這里面可能是因為我例子中設置了127.0.0.1,所以這個信息省略掉了。

客戶端通過一個WebSocket握手的過程建立一個WebSocket連接。整個過程看起來是這個樣子的:

完成握手以后,WebSocket協議就在TCP協議之上,客戶端和服務器端就可以開始傳送數據了。

websocket協議用ws表示,加密的websocket協議用wss協議,就像普通的HTTP協議用http表示,加密的HTTP協議用https表示一樣。

下面我們就通過一些實例看一下websocket的不同實現是如何應用的。

二、 運行在瀏覽器中的WebSocket客戶端+使用ws模塊搭建的簡單服務器

我們可以通過跑起來這個簡單的實例看一下如何編寫運行在瀏覽器中的WebSocket客戶端,並且看它是怎樣與服務器端交互的。

2.1 運行實例

我們把客戶端代碼和服務端代碼准備好,然后啟動服務器監聽端口,比如說8080,再然后運行我們的客戶端代碼即可看到效果。

我們的客戶端代碼寫在html文件中:

 1 var onOpen = function() {
 2                 console.log("Socket opened.");
 3                 socket.send("Hi, Server!");
 4             },
 5             onClose = function() {
 6                 console.log("Socket closed.");
 7             },
 8 
 9 
10             onMessage = function(data) {
11                 console.log("We get signal:");
12                 console.log(data);
13             },
14 
15 
16             onError = function() {
17                 console.log("We got an error.");
18             },
19 
20             
21             socket = new WebSocket("ws://127.0.0.1:8080/");
22 
23         socket.onopen = onOpen;
24         socket.onclose = onClose;
25         socket.onerror = onError;
26         socket.onmessage = onMessage;
View Code

[查看源碼]

我們通過它建立連接並且監聽open和messege等事件,與此同時,我們想要得到服務器的響應。服務器端的js代碼:

 1 var WebSocketServer = require('ws').Server;
 2 var wss = new WebSocketServer({ port: 8080 });
 3 
 4 wss.on('connection', function connection(ws) {
 5   ws.on('message', function incoming(message) {
 6     console.log('received: %s', message);
 7   });
 8 
 9   ws.send('something');
10 });
View Code

[查看源碼]

這個簡單的websocket服務器使用了[ws模塊],如果沒有安裝過,要先安裝一下:

1 sudo npm install ws

然后在我們的命令行執行:

1 node simpleWSserver.js

我們的服務器啟動之后,我們運行客戶端代碼可以看到:

瀏覽器:

        

命令行:

整個過程看起來是這個樣子的:

2.2 運行在瀏覽器中的websocket客戶端

 我們在瀏覽器中的websocket主要做的事情無非是以下幾個:

1 建立連接和關閉連接 
2 發送數據和接收數據
3 處理錯誤

對應的會觸發以下的事件:

1 onopen
2 onmessage
3 onclose
4 onerror

2.2.1 建立連接和關閉連接

通常我們新建了一個WebSocket的實例就可以建立一個連接:

1 if(window.WebSocket != undefined) {
2     var socket = new WebSocket("ws://127.0.0.1:8080/");
3 }

建立連接之后的WebSocket實例有一個readyState屬性,用來標識當前的狀態:

0-正在連接
1-連接成功
2-正在關閉
3-關閉成功

連接成功后會觸發onopen事件,這時我們就可以向服務器發送數據了:

1 var onOpen = function() {
2       console.log("Socket opened.");
3       socket.send("Hi, Server!");
4  }
5 socket.onopen = onOpen;

要是關閉連接的話就會出發onclose事件:

1 var onClose = function() {
2         console.log("Socket closed.");
3   }
4 socket.onclose = onClose;

2.2.2 發送數據和接收數據

在連接建立成功后觸發的onopen事件中我們通過send()方法發送數據給服務器:

1 socket.send("Hi, Server!");

除了發送字符串類型的數據,也可以使用 Blob 或 ArrayBuffer 對象發送二進制數據。不僅如此,我們還可以發送JSON數據:

 1  var onOpen = function() {
 2       var msg = {
 3          type: "message",
 4          text: "something",
 5          id:   "number",
 6          date: Date.now()
 7       };
 8 
 9    // Send the msg object as a JSON-formatted string.
10    socket.send(JSON.stringify(msg));
11   }
12  socket.onopen = onOpen;

這時會觸發服務器端的message事件:

1 ws.on('message', function incoming(message) {
2     console.log('received: %s', message);
3   });

同時,服務器端發來信息的時候:

1 ws.send('something');

也會觸發客戶端的onmessage事件:

1 var onMessage = function(data) {
2         console.log("We get signal:");
3         console.log(data);
4   }
5 socket.onmessage = onMessage;

2.2.3 處理錯誤

發生的錯誤會觸發onerror事件:

1 var onError = function() {
2      console.log("We got an error.");
3  }
4 socket.onerror = onError;

 

三、Node中的WebSocket

 WebSocket在Node中的實現[WebSocket-Node]使我們可以在Nodejs中使用websokcet開發客戶端和服務器端實時交互的應用程序。我們可以運行客戶端和服務器實時交換隨機數的例子看看它是怎么工作的:

啟動服務器:

node socketserver.js

[查看源碼]

啟動客戶端:

node socketclient.js

[查看源碼]

     

四、socket.io

現在很流行的websocket的實現socket.io同樣包括客戶端和服務器端兩部分。它不僅簡化了接口,使得操作更容易,而且對於那些不支持WebSocket的瀏覽器,會自動降為Ajax連接,最大限度地保證了兼容性。它的目標是統一通信機制,使得所有瀏覽器和移動設備都可以進行實時通信。

4.1 socket.io與WebSocket的區別在哪里呢? 

websocket是瀏覽器對象,websocket api是瀏覽器提供給我們的用於瀏覽器和服務器實時通信的接口。

websocket在node中的實現使我們可以開發服務端程序時使用websocket的特性。

在我們使用websocket的時候,因為他是瀏覽器提供的接口,所以會涉及到一些兼容性和支持性的問題。如果我們對程序所運行的環境或局限不是那么了解的化,那么可能會出現問題:

[Differences between socket.io and websocket] 。而socket.io則是進化了的websocket api。socket.io建立在websocket之上,它在合適的時候使用websocket。

 4.2 socket.io實現聊天室

使用websocket或socket.io可以從一個簡單的聊天室程序開始。對於socket.io來說,這非常容易。

基於node,這里使用express和socket.io:

1 npm install --save express@4.10.2
2 npm install --save socket.io

那么我們就可以開始寫聊天程序了。它需要的就是一個客戶端的聊天窗口和一個用來接收消息和分發消息的服務器。

我們需要三個文件,分別新建:package.json,index.js,index.html.

package.json:

1 {
2   "name": "chat-application",
3   "version": "0.0.1",
4   "description": "my first socket.io app",
5   "dependencies": {
6     "socket.io": "^1.3.5"
7   }
8 }
View Code

index.js:

 1 var app = require('express')();
 2 var http = require('http').Server(app);
 3 var io = require('socket.io')(http);
 4 
 5 app.get('/', function(req, res){
 6   res.sendfile('index.html');
 7 });
 8 
 9 io.on('connection', function(socket){
10   console.log('a user connected');
11   //監聽客戶端的消息
12   socket.on('chat message', function(msg){
13       //用於將消息發送給每個人,包括發送者
14     io.emit('chat message', msg);
15   });
16   socket.on('disconnect', function(){
17     console.log('user disconnected');
18   });
19 });
20 
21 http.listen(3000, function(){
22   console.log('listening on *:3000');
23 });
View Code

index.html:

 1 <!doctype html>
 2 <html>
 3   <head>
 4     <title>Socket.IO chat</title>
 5     <style>
 6       * { margin: 0; padding: 0; box-sizing: border-box; }
 7       body { font: 13px Helvetica, Arial; }
 8       form { background: #000; padding: 3px; position: fixed; bottom: 0; width: 100%; }
 9       form input { border: 0; padding: 10px; width: 90%; margin-right: .5%; }
10       form button { width: 9%; background: rgb(130, 224, 255); border: none; padding: 10px; }
11       #messages { list-style-type: none; margin: 0; padding: 0; }
12       #messages li { padding: 5px 10px; }
13       #messages li:nth-child(odd) { background: #eee; }
14     </style>
15   </head>
16   <body>
17     <ul id="messages"></ul>
18     <form action="">
19       <input id="m" autocomplete="off" /><button>Send</button>
20     </form>
21     <script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
22     <script src="http://code.jquery.com/jquery-1.11.1.js"></script>
23     <script>
24       var socket = io();
25       $('form').submit(function(){
26         //io.emit提供給我們可以發送給所有人的事件io.emit('some event', { for: 'everyone' });
27         socket.emit('chat message', $('#m').val());
28         $('#m').val('');
29         return false;
30       });
31       socket.on('chat message', function(msg){
32         $('#messages').append($('<li>').text(msg));
33       });
34     </script>
35   </body>
36 </html>
View Code

先運行:

node index.js

在打開兩個http://localhost:3000的窗口就可以開始聊天了:

     

[查看源代碼]

socket.io官網上有很詳細的使用方法和教程:[socket.io doc]

五、擴展閱讀

[瀏覽器對象-WebSocket]

[web通信]

[細說WebSocket]

[WebSocket MDN]

[WebSocket-Node implementation]

[A Guide For WebSocket]

[socket.IO]

[writing websocket client]

[deferences between socket.io and websocket]

[websocket and socketio]

[socket.io application]


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM