第一次看到 ws:// 和 wss:// 時(shí)候,感覺(jué)好高級(jí)啊,還有這種協(xié)議。
Websocket 歷史
WebSocket是在2008年6月誕生的1。經(jīng)由IEFT標(biāo)準(zhǔn)化后,2009年chrome 4第一個(gè)提供了該標(biāo)準(zhǔn)支持,并默認(rèn)啟用。于2011年由IEFT標(biāo)準(zhǔn)化為RFC 6455。
現(xiàn)在的瀏覽器均已支持該標(biāo)準(zhǔn)。
Websocket 出現(xiàn)的背景
思考一下我們經(jīng)常遇到的一種需求場(chǎng)景,要求在某個(gè)網(wǎng)頁(yè)下,網(wǎng)頁(yè)的內(nèi)容可以實(shí)時(shí)更新。
這種情況下,最大眾化的方式就是輪詢(xún)接口了,即通過(guò)定時(shí)器,定時(shí)請(qǐng)求接口,獲取到最新的信息后,將內(nèi)容更新到頁(yè)面中,如下:
setInterval(() => {
queryAPI().then(() => update());
}, 1000);
但是我們知道,這種定時(shí)器的延時(shí)并不是很精確,而且加上接口的請(qǐng)求時(shí)延,實(shí)際時(shí)間可能不止代碼中所預(yù)先設(shè)定的時(shí)間長(zhǎng)度,所以這種實(shí)時(shí)更新是偽實(shí)時(shí)更新。
除此之外,還有一點(diǎn)可能會(huì)經(jīng)常遇到,即,我們更新信息并總是要更新整個(gè)頁(yè)面上所有可以看到的信息,我們更關(guān)注一些經(jīng)常變化的信息,比如狀態(tài),狀態(tài)的信息可能大小只有幾個(gè)字節(jié),但是我們輪詢(xún)接口拿到的信息卻是這個(gè)頁(yè)面的所有信息,大小自然不只幾個(gè)字節(jié),但是除狀態(tài)以外的信息都可以視作是冗余的。
我們實(shí)際只需要一個(gè)字段,而且即使后端提供只返回狀態(tài)的接口,但實(shí)際在一個(gè)請(qǐng)求中還要計(jì)算ip報(bào)文頭的大小,依舊是很占用帶寬的。
輪詢(xún)這種解決方案目前依舊是非常流行,最新的輪詢(xún)技術(shù)是Comet,這種技術(shù)雖然可以實(shí)現(xiàn)雙向通信,但仍然需要反復(fù)發(fā)出請(qǐng)求。而且在Comet中普遍采用的HTTP長(zhǎng)連接也會(huì)消耗服務(wù)器資源2。
Websocket通信模式
了解網(wǎng)絡(luò)的都知道,數(shù)據(jù)傳輸分為單工、半雙工、全雙工三種工作模式。
Websocket是基于TCP的,使用全雙工通信模式的協(xié)議,他使得客戶(hù)端和服務(wù)端之間的數(shù)據(jù)交換變得更簡(jiǎn)單。而且,作為一個(gè)工作在全雙工模式下的協(xié)議,服務(wù)端可以在建立連接后隨時(shí)向客戶(hù)端推送消息。
由于協(xié)議是基于TCP的,所以websocket也是需要建連和關(guān)閉連接的,但要注意的是,一般在websocket的握手通常指的是:客戶(hù)端發(fā)送一個(gè)http請(qǐng)求到服務(wù)端,服務(wù)端響應(yīng)后標(biāo)志這個(gè)鏈接建立起來(lái)。而不是指tcp的三次握手。
另外在RFC 6455 1.1節(jié)「Background」中介紹:WebSocket通過(guò)HTTP端口的80和443進(jìn)行工作,并支持HTTP代理和中介。
原文:it is designed to work over HTTP ports 80 and 443 as well as to support HTTP proxies and intermediaries
為了實(shí)現(xiàn)和HTTP的兼容性,WebSocket握手使用HTTP的Upgrade頭將HTTP協(xié)議轉(zhuǎn)換成Websocket協(xié)議。
作為一種協(xié)議,websocket自然也是有其用于協(xié)議控制的頭部信息的,但是相對(duì)于HTTP請(qǐng)求每次都要帶上完整的頭部信息,傳輸數(shù)據(jù)時(shí),websocket數(shù)據(jù)包的頭部信息就相對(duì)較小,從而降低了控制開(kāi)銷(xiāo)。
相對(duì)于前文所提到的輪詢(xún)接口,websocket可以做到服務(wù)端直接向客戶(hù)端傳輸數(shù)據(jù),省去了客戶(hù)端發(fā)起請(qǐng)求的步驟,同時(shí)沒(méi)有間隔時(shí)間,只要服務(wù)端內(nèi)容變化,就可以告知客戶(hù)端,實(shí)時(shí)性上有了很大的提高。
Websocket 使用
WebSocket使用十分簡(jiǎn)單,只需要關(guān)注以下API:
- WebSocket(url[, protocol]) 構(gòu)造函數(shù)
使用 new WebSocket(xxx) 創(chuàng)建對(duì)象時(shí),會(huì)同時(shí)建立與服務(wù)器的連接
- WebSocket.onopen , WebSocket.onclose
分別對(duì)應(yīng)連接成功、失敗時(shí)的回調(diào),這里可以做一些初始化、銷(xiāo)毀的工作
- WebSocket.onmessage
實(shí)際處理數(shù)據(jù)是用的該函數(shù)
在數(shù)據(jù)處理完成后,需要移除回調(diào)函數(shù),不然可能會(huì)影響到其他地方的處理
const ws = new WebSocket('ws://sdf.com');
function handleData(evt) {
// handle server data.
}
ws.onmessage = handleData;
ws.addEventListener('message', handleData);
- WebSocket.send
主動(dòng)向服務(wù)端發(fā)送消息,可以通過(guò)send和onmessage進(jìn)行數(shù)據(jù)互動(dòng),如:
ws.send('list');
ws.onmessage = evt => {
const data = evt.data;
if (data === 'hello') {
console.log('world');
return ;
}
try {
const obj = JSON.parse(data);
switch (obj.type) {
case 'list':
// do something.
}
} catch (ex) {}
}
- WebSocket.close
關(guān)閉連接
Node服務(wù)端的實(shí)現(xiàn),這個(gè)就參考相關(guān)的庫(kù)吧,比較復(fù)雜。
衍生知識(shí)
http協(xié)議至今,主要經(jīng)歷了三個(gè)版本。
- http1.0 短連接,單工通信
- http/1.0默認(rèn)的模型是短連接,每個(gè)HTTP請(qǐng)求都由他自己獨(dú)立完成,下圖左1,可以看到每一個(gè)http請(qǐng)求都對(duì)應(yīng)了一個(gè)建立連接關(guān)閉連接的階段,每一個(gè)請(qǐng)求都有TCP握手和揮手的階段。
- 在這個(gè)模型下,想要做到實(shí)時(shí)更新頁(yè)面數(shù)據(jù),只能考慮輪詢(xún)。
- http1.1 支持長(zhǎng)鏈接,半雙工通信
- 1.0之后的版本,1.1會(huì)讓某個(gè)連接保持一定的時(shí)間,在這段時(shí)間里重復(fù)發(fā)送一系列請(qǐng)求(下圖左2),就是保活。
- http2.0 支持多路復(fù)用,全雙工通信
來(lái)源:
https://www.cnblogs.com/keepsmart/p/16007094.html






