websocket 是 HTML5 提供的一種長鏈接雙向通訊協議,使得客戶端和服務器之間的數據交換更簡單,允許服務端主動向客戶端推送數據,并且客戶端與服務端只需連接一次,就可以保持長久連接,并進行數據通信。
websocket 與 http 區別:
ajax 輪詢與 websocket 通信原理如圖:
websocket 就是為了解決客戶端發起多個 http 請求到服務器資源,瀏覽器必須要經過長時間的,輪詢問題而生的,實現多路復用。它最大特點就是服務器可以主動向客戶端推送信息。
2.1、創建對象:
let ws=new WebSocket( url , [ protocol ] )
url:指定連接的后臺服務地址。
protocol:指定可接受的子協議,是可選參數。
2.2、對象屬性
readyState:表示連接狀態,是一個只讀屬性。
使用語法:ws.readyState
返回的值有4個,分別表示的意義:
bufferedAmount:已被放入等待傳輸隊列,但是還沒有發出的 UTF-8 文本字節數,也是只讀屬性。
2.3、對象事件
onopen - 連接時觸發,用于指定連接成功后的回調函數。
使用語法:
// 方法一 :只可以指定一個回調函數
ws.onopen=function(){
}
//方法二 :可以指定多個回調函數
ws.addEventListener('open',function(){
})
onclose - 關閉時觸發,指定連接關閉時回調函數。
使用語法:與 onopen 完全一致。
onmessage - 客戶端接收服務端數據時觸發,指定回調函數。
使用語法:
// 方法一:
ws.inmessage=function(event){
let data=event.data
//服務器傳給客戶端的數據
}
// 方法二:
ws.addEventListener('inmessage', function(event){
let data=event.data
//服務器傳給客戶端的數據
})
onerror - 通信發生錯誤時觸發,并指定回調函數。
使用語法:
//方法一
ws.onerror=function(){
//錯誤處理
}
//方法二
ws.addEventListener('error',function(){
//錯誤處理
})
2.3、對象事件
send - 用于向服務器發送數據。
使用語法:
ws.send( data )
data :是發給服務器的數據,這個數據可以是字符串、數組、json、Blob 對象或 ArrayBuffer 對象等。
如發送 Blob 對象例子:
var file=document.querySelector('input='file'').files[0]
ws.send(file)
close - 關閉連接
使用語法:
ws.close()
WebSocket的出現,使得瀏覽器具備了實時雙向通信的能力。本文由淺入深,介紹了WebSocket如何建立連接、交換數據的細節,以及數據幀的格式。此外,還簡要介紹了針對WebSocket的安全攻擊,以及協議是如何抵御類似攻擊的。
HTML5開始提供的一種瀏覽器與服務器進行全雙工通訊的網絡技術,屬于應用層協議。它基于TCP傳輸協議,并復用HTTP的握手通道。
對大部分web開發者來說,上面這段描述有點枯燥,其實只要記住幾點:
WebSocket可以在瀏覽器里使用
支持雙向通信
使用很簡單
1、有哪些優點
說到優點,這里的對比參照物是HTTP協議,概括地說就是:支持雙向通信,更靈活,更高效,可擴展性更好。
支持雙向通信,實時性更強。
更好的二進制支持。
較少的控制開銷。連接創建后,ws客戶端、服務端進行數據交換時,協議控制的數據包頭部較小。在不包含頭部的情況下,服務端到客戶端的包頭只有2~10字節(取決于數據包長度),客戶端到服務端的的話,需要加上額外的4字節的掩碼。而HTTP協議每次通信都需要攜帶完整的頭部。
支持擴展。ws協議定義了擴展,用戶可以擴展協議,或者實現自定義的子協議。(比如支持自定義壓縮算法等)
對于后面兩點,沒有研究過WebSocket協議規范的同學可能理解起來不夠直觀,但不影響對WebSocket的學習和使用。
2、需要學習哪些東西
對網絡應用層協議的學習來說,最重要的往往就是連接建立過程、數據交換教程。當然,數據的格式是逃不掉的,因為它直接決定了協議本身的能力。好的數據格式能讓協議更高效、擴展性更好。
下文主要圍繞下面幾點展開:
如何建立連接
如何交換數據
數據幀格式
如何維持連接
在正式介紹協議細節前,先來看一個簡單的例子,有個直觀感受。例子包括了WebSocket服務端、WebSocket客戶端(網頁端)。完整代碼可以在 這里 找到。
這里服務端用了ws這個庫。相比大家熟悉的socket.io,ws實現更輕量,更適合學習的目的。
1、服務端
代碼如下,監聽8080端口。當有新的連接請求到達時,打印日志,同時向客戶端發送消息。當收到到來自客戶端的消息時,同樣打印日志。
var app=require('express')();var server=require('http').Server(app);var WebSocket=require('ws');var wss=new WebSocket.Server({ port: 8080 }); wss.on('connection', function connection(ws) { console.log('server: receive connection.'); ws.on('message', function incoming(message) { console.log('server: received: %s', message); }); ws.send('world'); }); app.get('/', function (req, res) { res.sendfile(__dirname + '/index.html'); }); app.listen(3000);
2、客戶端
代碼如下,向8080端口發起WebSocket連接。連接建立后,打印日志,同時向服務端發送消息。接收到來自服務端的消息后,同樣打印日志。
<script> var ws=new WebSocket('ws://localhost:8080'); ws.onopen=function () { console.log('ws onopen'); ws.send('from client: hello'); }; ws.onmessage=function (e) { console.log('ws onmessage'); console.log('from server: ' + e.data); };</script>
3、運行結果
可分別查看服務端、客戶端的日志,這里不展開。
服務端輸出:
server: receive connection. server: received hello
客戶端輸出:
client: ws connection is open client: received world
前面提到,WebSocket復用了HTTP的握手通道。具體指的是,客戶端通過HTTP請求與WebSocket服務端協商升級協議。協議升級完成后,后續的數據交換則遵照WebSocket的協議。
1、客戶端:申請協議升級
首先,客戶端發起協議升級請求。可以看到,采用的是標準的HTTP報文格式,且只支持GET方法。
GET / HTTP/1.1Host: localhost:8080Origin: http://127.0.0.1:3000Connection: UpgradeUpgrade: websocketSec-WebSocket-Version: 13Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw==
重點請求首部意義如下:
Connection: Upgrade
:表示要升級協議
Upgrade: websocket
:表示要升級到websocket協議。
Sec-WebSocket-Version: 13
:表示websocket的版本。如果服務端不支持該版本,需要返回一個Sec-WebSocket-Version
header,里面包含服務端支持的版本號。
Sec-WebSocket-Key
:與后面服務端響應首部的Sec-WebSocket-Accept
是配套的,提供基本的防護,比如惡意的連接,或者無意的連接。
注意,上面請求省略了部分非重點請求首部。由于是標準的HTTP請求,類似Host、Origin、Cookie等請求首部會照常發送。在握手階段,可以通過相關請求首部進行 安全限制、權限校驗等。
2、服務端:響應協議升級
服務端返回內容如下,狀態代碼101
表示協議切換。到此完成協議升級,后續的數據交互都按照新的協議來。
HTTP/1.1 101 Switching ProtocolsConnection:Upgrade Upgrade: websocketSec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=
備注:每個header都以
\r\n
結尾,并且最后一行加上一個額外的空行\r\n
。此外,服務端回應的HTTP狀態碼只能在握手階段使用。過了握手階段后,就只能采用特定的錯誤碼。
3、Sec-WebSocket-Accept的計算
Sec-WebSocket-Accept
根據客戶端請求首部的Sec-WebSocket-Key
計算出來。
計算公式為:
將Sec-WebSocket-Key
跟258EAFA5-E914-47DA-95CA-C5AB0DC85B11
拼接。
通過SHA1計算出摘要,并轉成base64字符串。
偽代碼如下:
>toBase64( sha1( Sec-WebSocket-Key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 ) )
驗證下前面的返回結果:
const crypto=require('crypto');const magic='258EAFA5-E914-47DA-95CA-C5AB0DC85B11';const secWebSocketKey='w4v7O6xFTi36lq3RNcgctw==';let secWebSocketAccept=crypto.createHash('sha1') .update(secWebSocketKey + magic) .digest('base64');console.log(secWebSocketAccept);// Oy4NRAQ13jhfONC7bP8dTKb4PTU=
客戶端、服務端數據的交換,離不開數據幀格式的定義。因此,在實際講解數據交換之前,我們先來看下WebSocket的數據幀格式。
WebSocket客戶端、服務端通信的最小單位是幀(frame),由1個或多個幀組成一條完整的消息(message)。
發送端:將消息切割成多個幀,并發送給服務端;
接收端:接收消息幀,并將關聯的幀重新組裝成完整的消息;
本節的重點,就是講解數據幀的格式。詳細定義可參考 RFC6455 5.2節 。
1、數據幀格式概覽
下面給出了WebSocket數據幀的統一格式。熟悉TCP/IP協議的同學對這樣的圖應該不陌生。
從左到右,單位是比特。比如FIN
、RSV1
各占據1比特,opcode
占據4比特。
內容包括了標識、操作代碼、掩碼、數據、數據長度等。(下一小節會展開)
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len==127 | + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key, if MASK set to 1 | +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +---------------------------------------------------------------+
2、數據幀格式詳解
針對前面的格式概覽圖,這里逐個字段進行講解,如有不清楚之處,可參考協議規范,或留言交流。
FIN:1個比特。
如果是1,表示這是消息(message)的最后一個分片(fragment),如果是0,表示不是是消息(message)的最后一個分片(fragment)。
RSV1, RSV2, RSV3:各占1個比特。
一般情況下全為0。當客戶端、服務端協商采用WebSocket擴展時,這三個標志位可以非0,且值的含義由擴展進行定義。如果出現非零的值,且并沒有采用WebSocket擴展,連接出錯。
Opcode: 4個比特。
操作代碼,Opcode的值決定了應該如何解析后續的數據載荷(data payload)。如果操作代碼是不認識的,那么接收端應該斷開連接(fail the connection)。可選的操作代碼如下:
%x0:表示一個延續幀。當Opcode為0時,表示本次數據傳輸采用了數據分片,當前收到的數據幀為其中一個數據分片。
%x1:表示這是一個文本幀(frame)
%x2:表示這是一個二進制幀(frame)
%x3-7:保留的操作代碼,用于后續定義的非控制幀。
%x8:表示連接斷開。
%x8:表示這是一個ping操作。
%xA:表示這是一個pong操作。
%xB-F:保留的操作代碼,用于后續定義的控制幀。
Mask: 1個比特。
表示是否要對數據載荷進行掩碼操作。從客戶端向服務端發送數據時,需要對數據進行掩碼操作;從服務端向客戶端發送數據時,不需要對數據進行掩碼操作。
如果服務端接收到的數據沒有進行過掩碼操作,服務端需要斷開連接。
如果Mask是1,那么在Masking-key中會定義一個掩碼鍵(masking key),并用這個掩碼鍵來對數據載荷進行反掩碼。所有客戶端發送到服務端的數據幀,Mask都是1。
掩碼的算法、用途在下一小節講解。
Payload length:數據載荷的長度,單位是字節。為7位,或7+16位,或1+64位。
假設數Payload length===x,如果
x為0~126:數據的長度為x字節。
x為126:后續2個字節代表一個16位的無符號整數,該無符號整數的值為數據的長度。
x為127:后續8個字節代表一個64位的無符號整數(最高位為0),該無符號整數的值為數據的長度。
此外,如果payload length占用了多個字節的話,payload length的二進制表達采用網絡序(big endian,重要的位在前)。
Masking-key:0或4字節(32位)
所有從客戶端傳送到服務端的數據幀,數據載荷都進行了掩碼操作,Mask為1,且攜帶了4字節的Masking-key。如果Mask為0,則沒有Masking-key。
備注:載荷數據的長度,不包括mask key的長度。
Payload data:(x+y) 字節
載荷數據:包括了擴展數據、應用數據。其中,擴展數據x字節,應用數據y字節。
擴展數據:如果沒有協商使用擴展的話,擴展數據數據為0字節。所有的擴展都必須聲明擴展數據的長度,或者可以如何計算出擴展數據的長度。此外,擴展如何使用必須在握手階段就協商好。如果擴展數據存在,那么載荷數據長度必須將擴展數據的長度包含在內。
應用數據:任意的應用數據,在擴展數據之后(如果存在擴展數據),占據了數據幀剩余的位置。載荷數據長度 減去 擴展數據長度,就得到應用數據的長度。
3、掩碼算法
掩碼鍵(Masking-key)是由客戶端挑選出來的32位的隨機數。掩碼操作不會影響數據載荷的長度。掩碼、反掩碼操作都采用如下算法:
首先,假設:
original-octet-i:為原始數據的第i字節。
transformed-octet-i:為轉換后的數據的第i字節。
j:為i mod 4
的結果。
masking-key-octet-j:為mask key第j字節。
算法描述為: original-octet-i 與 masking-key-octet-j 異或后,得到 transformed-octet-i。
j=i MOD 4
transformed-octet-i=original-octet-i XOR masking-key-octet-j
一旦WebSocket客戶端、服務端建立連接后,后續的操作都是基于數據幀的傳遞。
WebSocket根據opcode
來區分操作的類型。比如0x8
表示斷開連接,0x0
-0x2
表示數據交互。
1、數據分片
WebSocket的每條消息可能被切分成多個數據幀。當WebSocket的接收方收到一個數據幀時,會根據FIN
的值來判斷,是否已經收到消息的最后一個數據幀。
FIN=1表示當前數據幀為消息的最后一個數據幀,此時接收方已經收到完整的消息,可以對消息進行處理。FIN=0,則接收方還需要繼續監聽接收其余的數據幀。
此外,opcode
在數據交換的場景下,表示的是數據的類型。0x01
表示文本,0x02
表示二進制。而0x00
比較特殊,表示延續幀(continuation frame),顧名思義,就是完整消息對應的數據幀還沒接收完。
2、數據分片例子
直接看例子更形象些。下面例子來自MDN,可以很好地演示數據的分片。客戶端向服務端兩次發送消息,服務端收到消息后回應客戶端,這里主要看客戶端往服務端發送的消息。
第一條消息
FIN=1, 表示是當前消息的最后一個數據幀。服務端收到當前數據幀后,可以處理消息。opcode=0x1,表示客戶端發送的是文本類型。
第二條消息
FIN=0,opcode=0x1,表示發送的是文本類型,且消息還沒發送完成,還有后續的數據幀。
FIN=0,opcode=0x0,表示消息還沒發送完成,還有后續的數據幀,當前的數據幀需要接在上一條數據幀之后。
FIN=1,opcode=0x0,表示消息已經發送完成,沒有后續的數據幀,當前的數據幀需要接在上一條數據幀之后。服務端可以將關聯的數據幀組裝成完整的消息。
Client: FIN=1, opcode=0x1, msg="hello"Server: (process complete message immediately) Hi.Client: FIN=0, opcode=0x1, msg="and a"Server: (listening, new message containing text started)Client: FIN=0, opcode=0x0, msg="happy new"Server: (listening, payload concatenated to previous message)Client: FIN=1, opcode=0x0, msg="year!"Server: (process complete message) Happy new year to you too!
WebSocket為了保持客戶端、服務端的實時雙向通信,需要確保客戶端、服務端之間的TCP通道保持連接沒有斷開。然而,對于長時間沒有數據往來的連接,如果依舊長時間保持著,可能會浪費包括的連接資源。
但不排除有些場景,客戶端、服務端雖然長時間沒有數據往來,但仍需要保持連接。這個時候,可以采用心跳來實現。
發送方->接收方:ping
接收方->發送方:pong
ping、pong的操作,對應的是WebSocket的兩個控制幀,opcode
分別是0x9
、0xA
。
舉例,WebSocket服務端向客戶端發送ping,只需要如下代碼(采用ws模塊)
ws.ping('', false, true);
八、Sec-WebSocket-Key/Accept的作用
前面提到了,Sec-WebSocket-Key/Sec-WebSocket-Accept
在主要作用在于提供基礎的防護,減少惡意連接、意外連接。
作用大致歸納如下:
避免服務端收到非法的websocket連接(比如http客戶端不小心請求連接websocket服務,此時服務端可以直接拒絕連接)
確保服務端理解websocket連接。因為ws握手階段采用的是http協議,因此可能ws連接是被一個http服務器處理并返回的,此時客戶端可以通過Sec-WebSocket-Key來確保服務端認識ws協議。(并非百分百保險,比如總是存在那么些無聊的http服務器,光處理Sec-WebSocket-Key,但并沒有實現ws協議。。。)
用瀏覽器里發起ajax請求,設置header時,Sec-WebSocket-Key以及其他相關的header是被禁止的。這樣可以避免客戶端發送ajax請求時,意外請求協議升級(websocket upgrade)
可以防止反向代理(不理解ws協議)返回錯誤的數據。比如反向代理前后收到兩次ws連接的升級請求,反向代理把第一次請求的返回給cache住,然后第二次請求到來時直接把cache住的請求給返回(無意義的返回)。
Sec-WebSocket-Key主要目的并不是確保數據的安全性,因為Sec-WebSocket-Key、Sec-WebSocket-Accept的轉換計算公式是公開的,而且非常簡單,最主要的作用是預防一些常見的意外情況(非故意的)。
強調:Sec-WebSocket-Key/Sec-WebSocket-Accept 的換算,只能帶來基本的保障,但連接是否安全、數據是否安全、客戶端/服務端是否合法的 ws客戶端、ws服務端,其實并沒有實際性的保證。
九、數據掩碼的作用
WebSocket協議中,數據掩碼的作用是增強協議的安全性。但數據掩碼并不是為了保護數據本身,因為算法本身是公開的,運算也不復雜。除了加密通道本身,似乎沒有太多有效的保護通信安全的辦法。
那么為什么還要引入掩碼計算呢,除了增加計算機器的運算量外似乎并沒有太多的收益(這也是不少同學疑惑的點)。
答案還是兩個字:安全。但并不是為了防止數據泄密,而是為了防止早期版本的協議中存在的代理緩存污染攻擊(proxy cache poisoning attacks)等問題。
1、代理緩存污染攻擊
下面摘自2010年關于安全的一段講話。其中提到了代理服務器在協議實現上的缺陷可能導致的安全問題。猛擊出處。
“We show, empirically, that the current version of the WebSocket consent mechanism is vulnerable to proxy cache poisoning attacks. Even though the WebSocket handshake is based on HTTP, which should be understood by most network intermediaries, the handshake uses the esoteric “Upgrade” mechanism of HTTP [5]. In our experiment, we find that many proxies do not implement the Upgrade mechanism properly, which causes the handshake to succeed even though subsequent traffic over the socket will be misinterpreted by the proxy.”
[TALKING] Huang, L-S., Chen, E., Barth, A., Rescorla, E., and C.
Jackson, "Talking to Yourself for Fun and Profit", 2010,
在正式描述攻擊步驟之前,我們假設有如下參與者:
攻擊者、攻擊者自己控制的服務器(簡稱“邪惡服務器”)、攻擊者偽造的資源(簡稱“邪惡資源”)
受害者、受害者想要訪問的資源(簡稱“正義資源”)
受害者實際想要訪問的服務器(簡稱“正義服務器”)
中間代理服務器
攻擊步驟一:
攻擊者瀏覽器 向 邪惡服務器 發起WebSocket連接。根據前文,首先是一個協議升級請求。
協議升級請求 實際到達 代理服務器。
代理服務器 將協議升級請求轉發到 邪惡服務器。
邪惡服務器 同意連接,代理服務器 將響應轉發給 攻擊者。
由于 upgrade 的實現上有缺陷,代理服務器 以為之前轉發的是普通的HTTP消息。因此,當協議服務器 同意連接,代理服務器 以為本次會話已經結束。
攻擊步驟二:
攻擊者 在之前建立的連接上,通過WebSocket的接口向 邪惡服務器 發送數據,且數據是精心構造的HTTP格式的文本。其中包含了 正義資源 的地址,以及一個偽造的host(指向正義服務器)。(見后面報文)
請求到達 代理服務器 。雖然復用了之前的TCP連接,但 代理服務器 以為是新的HTTP請求。
代理服務器 向 邪惡服務器 請求 邪惡資源。
邪惡服務器 返回 邪惡資源。代理服務器 緩存住 邪惡資源(url是對的,但host是 正義服務器 的地址)。
到這里,受害者可以登場了:
受害者 通過 代理服務器 訪問 正義服務器 的 正義資源。
代理服務器 檢查該資源的url、host,發現本地有一份緩存(偽造的)。
代理服務器 將 邪惡資源 返回給 受害者。
受害者 卒。
附:前面提到的精心構造的“HTTP請求報文”。
Client → Server:POST /path/of/attackers/choice HTTP/1.1 Host: host-of-attackers-choice.com Sec-WebSocket-Key: <connection-key> Server → Client:HTTP/1.1 200 OK Sec-WebSocket-Accept: <connection-key>
2、當前解決方案
最初的提案是對數據進行加密處理。基于安全、效率的考慮,最終采用了折中的方案:對數據載荷進行掩碼處理。
需要注意的是,這里只是限制了瀏覽器對數據載荷進行掩碼處理,但是壞人完全可以實現自己的WebSocket客戶端、服務端,不按規則來,攻擊可以照常進行。
但是對瀏覽器加上這個限制后,可以大大增加攻擊的難度,以及攻擊的影響范圍。如果沒有這個限制,只需要在網上放個釣魚網站騙人去訪問,一下子就可以在短時間內展開大范圍的攻擊。
WebSocket可寫的東西還挺多,比如WebSocket擴展。客戶端、服務端之間是如何協商、使用擴展的。WebSocket擴展可以給協議本身增加很多能力和想象空間,比如數據的壓縮、加密,以及多路復用等。
篇幅所限,這里先不展開,感興趣的同學可以留言交流。文章如有錯漏,敬請指出。
作者:程序猿小卡
在傳統的Web應用交互中,客戶端通過瀏覽器發送請求,服務器處理后返回結果,這一過程雖然適用于信息更新不頻繁的場景,但在實時性和高并發需求日益增長的今天,顯得力不從心。特別是在移動互聯網的浪潮中,金融證券實時信息、地理位置獲取、社交網絡消息推送等功能對Web應用的實時響應能力提出了更高要求。
面對傳統Web模式在高并發和實時性需求上的挑戰,業界迫切需要一種更高效的雙向通信機制。基于HTML5規范的WebSocket技術應運而生,被譽為“Web TCP”。
WebSocket是HTML5中的一種新協議,它實現了瀏覽器與服務器之間的全雙工通信。與HTTP相比,WebSocket具有以下顯著特點:
1.通信模式
HTTP:傳統的HTTP協議是基于請求-響應模式的。客戶端發起請求,服務器響應請求并返回數據。每次通信都需要建立新的連接。
WebSocket:WebSocket協議支持全雙工通信。一旦建立連接,客戶端和服務器可以相互獨立地發送和接收數據,無需重新建立連接。
2.連接持久性
HTTP:每個HTTP請求/響應對都需要一個新的連接,連接在數據傳輸完成后關閉。這種模式適用于偶爾的數據交換,但在需要持續數據流的應用中效率較低。
WebSocket:WebSocket連接是持久的。一旦建立,連接會保持打開狀態,允許連續的數據交換,直到顯式關閉。
3.實時性
HTTP:由于每次通信都需要建立連接,HTTP的實時性較差。適合不需要即時響應的應用。
WebSocket:WebSocket提供了低延遲的實時通信,適合需要快速響應的應用,如在線游戲、實時數據更新等。
4.資源消耗
HTTP:頻繁的連接和斷開會增加服務器的負載,消耗更多的資源和帶寬。
WebSocket:由于連接是持久的,WebSocket減少了連接建立和斷開的開銷,節省了服務器資源和帶寬。
5.協議實現
HTTP:HTTP是基于TCP/IP協議的,廣泛支持各種瀏覽器和服務器。
WebSocket:WebSocket是基于HTTP協議進行握手的,但通信協議是獨立的,需要瀏覽器和服務器的特別支持。
6.安全性
HTTP:可以通過HTTPS來提供加密傳輸,保護數據安全。
WebSocket:WebSocket也支持通過WSS(WebSocket Secure)進行加密傳輸,確保數據傳輸的安全性。
7.適用場景
HTTP:適用于靜態內容傳輸、偶爾的API調用等不需要持續連接的場景。
WebSocket:適用于需要實時通信的應用,如聊天應用、實時數據推送、在線協作工具等。
8.瀏覽器兼容性
HTTP:幾乎所有的瀏覽器都支持HTTP協議。
WebSocket:現代瀏覽器都支持WebSocket,但老舊瀏覽器可能需要額外的插件或不支持。
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.OnMessage;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.CloseReason;
import java.io.IOException;
@ServerEndpoint("/websocket")
public class ChatServer {
private Session session;
@OnOpen
public void onOpen(Session session) {
this.session=session;
System.out.println("連接打開: " + session.getId());
}
@OnMessage
public void onMessage(String message, Session currentSession) {
// 將收到的消息廣播給所有連接的客戶端
for (Session session : session.getRequestedSessions()) {
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@OnClose
public void onClose(Session session, CloseReason closeReason) {
System.out.println("連接關閉: " + closeReason.getReasonPhrase());
}
@OnError
public void onError(Throwable t) {
t.printStackTrace();
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>簡單聊天應用</title>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script type="text/javascript">
var ws;
function connect() {
ws=new WebSocket("ws://localhost:8080/yourContextPath/websocket");
ws.onopen=function() {
alert("連接成功!");
};
ws.onmessage=function(event) {
var messages=document.getElementById('messages');
var message=document.createElement('li');
message.textContent=event.data;
messages.appendChild(message);
window.scrollTo(0, document.body.scrollHeight);
};
ws.onclose=function() {
alert("連接關閉!");
};
ws.onerror=function() {
alert("連接出錯!");
};
}
function send() {
var message=document.getElementById('message').value;
ws.send(message);
}
</script>
</head>
<body onload="connect();">
<ul id="messages"></ul>
<input type="text" id="message" onkeypress="if (event.keyCode==13) send();" />
</body>
</html>
#開發#
*請認真填寫需求信息,我們會在24小時內與您取得聯系。