整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          HTML5(十二)-一文讀懂 WebSocket 原

          HTML5(十二)-一文讀懂 WebSocket 原理

          、WebSocket 由來

          WebSocket 是一個持久化的協議,通過第一次 HTTP Request 建立連接之后,再把通信協議升級成 websocket,保持連接狀態,后續的數據交換不需要再重復請求。websocket 可以看成一種類似 TCP/IP 的 socke t技術,在 web 應用中實現、并獲得同 TCP/IP 通信一樣的雙向通信功能,因此客戶端既和服務器可以發送消息也可以接收消息,同時還支持多路復用的功能,由于它借用了 HTTP 協議的一些概念,所以被稱為 WebSocket。

          webSocket API定義了web應用和服務器進行通信的公共接口,具體的構造函數創建對象、對象的屬性、方法、事件及它的意義,在上一篇《HTML5(十一)——WebSocket 基礎教程》文章中已詳細介紹。

          二、WebSocket 通信過程

          WebSocket 協議可分為兩部分:握手階段和數據通信階段。

          WebSocket 為應用層協議,定義在 TCP/IP 協議棧之上,連接服務器的 url 是以 ws 或 wss 開頭的。ws 開頭的默認TCP端口為80,wss 開頭的默認端口為443。

          ws(websocket)是不安全的,容易被竊聽,只要別人知道你的ip和端口號,任何人都可以去連接通訊。

          wss(web socket secure)是websocket的加密版本。

          2.1、建立連接

          客戶端去與服務器建立 TCP 連接,客戶端生成 websocket 對象,然后使用 API 建立連接,代碼如下:

          let ws=new WebSocket('ws://localhost:8888')
          ws.onopen=function(){
           console.log("連接")
          }

          2.2、握手階段

          客戶端與服務器建立連接之后,客戶端發送握手請求,隨后服務器發送握手響應即完成握手階段。

          客戶端握手請求如下:

          'GET / HTTP/1.1',
           'Host: localhost:8888',
           'Connection: Upgrade',
           'Pragma: no-cache',
           'Cache-Control: no-cache',
           'Upgrade: websocket',
           'Origin: file://',
           'Sec-WebSocket-Version: 13',
           'Accept-Encoding: gzip, deflate, br',
           'Accept-Language: zh-CN,zh;q=0.9',
           'Sec-WebSocket-Key: In1aAp/ya9Lkv+tsUtXLXQ==',
           'Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits',

          服務器握手響應如下:

          Status Code: 101 Switching Protocols
          Connection: Upgrade
          sec-websocket-Accept: HBMDBbZMiS59r3aAITpGtJ64Mfc=Upgrade: websocket

          2.3、數據通訊

          WebSocket 握手連接成功之后。可以使用 send 進行發送數據,onmessage 接收數據,如下發送“你好”:

          let ws=new WebSocket('ws://localhost:8888')
          ws.onopen=function(){
           console.log("連接成功")
           ws.send("你好")
          }
          ws.onmessage=function(res){
           console.log('接收到的消息',res)
          }

          服務器打印接收到的數據,如:<Buffer 81 86 af 87 53 b4 4b 3a f3 51 0a 3a>。

          websocket 在發送數據時,被組織為一串數據幀,然后進行發送。傳送的幀包含兩部分:數據幀和控制幀。數據幀可以攜帶文本數據或者二進制數據,控制幀包含關閉幀和 Ping/Pong 幀。


          • FIN :1bit ,表示是消息的最后一幀,如果消息只有一幀那么第一幀也就是最后一幀。
          • RSV1,RSV2,RSV3:每個1bit,必須是0,除非擴展定義為非零。如果接受到的是非零值但是擴展沒有定義,則需要關閉連接。
          • Opcode:4bit,解釋Payload數據,規定有以下不同的狀態,如果是未知的,接收方必須馬上關閉連接。狀態如下:0x0(附加數據幀) 0x1(文本數據幀) 0x2(二進制數據幀) 0x3-7(保留為之后非控制幀使用) 0xB-F(保留為后面的控制幀使用) 0x8(關閉連接幀) 0x9(ping) 0xA(pong)
          • Mask:1bit,掩碼,定義payload數據是否進行了掩碼處理,如果是1表示進行了掩碼處理。Masking-key域的數據即是掩碼密鑰,用于解碼PayloadData。客戶端發出的數據幀需要進行掩碼處理,所以此位是1。
          • Payload length:7位,7 + 16位,7+64位,payload數據的長度,如果是0-125,就是真實的payload長度,如果是126,那么接著后面的2個字節對應的16位無符號整數就是payload數據長度;如果是127,那么接著后面的8個字節對應的64位無符號整數就是payload數據的長度。
          • Masking-key:0到4字節,如果MASK位設為1則有4個字節的掩碼解密密鑰,否則就沒有。
          • Payload data:任意長度數據。包含有擴展定義數據和應用數據,如果沒有定義擴展則沒有此項,僅含有應用數據。

          把接收到的buffer十六進制數據轉成二進制數據,控制幀與上述各個類型幀進行對比解析其意義。

          2.4、關閉連接

          任何一端可以關閉連接。客戶端關閉連接如下:

          ws.close()

          然后發送關閉幀給對方,通常會帶有關閉連接的狀態碼,常見的狀態碼如下:

          • 1000 連接正常關閉
          • 1001 端點離線,例如服務器down,或者瀏覽器已經離開此頁面
          • 1002 端點因為協議錯誤而中斷連接
          • 1003 端點因為受到不能接受的數據類型而中斷連接
          • 1004 保留
          • 1005 保留, 用于提示應用未收到連接關閉的狀態碼
          • 1006 端點異常關閉
          • 1007 端點收到的數據幀類型不一致而導致連接關閉
          • 1008 數據違例而關閉連接
          • 1009 收到的消息數據太大而關閉連接
          • 1010 客戶端因為服務器未協商擴展而關閉
          • 1011 服務器因為遭遇異常而關閉連接
          • 1015 TLS握手失敗關閉連接

          三、websocket 實例

          3.1、客戶端創建websocket對象,并建立連接之后發送數據。其中 ws 地址根據后臺服務端口對應。

          <!DOCTYPE html>
          <html lang="en">
          <head>
           <meta charset="UTF-8">
           <meta http-equiv="X-UA-Compatible" content="IE=edge">
           <meta name="viewport" content="width=device-width, initial-scale=1.0">
           <title>Document</title>
          </head>
          <body>
           <script>
            let ws=new WebSocket('ws://localhost:8888')
            ws.onopen=function(){
             console.log("連接")
             ws.send("你好")
             }
             ws.onmessage=function(res){
              console.log('res',res)
            }
           </script>
          </body>
          </html>

          3.2、使用 node.js 創建一個 websocket 服務,如創建一個serve.js文件,代碼如下:

          const http=require("http")
          const net=require("net") //原生的websocket
          const crypto=require('crypto') // 安全性校驗
          let serve=net.createServer(sock=>{
           //只握手一次
           sock.once('data',(data)=>{
            console.log("hand shake start") // 開始握手
            let str=data.toString();
            let lines=str.split('\r\n')
            //舍棄第一行和最后兩行
            lines=lines.slice(1,lines.length-2)
            let headers={}
            lines.forEach(line=>{
             let [key,val]=line.split(': ')
             headers[key.toLowerCase()]=val
            })
            if( headers['upgrade']!='websocket' ){
              console.log("其他協議")
              sock.end()
            }else if(headers['sec-websocket-version']!=13){
             console.log("版本不對")
             sock.end()
            }else{
             let key=headers['sec-websocket-key']
             let mask='258EAFA5-E914-47DA-95CA-C5AB0DC85B11'
             //sha1(key+mask) -> base64=>client
             let hash=crypto.createHash('sha1')
             hash.update(key+mask)
             let key2=hash.digest('base64')
             sock.write(`HTTP/1.1 101 Switching Protocols\r\nUpgrade:websocket\r\nConnection:Upgrade\r\nsec-websocket-Accept:${key2}\r\n\r\n` )
             console.log("hand shake end") // 握手結束
             //真正的數據
             sock.on('data',res=>{
             console.log("真正接收數據",res)
             //數據解析
             let FIN=res[0]&0x001;
             let opcode=data[0]&0x0F0;
             let msak=data[1]&0x001;
             let payload=data[1]&0x0FE;          
            })
            }
           })
           //斷開
           sock.on('end',()=>{
            console.log("連接已斷開")
           })
          })
          serve.listen("8888")
          

          使用命令 node serve.js 或node serve 啟動服務,服務啟動成功之后可以使用localhost:8888訪問服務。

          啟動服務之后,訪問前邊創建的html文件訪問websocket服務。

          四、websocket的優點

          • 第一次通過http建立連接之后,數據交互不用發送http請求,節省了帶寬資源。
          • websocket連接是雙向通信,服務器和客戶端既可接受也可發送消息。
          • websocket多路復用,幾個不同url可以復用一個websocket服務。
          • 是HTML5的技術之一,有巨大應用前景。

          ebSocket 是 HTML5 開始提供的一種在單個 TCP 連接上進行全雙工通訊的協議。

          WebSocket 使得客戶端和服務器之間的數據交換變得更加簡單,允許服務端主動向客戶端推送數據。在 WebSocket API 中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,并進行雙向數據傳輸。可以說WebSocket的出現,使得瀏覽器具備了實時雙向通信的能力

          在 WebSocket API 中,瀏覽器和服務器只需要做一個握手的動作,然后,瀏覽器和服務器之間就形成了一條快速通道。兩者之間就直接可以數據互相傳送。

          原來為了實現推送,很多公司用的是Ajax 輪詢,即按照特定的時間間隔,由瀏覽器對服務器發出HTTP請求,然后由服務器返回最新的數據給客戶端的瀏覽器。這種傳統的模式帶來很明顯的缺點,即瀏覽器需要不斷的向服務器發出請求,然而HTTP請求可能包含較長的頭部,其中真正有效的數據可能只是很小的一部分,顯然這樣會浪費很多的帶寬等資源。而websocket就可以解決這些問題。websocker有如下優點

          1. 支持雙向通信,實時性更強。
          2. 更好的二進制支持。
          3. 較少的控制開銷。連接創建后,ws客戶端、服務端進行數據交換時,協議控制的數據包頭部較小。在不包含頭部的情況下,服務端到客戶端的包頭只有2~10字節(取決于數據包長度),客戶端到服務端的的話,需要加上額外的4字節的掩碼。而HTTP協議每次通信都需要攜帶完整的頭部。
          4. 支持擴展。ws協議定義了擴展,用戶可以擴展協議,或者實現自定義的子協議。(比如支持自定義壓縮算法等)

          HTML5 定義的 WebSocket 協議,能更好的節省服務器資源和帶寬,并且能夠更實時地進行通訊。

          websocket在越來越多的公司開始開始使用,如推送,客服聊天之類的,今天推薦一個在線調試工具,使用起來超級方便,如果有更好的工具歡迎大家在下方留言,地址為:http://www.websocket-test.com/

          還有并發壓力測試工具等一系列實用功能

          ebSocket 和 Socket 的區別就像Java和JavaScript,并沒有什么太大的關系,但又不能說完全沒關系。可以這么說:

          • 命名方面,Socket是一個深入人心的概念,WebSocket借用了這一概念;
          • 使用方面,完全兩個東西。

          Java和JavaScript的關系

          當我們探討兩件事物的區別和聯系時,我們想探討些什么?

          對這個問題最直接的解決方法應該是去了解Socket和WebSocket的來源和用法,那么它們的區別和聯系就不言自明了。

          Socket

          Socket可以有很多意思,和IT較相關的本意大致是指在端到端的一個連接中,這兩個端叫做Socket。對于IT從業者來說,它往往指的是TCP/IP網絡環境中的兩個連接端,大多數的API提供者(如操作系統,JDK)往往會提供基于這種概念的接口,所以對于開發者來說也往往是在說一種編程概念。同時,操作系統中進程間通信也有Socket的概念,但這個Socket就不是基于網絡傳輸層的協議了。

          Unix 中的 Socket

          操作系統中也有使用到Socket這個概念用來進行進程間通信,它和通常說的基于TCP/IP的Socket概念十分相似,代表了在操作系統中傳輸數據的兩方,只是它不再基于網絡協議,而是操作系統本身的文件系統。

          網絡中的 Socket

          通常所說的Socket API,是指操作系統中(也可能不是操作系統)提供的對于傳輸層(TCP/UDP)抽象的接口。現行的Socket API大致都是遵循了BSD Socket規范(包括Windows)。這里稱規范其實不太準確,規范其實是POSIX,但BSD Unix中對于Socket的實現被廣為使用,所以成為了實際的規范。如果你要使用HTTP來構建服務,那么就不需要關心Socket,如果你想基于TCP/IP來構建服務,那么Socket可能就是你會接觸到的API。

          在TCP/IP網絡中HTTP的位置

          從上圖中可以看到,HTTP是基于傳輸層的TCP協議的,而Socket API也是,所以只是從使用上說,可以認為Socket和HTTP類似(但一個是成文的互聯網協議,一個是一直沿用的一種編程概念),是對于傳輸層協議的另一種直接使用,因為按照設計,網絡對用戶的接口都應該在應用層。

          Socket 名稱的由來

          和很多其他Internet上的事物一樣,Socket這個名稱來自于大名鼎鼎的ARPANET(Advanced Research Projects Agency),早期ARPANET中的Socket指的是一個源或者目的地址——大致就是今天我們所說的IP地址和端口號。最早的時候一個Socket指的是一個40位的數字(RFC33中說明了此用法,但在RFC36中并沒有明確地說使用40位數字來標識一個地址),其中前32為指向的地址(socket number,大致相當于IP),后8位為發送數據的源(link,大致相當于端口號)。對他們的叫法有很多的版本,這里列舉的并不嚴謹。

          領取C++音視頻開發學習資料:點擊→音視頻開發(資料文檔+視頻教程+面試題)(FFmpeg+WebRTC+RTMP+RTSP+HLS+RTP)

          端口號的野史

          隨著ARPANET的發展,后來(RFC433,Socket Number List)socket number被明確地定義為一個40位的數字,其中后8位被用來制定某個特定的應用使用(比如1是Telnet)。這8位數有很多名字:link、socket name、AEN(another eight number,看到這個名字我也是醉了),工程師逗逼起來也是挺拼的。

          后來在Internet的規范制定中,才真正的用起了port number這個詞。至于為什么端口號是16位的,我想可能有兩個原因,一是對于當時的工程師來說,如果每個端口號來標識一個程序,65535個端口號也差不多夠用了。二可能是為了對齊吧,^_^!!。


          Socket 原本的意思

          在上邊提到的歷史中使用到的Socket,包括TCP文檔中使用到的Socket,其實指的是網絡傳輸中的一端,是一個虛擬化的概念。


          WebSocket

          上邊簡單敘述了Socket的意義,由于年代久遠,很多事情也搞不了那么清楚。但WebSocket是一個很晚近的東西,可以讓我們看到它是如何成為現在我們看到的這個樣子的。


          WHATWG(Web Hypertext Application Technology Working Group)

          關于HTML5的故事很多人都是知道的,w3c放棄了HTML,然后有一群人(也有說是這些人供職的公司,不過官方的文檔上是說的個人)創立了WHATWG組織來推動HTML語言的繼續發展,同時,他們還發展了很多關于Web的技術標準,這些標準不斷地被官方所接受。WebSocket就屬于WHATWG發布的Web Application的一部分(即HTML5)的產物。


          為什么會有 WebSocket

          大約在08年的時候,WG的工程師在討論網絡環境中需要一種全雙工的連接形式,剛開始一直叫做「TCPConnection」,并討論了這種協議需要支持的功能,大致已經和我們今天看到的WebSocket差不多了。他們認為基于現有的HTTP之上的一些技術(如長輪詢、Comet)并滿足不了這種需求,有必要定義一個全新的協議。


          名稱的由來

          在很多的關于HTML5或者WebSocket的文檔中,都能看到一個名字,Hixie(Ian Hickson),他是WHATWG組織的發言人,曾供職于Netscape、Opera、Google,看工作的公司就知道這個人的背景了。

          08年6月18日,一群WHATWG的工程師在討論一些技術問題,一個工程師提到說「我們之前討論的那個東西,不要叫TCPConnection 了,還是起個別的名字吧 」,接著幾個名字被提及,DuplexConnection,TCPSocket,SocketConnection ,一個叫mcarter(Michael Carter )的工程師說他馬上要寫一篇關于Comet的文章,如果可以確定這個名稱,想在文章中引用這個名字。

          Socket一直以來都被人用來表示網絡中一個連接的兩端,考慮到怎么讓工程師更容易接受,后來Hixie說了一句「我看WebSocket這個名字就很適合嘛(Hixie briefly pops back online to record that “WebSocket” would probably be a good new name for the TCPConnection object)」,大家都沒有異議,緊接著mcarter在Comet Daily中發表了文章Independence Day: HTML5 WebSocket Liberates Comet From Hacks,后來隨著各大瀏覽器對WebSocket的支持,它變成了實際的標準,IETF也沿用了這個名字。

          下邊是在WHATWG文檔中對WebSocket接口的定義

              enum BinaryType { "blob", "arraybuffer" };
          
              [Constructor(USVString url, optional (DOMString or sequence<DOMString>) protocols=[]), Exposed=(Window,Worker)]
          
              interface WebSocket : EventTarget {
          
                readonly attribute USVString url;
          
               
          
                // ready state
          
                const unsigned short CONNECTING=0;
          
                const unsigned short OPEN=1;
          
                const unsigned short CLOSING=2;
          
                const unsigned short CLOSED=3;
          
                readonly attribute unsigned short readyState;
          
                readonly attribute unsigned long long bufferedAmount;
          
               
          
                // networking
          
                attribute EventHandler onopen;
          
                attribute EventHandler onerror;
          
                attribute EventHandler onclose;
          
                readonly attribute DOMString extensions;
          
                readonly attribute DOMString protocol;
          
                void close([Clamp] optional unsigned short code, optional USVString reason);
          
               
          
                // messaging
          
                attribute EventHandler onmessage;
          
                attribute BinaryType binaryType;
          
                void send(USVString data);
          
                void send(Blob data);
          
                void send(ArrayBuffer data);
          
                void send(ArrayBufferView data);
          
              };

          內容的確定

          大多數新技術的出現都是建立在已有技術的鋪墊之上的,WebSocket內容的確定也是如此,其中就有Comet看不到的貢獻,Comet是一個很有趣的技術,有興趣可以看看這里


          結論

          可以把WebSocket想象成HTTP,HTTP和Socket什么關系,WebSocket和Socket就是什么關系。


          主站蜘蛛池模板: 免费视频精品一区二区| 久久久老熟女一区二区三区| 亚洲一区在线免费观看| 免费一本色道久久一区| 国产一区二区在线观看视频| 久久精品视频一区| 一区二区三区四区视频在线| 精品一区高潮喷吹在线播放| 国产婷婷一区二区三区| 中文字幕无线码一区2020青青 | 无码人妻一区二区三区免费n鬼沢 无码人妻一区二区三区免费看 | 人妻少妇一区二区三区| 国产成人精品一区二三区熟女 | 中文字幕日韩一区| 久久免费视频一区| 国产伦一区二区三区免费| 国产日韩一区二区三区| 精品乱人伦一区二区三区| 中文字幕日韩人妻不卡一区 | 三上悠亚亚洲一区高清| 国产高清视频一区二区| 亚洲宅男精品一区在线观看| 精品亚洲一区二区三区在线观看| 成人区人妻精品一区二区三区 | 福利一区福利二区| 国产综合精品一区二区| 国产未成女一区二区三区| 精品无码人妻一区二区三区品 | 精品女同一区二区三区免费播放| 日本高清一区二区三区| 精品一区二区三区免费毛片爱| 在线精品国产一区二区三区| 日本一区二区三区在线网| 日韩在线观看一区二区三区| 少妇一夜三次一区二区| 91午夜精品亚洲一区二区三区| 亚洲AV无码一区二区乱子伦 | 亚洲一区二区三区在线观看精品中文 | 日韩精品无码一区二区三区不卡| 久久无码精品一区二区三区| 国产日韩AV免费无码一区二区三区|