Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 国产男女交性视频播放免费bd,成人在线欧美,www.伊人网

          整合營(yíng)銷(xiāo)服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢(xún)熱線:

          HTML5(十一)-WebSocket 基礎(chǔ)教程

          HTML5(十一)-WebSocket 基礎(chǔ)教程

          、為什么要學(xué) WebSocket?

          websocket 是 HTML5 提供的一種長(zhǎng)鏈接雙向通訊協(xié)議,使得客戶(hù)端和服務(wù)器之間的數(shù)據(jù)交換更簡(jiǎn)單,允許服務(wù)端主動(dòng)向客戶(hù)端推送數(shù)據(jù),并且客戶(hù)端與服務(wù)端只需連接一次,就可以保持長(zhǎng)久連接,并進(jìn)行數(shù)據(jù)通信。

          websocket 與 http 區(qū)別:

          • http 鏈接分為長(zhǎng)鏈接、短鏈接,短鏈接是發(fā)送一個(gè)請(qǐng)求,返回一個(gè)響應(yīng),長(zhǎng)鏈接是在一定周期內(nèi)保持鏈接。但是 websocket 只需連接一次就可以保持長(zhǎng)鏈接,不需要的時(shí)候可以手動(dòng)斷開(kāi)。
          • http 通信中,客戶(hù)端是主動(dòng)的,服務(wù)端是被動(dòng)的。但是 websocket,服務(wù)端可以主動(dòng)向客戶(hù)端推送數(shù)據(jù)。
          • http 通過(guò)輪詢(xún)建立的長(zhǎng)鏈接,多次建立 request / response 會(huì)產(chǎn)生冗余的頭部信息。

          ajax 輪詢(xún)與 websocket 通信原理如圖:



          websocket 就是為了解決客戶(hù)端發(fā)起多個(gè) http 請(qǐng)求到服務(wù)器資源,瀏覽器必須要經(jīng)過(guò)長(zhǎng)時(shí)間的,輪詢(xún)問(wèn)題而生的,實(shí)現(xiàn)多路復(fù)用。它最大特點(diǎn)就是服務(wù)器可以主動(dòng)向客戶(hù)端推送信息。

          二、WebSocket 對(duì)象

          2.1、創(chuàng)建對(duì)象:

          let ws=new WebSocket( url , [ protocol ] )

          url:指定連接的后臺(tái)服務(wù)地址。

          protocol:指定可接受的子協(xié)議,是可選參數(shù)。

          2.2、對(duì)象屬性

          readyState:表示連接狀態(tài),是一個(gè)只讀屬性。

          使用語(yǔ)法:ws.readyState

          返回的值有4個(gè),分別表示的意義:

          • 0 - 未建立連接
          • 1 - 已建立連接,可正常通信
          • 2 - 連接正在進(jìn)行關(guān)閉
          • 3 - 連接已經(jīng)關(guān)閉或斷開(kāi),無(wú)法通信

          bufferedAmount:已被放入等待傳輸隊(duì)列,但是還沒(méi)有發(fā)出的 UTF-8 文本字節(jié)數(shù),也是只讀屬性。

          2.3、對(duì)象事件

          onopen - 連接時(shí)觸發(fā),用于指定連接成功后的回調(diào)函數(shù)。

          使用語(yǔ)法:

          // 方法一 :只可以指定一個(gè)回調(diào)函數(shù)
          ws.onopen=function(){
          
          }
          //方法二 :可以指定多個(gè)回調(diào)函數(shù)
          ws.addEventListener('open',function(){
          
          })

          onclose - 關(guān)閉時(shí)觸發(fā),指定連接關(guān)閉時(shí)回調(diào)函數(shù)。

          使用語(yǔ)法:與 onopen 完全一致。

          onmessage - 客戶(hù)端接收服務(wù)端數(shù)據(jù)時(shí)觸發(fā),指定回調(diào)函數(shù)。

          使用語(yǔ)法:

          // 方法一:
          ws.inmessage=function(event){
          	let data=event.data
            //服務(wù)器傳給客戶(hù)端的數(shù)據(jù)
          }
          // 方法二:
          ws.addEventListener('inmessage', function(event){
          	let data=event.data
            //服務(wù)器傳給客戶(hù)端的數(shù)據(jù)
          })

          onerror - 通信發(fā)生錯(cuò)誤時(shí)觸發(fā),并指定回調(diào)函數(shù)。

          使用語(yǔ)法:

          //方法一
          ws.onerror=function(){
          //錯(cuò)誤處理
          }
          //方法二
          ws.addEventListener('error',function(){
          //錯(cuò)誤處理
          })

          2.3、對(duì)象事件

          send - 用于向服務(wù)器發(fā)送數(shù)據(jù)。

          使用語(yǔ)法:

          ws.send( data )

          data :是發(fā)給服務(wù)器的數(shù)據(jù),這個(gè)數(shù)據(jù)可以是字符串、數(shù)組、json、Blob 對(duì)象或 ArrayBuffer 對(duì)象等。

          如發(fā)送 Blob 對(duì)象例子:

          var file=document.querySelector('input='file'').files[0]
          ws.send(file)

          close - 關(guān)閉連接

          使用語(yǔ)法:

          ws.close()

          、內(nèi)容概覽

          WebSocket的出現(xiàn),使得瀏覽器具備了實(shí)時(shí)雙向通信的能力。本文由淺入深,介紹了WebSocket如何建立連接、交換數(shù)據(jù)的細(xì)節(jié),以及數(shù)據(jù)幀的格式。此外,還簡(jiǎn)要介紹了針對(duì)WebSocket的安全攻擊,以及協(xié)議是如何抵御類(lèi)似攻擊的。

          二、什么是WebSocket

          HTML5開(kāi)始提供的一種瀏覽器與服務(wù)器進(jìn)行全雙工通訊的網(wǎng)絡(luò)技術(shù),屬于應(yīng)用層協(xié)議。它基于TCP傳輸協(xié)議,并復(fù)用HTTP的握手通道。

          對(duì)大部分web開(kāi)發(fā)者來(lái)說(shuō),上面這段描述有點(diǎn)枯燥,其實(shí)只要記住幾點(diǎn):

          1. WebSocket可以在瀏覽器里使用

          2. 支持雙向通信

          3. 使用很簡(jiǎn)單

          1、有哪些優(yōu)點(diǎn)

          說(shuō)到優(yōu)點(diǎn),這里的對(duì)比參照物是HTTP協(xié)議,概括地說(shuō)就是:支持雙向通信,更靈活,更高效,可擴(kuò)展性更好。

          1. 支持雙向通信,實(shí)時(shí)性更強(qiáng)。

          2. 更好的二進(jìn)制支持。

          3. 較少的控制開(kāi)銷(xiāo)。連接創(chuàng)建后,ws客戶(hù)端、服務(wù)端進(jìn)行數(shù)據(jù)交換時(shí),協(xié)議控制的數(shù)據(jù)包頭部較小。在不包含頭部的情況下,服務(wù)端到客戶(hù)端的包頭只有2~10字節(jié)(取決于數(shù)據(jù)包長(zhǎng)度),客戶(hù)端到服務(wù)端的的話,需要加上額外的4字節(jié)的掩碼。而HTTP協(xié)議每次通信都需要攜帶完整的頭部。

          4. 支持?jǐn)U展。ws協(xié)議定義了擴(kuò)展,用戶(hù)可以擴(kuò)展協(xié)議,或者實(shí)現(xiàn)自定義的子協(xié)議。(比如支持自定義壓縮算法等)

          對(duì)于后面兩點(diǎn),沒(méi)有研究過(guò)WebSocket協(xié)議規(guī)范的同學(xué)可能理解起來(lái)不夠直觀,但不影響對(duì)WebSocket的學(xué)習(xí)和使用。

          2、需要學(xué)習(xí)哪些東西

          對(duì)網(wǎng)絡(luò)應(yīng)用層協(xié)議的學(xué)習(xí)來(lái)說(shuō),最重要的往往就是連接建立過(guò)程、數(shù)據(jù)交換教程。當(dāng)然,數(shù)據(jù)的格式是逃不掉的,因?yàn)樗苯記Q定了協(xié)議本身的能力。好的數(shù)據(jù)格式能讓協(xié)議更高效、擴(kuò)展性更好。

          下文主要圍繞下面幾點(diǎn)展開(kāi):

          1. 如何建立連接

          2. 如何交換數(shù)據(jù)

          3. 數(shù)據(jù)幀格式

          4. 如何維持連接

          三、入門(mén)例子

          在正式介紹協(xié)議細(xì)節(jié)前,先來(lái)看一個(gè)簡(jiǎn)單的例子,有個(gè)直觀感受。例子包括了WebSocket服務(wù)端、WebSocket客戶(hù)端(網(wǎng)頁(yè)端)。完整代碼可以在 這里 找到。

          這里服務(wù)端用了ws這個(gè)庫(kù)。相比大家熟悉的socket.iows實(shí)現(xiàn)更輕量,更適合學(xué)習(xí)的目的。

          1、服務(wù)端

          代碼如下,監(jiān)聽(tīng)8080端口。當(dāng)有新的連接請(qǐng)求到達(dá)時(shí),打印日志,同時(shí)向客戶(hù)端發(fā)送消息。當(dāng)收到到來(lái)自客戶(hù)端的消息時(shí),同樣打印日志。

          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、客戶(hù)端

          代碼如下,向8080端口發(fā)起WebSocket連接。連接建立后,打印日志,同時(shí)向服務(wù)端發(fā)送消息。接收到來(lái)自服務(wù)端的消息后,同樣打印日志。

          <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、運(yùn)行結(jié)果

          可分別查看服務(wù)端、客戶(hù)端的日志,這里不展開(kāi)。

          服務(wù)端輸出:

          server: receive connection.
          server: received hello

          客戶(hù)端輸出:

          client: ws connection is open
          client: received world

          四、如何建立連接

          前面提到,WebSocket復(fù)用了HTTP的握手通道。具體指的是,客戶(hù)端通過(guò)HTTP請(qǐng)求與WebSocket服務(wù)端協(xié)商升級(jí)協(xié)議。協(xié)議升級(jí)完成后,后續(xù)的數(shù)據(jù)交換則遵照WebSocket的協(xié)議。

          1、客戶(hù)端:申請(qǐng)協(xié)議升級(jí)

          首先,客戶(hù)端發(fā)起協(xié)議升級(jí)請(qǐng)求。可以看到,采用的是標(biāo)準(zhǔn)的HTTP報(bào)文格式,且只支持GET方法。

          GET / HTTP/1.1Host: localhost:8080Origin: http://127.0.0.1:3000Connection: UpgradeUpgrade: websocketSec-WebSocket-Version: 13Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw==

          重點(diǎn)請(qǐng)求首部意義如下:

          • Connection: Upgrade:表示要升級(jí)協(xié)議

          • Upgrade: websocket:表示要升級(jí)到websocket協(xié)議。

          • Sec-WebSocket-Version: 13:表示websocket的版本。如果服務(wù)端不支持該版本,需要返回一個(gè)Sec-WebSocket-Versionheader,里面包含服務(wù)端支持的版本號(hào)。

          • Sec-WebSocket-Key:與后面服務(wù)端響應(yīng)首部的Sec-WebSocket-Accept是配套的,提供基本的防護(hù),比如惡意的連接,或者無(wú)意的連接。

          注意,上面請(qǐng)求省略了部分非重點(diǎn)請(qǐng)求首部。由于是標(biāo)準(zhǔn)的HTTP請(qǐng)求,類(lèi)似Host、Origin、Cookie等請(qǐng)求首部會(huì)照常發(fā)送。在握手階段,可以通過(guò)相關(guān)請(qǐng)求首部進(jìn)行 安全限制、權(quán)限校驗(yàn)等。

          2、服務(wù)端:響應(yīng)協(xié)議升級(jí)

          服務(wù)端返回內(nèi)容如下,狀態(tài)代碼101表示協(xié)議切換。到此完成協(xié)議升級(jí),后續(xù)的數(shù)據(jù)交互都按照新的協(xié)議來(lái)。

          HTTP/1.1 101 Switching ProtocolsConnection:Upgrade
          Upgrade: websocketSec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=

          備注:每個(gè)header都以\r\n結(jié)尾,并且最后一行加上一個(gè)額外的空行\r\n。此外,服務(wù)端回應(yīng)的HTTP狀態(tài)碼只能在握手階段使用。過(guò)了握手階段后,就只能采用特定的錯(cuò)誤碼。

          3、Sec-WebSocket-Accept的計(jì)算

          Sec-WebSocket-Accept根據(jù)客戶(hù)端請(qǐng)求首部的Sec-WebSocket-Key計(jì)算出來(lái)。

          計(jì)算公式為:

          1. Sec-WebSocket-Key258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接。

          2. 通過(guò)SHA1計(jì)算出摘要,并轉(zhuǎn)成base64字符串。

          偽代碼如下:

          >toBase64( sha1( Sec-WebSocket-Key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 ) )

          驗(yàn)證下前面的返回結(jié)果:

          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=

          五、數(shù)據(jù)幀格式

          客戶(hù)端、服務(wù)端數(shù)據(jù)的交換,離不開(kāi)數(shù)據(jù)幀格式的定義。因此,在實(shí)際講解數(shù)據(jù)交換之前,我們先來(lái)看下WebSocket的數(shù)據(jù)幀格式。

          WebSocket客戶(hù)端、服務(wù)端通信的最小單位是幀(frame),由1個(gè)或多個(gè)幀組成一條完整的消息(message)。

          1. 發(fā)送端:將消息切割成多個(gè)幀,并發(fā)送給服務(wù)端;

          2. 接收端:接收消息幀,并將關(guān)聯(lián)的幀重新組裝成完整的消息;

          本節(jié)的重點(diǎn),就是講解數(shù)據(jù)幀的格式。詳細(xì)定義可參考 RFC6455 5.2節(jié) 。

          1、數(shù)據(jù)幀格式概覽

          下面給出了WebSocket數(shù)據(jù)幀的統(tǒng)一格式。熟悉TCP/IP協(xié)議的同學(xué)對(duì)這樣的圖應(yīng)該不陌生。

          1. 從左到右,單位是比特。比如FINRSV1各占據(jù)1比特,opcode占據(jù)4比特。

          2. 內(nèi)容包括了標(biāo)識(shí)、操作代碼、掩碼、數(shù)據(jù)、數(shù)據(jù)長(zhǎng)度等。(下一小節(jié)會(huì)展開(kāi))

           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、數(shù)據(jù)幀格式詳解

          針對(duì)前面的格式概覽圖,這里逐個(gè)字段進(jìn)行講解,如有不清楚之處,可參考協(xié)議規(guī)范,或留言交流。

          FIN:1個(gè)比特。

          如果是1,表示這是消息(message)的最后一個(gè)分片(fragment),如果是0,表示不是是消息(message)的最后一個(gè)分片(fragment)。

          RSV1, RSV2, RSV3:各占1個(gè)比特。

          一般情況下全為0。當(dāng)客戶(hù)端、服務(wù)端協(xié)商采用WebSocket擴(kuò)展時(shí),這三個(gè)標(biāo)志位可以非0,且值的含義由擴(kuò)展進(jìn)行定義。如果出現(xiàn)非零的值,且并沒(méi)有采用WebSocket擴(kuò)展,連接出錯(cuò)。

          Opcode: 4個(gè)比特。

          操作代碼,Opcode的值決定了應(yīng)該如何解析后續(xù)的數(shù)據(jù)載荷(data payload)。如果操作代碼是不認(rèn)識(shí)的,那么接收端應(yīng)該斷開(kāi)連接(fail the connection)。可選的操作代碼如下:

          • %x0:表示一個(gè)延續(xù)幀。當(dāng)Opcode為0時(shí),表示本次數(shù)據(jù)傳輸采用了數(shù)據(jù)分片,當(dāng)前收到的數(shù)據(jù)幀為其中一個(gè)數(shù)據(jù)分片。

          • %x1:表示這是一個(gè)文本幀(frame)

          • %x2:表示這是一個(gè)二進(jìn)制幀(frame)

          • %x3-7:保留的操作代碼,用于后續(xù)定義的非控制幀。

          • %x8:表示連接斷開(kāi)。

          • %x8:表示這是一個(gè)ping操作。

          • %xA:表示這是一個(gè)pong操作。

          • %xB-F:保留的操作代碼,用于后續(xù)定義的控制幀。

          Mask: 1個(gè)比特。

          表示是否要對(duì)數(shù)據(jù)載荷進(jìn)行掩碼操作。從客戶(hù)端向服務(wù)端發(fā)送數(shù)據(jù)時(shí),需要對(duì)數(shù)據(jù)進(jìn)行掩碼操作;從服務(wù)端向客戶(hù)端發(fā)送數(shù)據(jù)時(shí),不需要對(duì)數(shù)據(jù)進(jìn)行掩碼操作。

          如果服務(wù)端接收到的數(shù)據(jù)沒(méi)有進(jìn)行過(guò)掩碼操作,服務(wù)端需要斷開(kāi)連接。

          如果Mask是1,那么在Masking-key中會(huì)定義一個(gè)掩碼鍵(masking key),并用這個(gè)掩碼鍵來(lái)對(duì)數(shù)據(jù)載荷進(jìn)行反掩碼。所有客戶(hù)端發(fā)送到服務(wù)端的數(shù)據(jù)幀,Mask都是1。

          掩碼的算法、用途在下一小節(jié)講解。

          Payload length:數(shù)據(jù)載荷的長(zhǎng)度,單位是字節(jié)。為7位,或7+16位,或1+64位。

          假設(shè)數(shù)Payload length===x,如果

          • x為0~126:數(shù)據(jù)的長(zhǎng)度為x字節(jié)。

          • x為126:后續(xù)2個(gè)字節(jié)代表一個(gè)16位的無(wú)符號(hào)整數(shù),該無(wú)符號(hào)整數(shù)的值為數(shù)據(jù)的長(zhǎng)度。

          • x為127:后續(xù)8個(gè)字節(jié)代表一個(gè)64位的無(wú)符號(hào)整數(shù)(最高位為0),該無(wú)符號(hào)整數(shù)的值為數(shù)據(jù)的長(zhǎng)度。

          此外,如果payload length占用了多個(gè)字節(jié)的話,payload length的二進(jìn)制表達(dá)采用網(wǎng)絡(luò)序(big endian,重要的位在前)。

          Masking-key:0或4字節(jié)(32位)

          所有從客戶(hù)端傳送到服務(wù)端的數(shù)據(jù)幀,數(shù)據(jù)載荷都進(jìn)行了掩碼操作,Mask為1,且攜帶了4字節(jié)的Masking-key。如果Mask為0,則沒(méi)有Masking-key。

          備注:載荷數(shù)據(jù)的長(zhǎng)度,不包括mask key的長(zhǎng)度。

          Payload data:(x+y) 字節(jié)

          載荷數(shù)據(jù):包括了擴(kuò)展數(shù)據(jù)、應(yīng)用數(shù)據(jù)。其中,擴(kuò)展數(shù)據(jù)x字節(jié),應(yīng)用數(shù)據(jù)y字節(jié)。

          擴(kuò)展數(shù)據(jù):如果沒(méi)有協(xié)商使用擴(kuò)展的話,擴(kuò)展數(shù)據(jù)數(shù)據(jù)為0字節(jié)。所有的擴(kuò)展都必須聲明擴(kuò)展數(shù)據(jù)的長(zhǎng)度,或者可以如何計(jì)算出擴(kuò)展數(shù)據(jù)的長(zhǎng)度。此外,擴(kuò)展如何使用必須在握手階段就協(xié)商好。如果擴(kuò)展數(shù)據(jù)存在,那么載荷數(shù)據(jù)長(zhǎng)度必須將擴(kuò)展數(shù)據(jù)的長(zhǎng)度包含在內(nèi)。

          應(yīng)用數(shù)據(jù):任意的應(yīng)用數(shù)據(jù),在擴(kuò)展數(shù)據(jù)之后(如果存在擴(kuò)展數(shù)據(jù)),占據(jù)了數(shù)據(jù)幀剩余的位置。載荷數(shù)據(jù)長(zhǎng)度 減去 擴(kuò)展數(shù)據(jù)長(zhǎng)度,就得到應(yīng)用數(shù)據(jù)的長(zhǎng)度。

          3、掩碼算法

          掩碼鍵(Masking-key)是由客戶(hù)端挑選出來(lái)的32位的隨機(jī)數(shù)。掩碼操作不會(huì)影響數(shù)據(jù)載荷的長(zhǎng)度。掩碼、反掩碼操作都采用如下算法:

          首先,假設(shè):

          • original-octet-i:為原始數(shù)據(jù)的第i字節(jié)。

          • transformed-octet-i:為轉(zhuǎn)換后的數(shù)據(jù)的第i字節(jié)。

          • j:為i mod 4的結(jié)果。

          • masking-key-octet-j:為mask key第j字節(jié)。

          算法描述為: 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

          六、數(shù)據(jù)傳遞

          一旦WebSocket客戶(hù)端、服務(wù)端建立連接后,后續(xù)的操作都是基于數(shù)據(jù)幀的傳遞。

          WebSocket根據(jù)opcode來(lái)區(qū)分操作的類(lèi)型。比如0x8表示斷開(kāi)連接,0x0-0x2表示數(shù)據(jù)交互。

          1、數(shù)據(jù)分片

          WebSocket的每條消息可能被切分成多個(gè)數(shù)據(jù)幀。當(dāng)WebSocket的接收方收到一個(gè)數(shù)據(jù)幀時(shí),會(huì)根據(jù)FIN的值來(lái)判斷,是否已經(jīng)收到消息的最后一個(gè)數(shù)據(jù)幀。

          FIN=1表示當(dāng)前數(shù)據(jù)幀為消息的最后一個(gè)數(shù)據(jù)幀,此時(shí)接收方已經(jīng)收到完整的消息,可以對(duì)消息進(jìn)行處理。FIN=0,則接收方還需要繼續(xù)監(jiān)聽(tīng)接收其余的數(shù)據(jù)幀。

          此外,opcode在數(shù)據(jù)交換的場(chǎng)景下,表示的是數(shù)據(jù)的類(lèi)型。0x01表示文本,0x02表示二進(jìn)制。而0x00比較特殊,表示延續(xù)幀(continuation frame),顧名思義,就是完整消息對(duì)應(yīng)的數(shù)據(jù)幀還沒(méi)接收完。

          2、數(shù)據(jù)分片例子

          直接看例子更形象些。下面例子來(lái)自MDN,可以很好地演示數(shù)據(jù)的分片。客戶(hù)端向服務(wù)端兩次發(fā)送消息,服務(wù)端收到消息后回應(yīng)客戶(hù)端,這里主要看客戶(hù)端往服務(wù)端發(fā)送的消息。

          第一條消息

          FIN=1, 表示是當(dāng)前消息的最后一個(gè)數(shù)據(jù)幀。服務(wù)端收到當(dāng)前數(shù)據(jù)幀后,可以處理消息。opcode=0x1,表示客戶(hù)端發(fā)送的是文本類(lèi)型。

          第二條消息

          1. FIN=0,opcode=0x1,表示發(fā)送的是文本類(lèi)型,且消息還沒(méi)發(fā)送完成,還有后續(xù)的數(shù)據(jù)幀。

          2. FIN=0,opcode=0x0,表示消息還沒(méi)發(fā)送完成,還有后續(xù)的數(shù)據(jù)幀,當(dāng)前的數(shù)據(jù)幀需要接在上一條數(shù)據(jù)幀之后。

          3. FIN=1,opcode=0x0,表示消息已經(jīng)發(fā)送完成,沒(méi)有后續(xù)的數(shù)據(jù)幀,當(dāng)前的數(shù)據(jù)幀需要接在上一條數(shù)據(jù)幀之后。服務(wù)端可以將關(guān)聯(lián)的數(shù)據(jù)幀組裝成完整的消息。

          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為了保持客戶(hù)端、服務(wù)端的實(shí)時(shí)雙向通信,需要確保客戶(hù)端、服務(wù)端之間的TCP通道保持連接沒(méi)有斷開(kāi)。然而,對(duì)于長(zhǎng)時(shí)間沒(méi)有數(shù)據(jù)往來(lái)的連接,如果依舊長(zhǎng)時(shí)間保持著,可能會(huì)浪費(fèi)包括的連接資源。

          但不排除有些場(chǎng)景,客戶(hù)端、服務(wù)端雖然長(zhǎng)時(shí)間沒(méi)有數(shù)據(jù)往來(lái),但仍需要保持連接。這個(gè)時(shí)候,可以采用心跳來(lái)實(shí)現(xiàn)。

          • 發(fā)送方->接收方:ping

          • 接收方->發(fā)送方:pong

          ping、pong的操作,對(duì)應(yīng)的是WebSocket的兩個(gè)控制幀,opcode分別是0x90xA

          舉例,WebSocket服務(wù)端向客戶(hù)端發(fā)送ping,只需要如下代碼(采用ws模塊)

          ws.ping('', false, true);

          八、Sec-WebSocket-Key/Accept的作用

          前面提到了,Sec-WebSocket-Key/Sec-WebSocket-Accept在主要作用在于提供基礎(chǔ)的防護(hù),減少惡意連接、意外連接。

          作用大致歸納如下:

          1. 避免服務(wù)端收到非法的websocket連接(比如http客戶(hù)端不小心請(qǐng)求連接websocket服務(wù),此時(shí)服務(wù)端可以直接拒絕連接)

          2. 確保服務(wù)端理解websocket連接。因?yàn)閣s握手階段采用的是http協(xié)議,因此可能ws連接是被一個(gè)http服務(wù)器處理并返回的,此時(shí)客戶(hù)端可以通過(guò)Sec-WebSocket-Key來(lái)確保服務(wù)端認(rèn)識(shí)ws協(xié)議。(并非百分百保險(xiǎn),比如總是存在那么些無(wú)聊的http服務(wù)器,光處理Sec-WebSocket-Key,但并沒(méi)有實(shí)現(xiàn)ws協(xié)議。。。)

          3. 用瀏覽器里發(fā)起ajax請(qǐng)求,設(shè)置header時(shí),Sec-WebSocket-Key以及其他相關(guān)的header是被禁止的。這樣可以避免客戶(hù)端發(fā)送ajax請(qǐng)求時(shí),意外請(qǐng)求協(xié)議升級(jí)(websocket upgrade)

          4. 可以防止反向代理(不理解ws協(xié)議)返回錯(cuò)誤的數(shù)據(jù)。比如反向代理前后收到兩次ws連接的升級(jí)請(qǐng)求,反向代理把第一次請(qǐng)求的返回給cache住,然后第二次請(qǐng)求到來(lái)時(shí)直接把cache住的請(qǐng)求給返回(無(wú)意義的返回)。

          5. Sec-WebSocket-Key主要目的并不是確保數(shù)據(jù)的安全性,因?yàn)镾ec-WebSocket-Key、Sec-WebSocket-Accept的轉(zhuǎn)換計(jì)算公式是公開(kāi)的,而且非常簡(jiǎn)單,最主要的作用是預(yù)防一些常見(jiàn)的意外情況(非故意的)。

          強(qiáng)調(diào):Sec-WebSocket-Key/Sec-WebSocket-Accept 的換算,只能帶來(lái)基本的保障,但連接是否安全、數(shù)據(jù)是否安全、客戶(hù)端/服務(wù)端是否合法的 ws客戶(hù)端、ws服務(wù)端,其實(shí)并沒(méi)有實(shí)際性的保證。

          九、數(shù)據(jù)掩碼的作用

          WebSocket協(xié)議中,數(shù)據(jù)掩碼的作用是增強(qiáng)協(xié)議的安全性。但數(shù)據(jù)掩碼并不是為了保護(hù)數(shù)據(jù)本身,因?yàn)樗惴ū旧硎枪_(kāi)的,運(yùn)算也不復(fù)雜。除了加密通道本身,似乎沒(méi)有太多有效的保護(hù)通信安全的辦法。

          那么為什么還要引入掩碼計(jì)算呢,除了增加計(jì)算機(jī)器的運(yùn)算量外似乎并沒(méi)有太多的收益(這也是不少同學(xué)疑惑的點(diǎn))。

          答案還是兩個(gè)字:安全。但并不是為了防止數(shù)據(jù)泄密,而是為了防止早期版本的協(xié)議中存在的代理緩存污染攻擊(proxy cache poisoning attacks)等問(wèn)題。

          1、代理緩存污染攻擊

          下面摘自2010年關(guān)于安全的一段講話。其中提到了代理服務(wù)器在協(xié)議實(shí)現(xiàn)上的缺陷可能導(dǎo)致的安全問(wèn)題。猛擊出處。

          “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,

          在正式描述攻擊步驟之前,我們假設(shè)有如下參與者:

          • 攻擊者、攻擊者自己控制的服務(wù)器(簡(jiǎn)稱(chēng)“邪惡服務(wù)器”)、攻擊者偽造的資源(簡(jiǎn)稱(chēng)“邪惡資源”)

          • 受害者、受害者想要訪問(wèn)的資源(簡(jiǎn)稱(chēng)“正義資源”)

          • 受害者實(shí)際想要訪問(wèn)的服務(wù)器(簡(jiǎn)稱(chēng)“正義服務(wù)器”)

          • 中間代理服務(wù)器

          攻擊步驟一:

          1. 攻擊者瀏覽器 向 邪惡服務(wù)器 發(fā)起WebSocket連接。根據(jù)前文,首先是一個(gè)協(xié)議升級(jí)請(qǐng)求。

          2. 協(xié)議升級(jí)請(qǐng)求 實(shí)際到達(dá) 代理服務(wù)器。

          3. 代理服務(wù)器 將協(xié)議升級(jí)請(qǐng)求轉(zhuǎn)發(fā)到 邪惡服務(wù)器。

          4. 邪惡服務(wù)器 同意連接,代理服務(wù)器 將響應(yīng)轉(zhuǎn)發(fā)給 攻擊者。

          由于 upgrade 的實(shí)現(xiàn)上有缺陷,代理服務(wù)器 以為之前轉(zhuǎn)發(fā)的是普通的HTTP消息。因此,當(dāng)協(xié)議服務(wù)器 同意連接,代理服務(wù)器 以為本次會(huì)話已經(jīng)結(jié)束。

          攻擊步驟二:

          1. 攻擊者 在之前建立的連接上,通過(guò)WebSocket的接口向 邪惡服務(wù)器 發(fā)送數(shù)據(jù),且數(shù)據(jù)是精心構(gòu)造的HTTP格式的文本。其中包含了 正義資源 的地址,以及一個(gè)偽造的host(指向正義服務(wù)器)。(見(jiàn)后面報(bào)文)

          2. 請(qǐng)求到達(dá) 代理服務(wù)器 。雖然復(fù)用了之前的TCP連接,但 代理服務(wù)器 以為是新的HTTP請(qǐng)求。

          3. 代理服務(wù)器 向 邪惡服務(wù)器 請(qǐng)求 邪惡資源。

          4. 邪惡服務(wù)器 返回 邪惡資源。代理服務(wù)器 緩存住 邪惡資源(url是對(duì)的,但host是 正義服務(wù)器 的地址)。

          到這里,受害者可以登場(chǎng)了:

          1. 受害者 通過(guò) 代理服務(wù)器 訪問(wèn) 正義服務(wù)器 的 正義資源。

          2. 代理服務(wù)器 檢查該資源的url、host,發(fā)現(xiàn)本地有一份緩存(偽造的)。

          3. 代理服務(wù)器 將 邪惡資源 返回給 受害者。

          4. 受害者 卒。

          附:前面提到的精心構(gòu)造的“HTTP請(qǐng)求報(bào)文”。

          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、當(dāng)前解決方案

          最初的提案是對(duì)數(shù)據(jù)進(jìn)行加密處理。基于安全、效率的考慮,最終采用了折中的方案:對(duì)數(shù)據(jù)載荷進(jìn)行掩碼處理。

          需要注意的是,這里只是限制了瀏覽器對(duì)數(shù)據(jù)載荷進(jìn)行掩碼處理,但是壞人完全可以實(shí)現(xiàn)自己的WebSocket客戶(hù)端、服務(wù)端,不按規(guī)則來(lái),攻擊可以照常進(jìn)行。

          但是對(duì)瀏覽器加上這個(gè)限制后,可以大大增加攻擊的難度,以及攻擊的影響范圍。如果沒(méi)有這個(gè)限制,只需要在網(wǎng)上放個(gè)釣魚(yú)網(wǎng)站騙人去訪問(wèn),一下子就可以在短時(shí)間內(nèi)展開(kāi)大范圍的攻擊。

          十、寫(xiě)在后面

          WebSocket可寫(xiě)的東西還挺多,比如WebSocket擴(kuò)展。客戶(hù)端、服務(wù)端之間是如何協(xié)商、使用擴(kuò)展的。WebSocket擴(kuò)展可以給協(xié)議本身增加很多能力和想象空間,比如數(shù)據(jù)的壓縮、加密,以及多路復(fù)用等。

          篇幅所限,這里先不展開(kāi),感興趣的同學(xué)可以留言交流。文章如有錯(cuò)漏,敬請(qǐng)指出。

          作者:程序猿小卡

          ebSocket:歷史演變與現(xiàn)代應(yīng)用

          在傳統(tǒng)的Web應(yīng)用交互中,客戶(hù)端通過(guò)瀏覽器發(fā)送請(qǐng)求,服務(wù)器處理后返回結(jié)果,這一過(guò)程雖然適用于信息更新不頻繁的場(chǎng)景,但在實(shí)時(shí)性和高并發(fā)需求日益增長(zhǎng)的今天,顯得力不從心。特別是在移動(dòng)互聯(lián)網(wǎng)的浪潮中,金融證券實(shí)時(shí)信息、地理位置獲取、社交網(wǎng)絡(luò)消息推送等功能對(duì)Web應(yīng)用的實(shí)時(shí)響應(yīng)能力提出了更高要求。

          傳統(tǒng)解決方案的局限性

          1. 輪詢(xún):客戶(hù)端定時(shí)向服務(wù)器發(fā)送請(qǐng)求以保持?jǐn)?shù)據(jù)同步,但這種方法效率低下,因?yàn)樵S多請(qǐng)求可能在服務(wù)器數(shù)據(jù)未更新時(shí)發(fā)送,造成帶寬浪費(fèi)。
          2. 基于Flash:Adobe Flash通過(guò)Socket實(shí)現(xiàn)數(shù)據(jù)交換,并通過(guò)JavaScript接口實(shí)現(xiàn)實(shí)時(shí)傳輸。盡管這種方法比輪詢(xún)更高效,但其在移動(dòng)設(shè)備上的支持不佳。iOS系統(tǒng)不支持Flash,Android系統(tǒng)的支持也不理想。2012年,Adobe宣布停止對(duì)Android 4.1及以上版本的支持,標(biāo)志著Flash在移動(dòng)終端的終結(jié)。

          WebSocket的誕生

          面對(duì)傳統(tǒng)Web模式在高并發(fā)和實(shí)時(shí)性需求上的挑戰(zhàn),業(yè)界迫切需要一種更高效的雙向通信機(jī)制。基于HTML5規(guī)范的WebSocket技術(shù)應(yīng)運(yùn)而生,被譽(yù)為“Web TCP”。

          WebSocket機(jī)制

          WebSocket是HTML5中的一種新協(xié)議,它實(shí)現(xiàn)了瀏覽器與服務(wù)器之間的全雙工通信。與HTTP相比,WebSocket具有以下顯著特點(diǎn):

          • 雙向通信:一旦建立連接,服務(wù)器和客戶(hù)端都可以主動(dòng)發(fā)送或接收數(shù)據(jù),類(lèi)似于傳統(tǒng)的Socket通信。
          • 持久連接:WebSocket連接建立后,數(shù)據(jù)以幀序列的形式傳輸,無(wú)需重新發(fā)起連接請(qǐng)求,顯著降低了網(wǎng)絡(luò)帶寬消耗,并提高了實(shí)時(shí)性。

          WebSocket與HTTP的主要區(qū)別

          1.通信模式

          HTTP:傳統(tǒng)的HTTP協(xié)議是基于請(qǐng)求-響應(yīng)模式的。客戶(hù)端發(fā)起請(qǐng)求,服務(wù)器響應(yīng)請(qǐng)求并返回?cái)?shù)據(jù)。每次通信都需要建立新的連接。

          WebSocket:WebSocket協(xié)議支持全雙工通信。一旦建立連接,客戶(hù)端和服務(wù)器可以相互獨(dú)立地發(fā)送和接收數(shù)據(jù),無(wú)需重新建立連接。

          2.連接持久性

          HTTP:每個(gè)HTTP請(qǐng)求/響應(yīng)對(duì)都需要一個(gè)新的連接,連接在數(shù)據(jù)傳輸完成后關(guān)閉。這種模式適用于偶爾的數(shù)據(jù)交換,但在需要持續(xù)數(shù)據(jù)流的應(yīng)用中效率較低。

          WebSocket:WebSocket連接是持久的。一旦建立,連接會(huì)保持打開(kāi)狀態(tài),允許連續(xù)的數(shù)據(jù)交換,直到顯式關(guān)閉。

          3.實(shí)時(shí)性

          HTTP:由于每次通信都需要建立連接,HTTP的實(shí)時(shí)性較差。適合不需要即時(shí)響應(yīng)的應(yīng)用。

          WebSocket:WebSocket提供了低延遲的實(shí)時(shí)通信,適合需要快速響應(yīng)的應(yīng)用,如在線游戲、實(shí)時(shí)數(shù)據(jù)更新等。

          4.資源消耗

          HTTP:頻繁的連接和斷開(kāi)會(huì)增加服務(wù)器的負(fù)載,消耗更多的資源和帶寬。

          WebSocket:由于連接是持久的,WebSocket減少了連接建立和斷開(kāi)的開(kāi)銷(xiāo),節(jié)省了服務(wù)器資源和帶寬。

          5.協(xié)議實(shí)現(xiàn)

          HTTP:HTTP是基于TCP/IP協(xié)議的,廣泛支持各種瀏覽器和服務(wù)器。

          WebSocket:WebSocket是基于HTTP協(xié)議進(jìn)行握手的,但通信協(xié)議是獨(dú)立的,需要瀏覽器和服務(wù)器的特別支持。

          6.安全性

          HTTP:可以通過(guò)HTTPS來(lái)提供加密傳輸,保護(hù)數(shù)據(jù)安全。

          WebSocket:WebSocket也支持通過(guò)WSS(WebSocket Secure)進(jìn)行加密傳輸,確保數(shù)據(jù)傳輸?shù)陌踩浴?/p>

          7.適用場(chǎng)景

          HTTP:適用于靜態(tài)內(nèi)容傳輸、偶爾的API調(diào)用等不需要持續(xù)連接的場(chǎng)景。

          WebSocket:適用于需要實(shí)時(shí)通信的應(yīng)用,如聊天應(yīng)用、實(shí)時(shí)數(shù)據(jù)推送、在線協(xié)作工具等。

          8.瀏覽器兼容性

          HTTP:幾乎所有的瀏覽器都支持HTTP協(xié)議。

          WebSocket:現(xiàn)代瀏覽器都支持WebSocket,但老舊瀏覽器可能需要額外的插件或不支持。

          案例-簡(jiǎn)單的聊天應(yīng)用

          服務(wù)器端

          1. 初始化WebSocket服務(wù)端: 使用Java和JSR 356規(guī)范,可以創(chuàng)建一個(gè)WebSocket服務(wù)端。
          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("連接打開(kāi): " + session.getId());
              }
          
              @OnMessage
              public void onMessage(String message, Session currentSession) {
                  // 將收到的消息廣播給所有連接的客戶(hù)端
                  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("連接關(guān)閉: " + closeReason.getReasonPhrase());
              }
          
              @OnError
              public void onError(Throwable t) {
                  t.printStackTrace();
              }
          }
          1. 部署WebSocket服務(wù): 將上述代碼部署在支持WebSocket的應(yīng)用服務(wù)器上,如Tomcat。

          客戶(hù)端

          1. 創(chuàng)建HTML頁(yè)面: 創(chuàng)建一個(gè)簡(jiǎn)單的HTML頁(yè)面,用于顯示消息和發(fā)送消息。
          <!DOCTYPE html>
          <html lang="en">
          <head>
              <meta charset="UTF-8">
              <title>簡(jiǎn)單聊天應(yīng)用</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("連接關(guān)閉!");
                      };
                      ws.onerror=function() {
                          alert("連接出錯(cuò)!");
                      };
                  }
          
                  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>
          1. 連接WebSocket服務(wù)器: 在<script>標(biāo)簽中,初始化WebSocket連接,并處理打開(kāi)、消息、關(guān)閉和錯(cuò)誤事件。
          2. 發(fā)送和接收消息: 當(dāng)用戶(hù)在輸入框中輸入消息并按下回車(chē)時(shí),消息會(huì)通過(guò)send()函數(shù)發(fā)送到服務(wù)器。服務(wù)器接收到消息后,會(huì)將消息廣播給所有連接的客戶(hù)端。

          運(yùn)行和測(cè)試

          1. 啟動(dòng)服務(wù)器: 確保應(yīng)用服務(wù)器運(yùn)行,并部署了WebSocket服務(wù)。
          2. 打開(kāi)客戶(hù)端頁(yè)面: 在多個(gè)瀏覽器窗口中打開(kāi)客戶(hù)端HTML頁(yè)面,測(cè)試聊天功能。
          3. 發(fā)送消息: 在任一窗口中輸入消息并發(fā)送,查看其他窗口是否能夠?qū)崟r(shí)接收到消息。

          #開(kāi)發(fā)#


          主站蜘蛛池模板: 成人国产精品一区二区网站公司| 日本一区二区三区日本免费| 久久久精品人妻一区二区三区蜜桃| 一级特黄性色生活片一区二区 | 免费一区二区三区| 久久久久无码国产精品一区| 台湾无码一区二区| 国产伦精品一区二区免费 | 亚洲av无码不卡一区二区三区| 波多野结衣在线观看一区二区三区 | 国产另类ts人妖一区二区三区 | 国产精品高清一区二区三区| 亚洲日韩精品一区二区三区无码| 国产伦精品一区二区三区免费迷| 日韩中文字幕一区| 亚洲日韩精品一区二区三区| 中文国产成人精品久久一区| 一区二区三区伦理高清| 日韩一区二区三区免费体验| 国产精品av一区二区三区不卡蜜| 亚洲国产成人久久综合一区| 人妻av无码一区二区三区| 日韩一区二区在线观看视频| 蜜桃视频一区二区三区在线观看| 日本高清不卡一区| 色久综合网精品一区二区| 亚洲一区二区三区影院| 亚洲国产综合无码一区| 亚洲av不卡一区二区三区| 中文字幕永久一区二区三区在线观看| 色综合视频一区二区三区| 日本在线视频一区| 国产丝袜视频一区二区三区 | 国产精品亚洲综合一区在线观看 | 久久无码人妻一区二区三区午夜 | 国产一区二区影院| 高清一区高清二区视频| 中文字幕一区二区三区四区| av在线亚洲欧洲日产一区二区| 嫩B人妻精品一区二区三区| 无码人妻精品一区二区在线视频 |