在上一篇文章當中介紹了一下websocket是什麼和産生原因以及和http協定的差別,我們今天來讨論一下websocket中的用戶端和服務端程式。
1.websocket中的API
我們看一下就websocket JavaScript定義的接口:
[Constructor(in DOMString url, optional in DOMString protocol)]
interface WebSocket {
readonly attribute DOMString URL;
// ready state
const unsigned short CONNECTING = 0;
const unsigned short OPEN = 1;
const unsigned short CLOSED = 2;
readonly attribute unsigned short readyState;
readonly attribute unsigned long bufferedAmount;
// networking
attribute Function onopen;
attribute Function onmessage;
attribute Function onclose;
boolean send(in DOMString data);
void close();
};
WebSocket implements EventTarget;
既然是一個協定就是要有用戶端和服務端,服務端每種語言有自己的API
websocket
的作用已經完全可以代替ajax,用戶端API和ajax十分相似下面我們來具體的介紹一下:
- onopen,表示和伺服器建立連結,建立連接配接之後才能進入下面的步驟,這個對應于
open方法ajax
- send,想使用者伺服器發送資料,對應于
的send方法ajax
- onmessage,建立連接配接之後,用戶端收到伺服器發來的資料,這個而對應
的responseTextajax
- onerror,當連接配接出現錯誤的時候,接收錯誤資訊,這個
裡面沒有ajax
- onclose,監聽消息的關閉,當連接配接結束的時候出發,這個
裡面沒有ajax
-
close,主動關閉socket連接配接
再來介紹一下ready state表示的四種狀态(ajax中是五種)
- CONNECTING (0):表示還沒建立連接配接;
- OPEN (1): 已經建立連接配接,可以進行通訊;
- CLOSING (2):通過關閉握手,正在關閉連接配接;
- CLOSED (3):連接配接已經關閉或無法打開;
2.用戶端的websocket
2.1 websocket執行個體
介紹完了API我們就要用這些API去做一些事情,下面我們通過一個執行個體來應用一下這些方法
var serverAddr = 'ws://localhost:3000';
// 建立一個Socket執行個體
var socket = new WebSocket(serverAddr);
// 打開Socket
socket.onopen = function(event) {
// 發送一個初始化消息
socket.send('I am the client and I\'m listening!');
// 監聽消息
socket.onmessage = function(event) {
console.log('Client received a message',event);
};
// 監聽Socket的關閉
socket.onclose = function(event) {
console.log('Client notified socket has closed',event);
};
// 關閉Socket....
//socket.close()
};
var connection = new WebSocket('ws://html5rocks.websocket.org/echo', ['soap', 'xmpp']);
//第二個參數可接受可選子協定,它既可以是字元串,也可以是字元串數組。每個字元串都應代表一個子協定名稱,而伺服器隻能接受數組中通過的一個子協定。通路 WebSocket 對象的 protocol 屬性可确定接受的子協定。
通過上面的例子我們就在用戶端建立了一個和伺服器建立連接配接的程式,這種方法比ajax的好處我們也在前面說過了。下面我們來做一下對比順便複習一下
序号 | 步驟 | websocket | ajax |
---|---|---|---|
1 | 建立對象 | new WebSocket(serverUrl) | $1600 |
2 | 和伺服器建立連接配接 | onopen | open |
2 | 向伺服器發送資料 | send | send |
3 | 監聽伺服器的資訊 | onmessage | readyState,status |
産生異常的應對機制 | onerror | 無,在jquery架構中有 | |
監聽連接配接關閉 | onclose | 沒有,因為不是長連接配接 | |
能否主動關閉 | close | 沒有必要主動發起 |
在來看一下ready state的對比圖:
狀态值 | websocket | ajax |
---|---|---|
還沒建立連接配接 | 還沒建立連接配接 | |
1 | 已經建立連接配接,可以進行通訊 | 請求已經建立,但是還沒有發送 |
2 | 通過關閉握手,正在關閉連接配接 | 請求已發送,正在進行中 |
3 | 連接配接已經關閉或無法打開 | 請求在進行中;通常響應中已有部分資料可用了,但是伺服器還沒有完成響應的生成 |
4 | 無 | 響應已完成;您可以擷取并使用伺服器的響應了。 |
第一個對比中前面序号是ajax運作的四個步驟,但是少了第四步驟,第四個步驟是顯示到相應的位置,在這裡滅有過多去說,通過第二個對比我們可以看出在
狀态值
中websocket和ajax有很大的差別,這還是因為
http
寫一個
websocket
協定的不同。如果對這個幾個步驟不清楚的可以看我的另一篇博文ajax全接觸
2.1 socket.io
Socket.IO使用檢測功能來判斷是否建立WebSocket連接配接,或者是AJAX long-polling連接配接,或Flash等。可快速建立實時的應用程式。Socket.IO還提供了一個NodeJS API,
來看一個執行個體:
<script src="socket.io\node_modules\socket.io-client\socket.io.js"> </script>
<script>
var socket = io('http://localhost:3000');
socket.on('news', function (data) {
console.log(data);
socket.emit('my other event', { my: 'data' });
});
</script>
Socket.IO簡化了WebSocket API,統一了傳回運輸的API。傳輸包括:
- WebSocket
- Flash Socket
- AJAX long-polling
- AJAX multipart streaming
- IFrame
-
JSONP polling
你還可以設定任意的Socket.IO構造器的第二個選項,選項包括:
port - 待連接配接的端口
transports - 一個數組,包含不同的傳輸類型
transportOptions - 傳輸的參數使用的對象,帶附加屬性
Socket.IO還提供了由本地WebSocket API提供的普通連接配接、斷開連接配接、消息事件。Socket還提供了封裝每個事件類型的方法。
後續有機會我們繼續讨論。
伺服器的websocket
握手協定已經由用戶端代碼解決了,我們這裡主要說伺服器的程式,伺服器程式我們在這裡主要是用的
nodejs
使用其他語言的可以參考一下邏輯,因為nodejs的文法 和JavaScript一樣都能 看懂。除了nodejs目前還有很多例如:
- Kaazing WebSocket Gateway(一個 Java 實作的 WebSocket Server)
- mod_pywebsocket(一個 Python 實作的 WebSocket Server)
- Netty(一個 Java 實作的網絡架構其中包括了對 WebSocket 的支援)
- WebSocket4Net(一個.net的伺服器端實作)
下一個考慮的問題:怎麼握手呢?
還幾個上一篇文章中用戶端和服務端的協定棧圖麼,
其中用戶端有一個:
Sec-WebSocket-Key
字段,伺服器有一個:
Sec-WebSocket-Accept
字段,
伺服器的
Sec-WebSocket-Accept
是由用戶端的
Sec-WebSocket-Key
計算出來的,上一篇文章說
Sec-WebSocket-Key
是用戶端随機産生的其實是不準确的,上一篇文章隻是讓大家了解這個字段,現在我們來看一下
Sec-WebSocket-Key
及産生過程:
- 取出Sec-WebSocket-Key(随機産生),與一個magic string “258EAFA5-E914-47DA-95CA-C5AB0DC85B11” 連接配接成一個新的key串
- 将新的key串SHA1編碼,生成一個由多組兩位16進制數構成的加密串
- 把加密串進行base64編碼生成最終的key,這個key就是Sec-WebSocket-Key
在考慮一個:怎麼才算握手成功呢?
為了表示伺服器同意和用戶端進行Socket連接配接, 伺服器端需要使用用戶端發送的這個Key進行校驗 ,然後傳回一個校驗過的字元串給用戶端,用戶端驗證通過後才能正式建立Socket連接配接。
當握手成功之後會執行open方法。
這個過程可能以後會詳細的解釋。
我們再來看一下服務端的程式[1]:
var app = require('http').createServer(handler);
var io = require('socket.io')(app);
var fs = require('fs');
//監聽端口
app.listen(3000);
//響應狀态
function handler (req, res) {
//讀取檔案
fs.readFile(__dirname + '/index.html',
//讀取異常,輸出錯誤
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
//設定傳回碼
res.writeHead(200);
//結束時,輸出傳回資訊
res.end(data);
});
}
//l建立連接配接
io.on('connection', function (socket) {
//傳回資料
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
這個就不做過多的解釋,注釋就很清楚了
[1]來自socket.io官方文檔
如果對我的文章感興趣可以關注的公衆号:雲影原生。
