1. WebSocket一篇就夠了

        共 7849字,需瀏覽 16分鐘

         ·

        2020-11-22 22:08

        WebSocket protocol 是HTML5一種新的協(xié)議。它實現(xiàn)了瀏覽器與服務器全雙工通信(full-duplex)。一開始的握手需要借助HTTP請求完成。

        ——百度百科

        目的:即時通訊,替代輪詢

        網(wǎng)站上的即時通訊是很常見的,比如網(wǎng)頁的QQ,聊天系統(tǒng)等。按照以往的技術(shù)能力通常是采用輪詢、Comet技術(shù)解決。

        HTTP協(xié)議是非持久化的,單向的網(wǎng)絡協(xié)議,在建立連接后只允許瀏覽器向服務器發(fā)出請求后,服務器才能返回相應的數(shù)據(jù)。當需要即時通訊時,通過輪詢在特定的時間間隔(如1秒),由瀏覽器向服務器發(fā)送Request請求,然后將最新的數(shù)據(jù)返回給瀏覽器。這樣的方法最明顯的缺點就是需要不斷的發(fā)送請求,而且通常HTTP request的Header是非常長的,為了傳輸一個很小的數(shù)據(jù) 需要付出巨大的代價,是很不合算的,占用了很多的寬帶。

        缺點:會導致過多不必要的請求,浪費流量和服務器資源,每一次請求、應答,都浪費了一定流量在相同的頭部信息上

        然而WebSocket的出現(xiàn)可以彌補這一缺點。在WebSocket中,只需要服務器和瀏覽器通過HTTP協(xié)議進行一個握手的動作,然后單獨建立一條TCP的通信通道進行數(shù)據(jù)的傳送。

        原理

        WebSocket同HTTP一樣也是應用層的協(xié)議,但是它是一種雙向通信協(xié)議,是建立在TCP之上的。

        連接過程 —— 握手過程

          1. 瀏覽器、服務器建立TCP連接,三次握手。這是通信的基礎,傳輸控制層,若失敗后續(xù)都不執(zhí)行。

          2. TCP連接成功后,瀏覽器通過HTTP協(xié)議向服務器傳送WebSocket支持的版本號等信息。(開始前的HTTP握手

          3. 服務器收到客戶端的握手請求后,同樣采用HTTP協(xié)議回饋數(shù)據(jù)。

          4. 當收到了連接成功的消息后,通過TCP通道進行傳輸通信。

        WebSocket與HTTP的關(guān)系

        相同點

          1. 都是一樣基于TCP的,都是可靠性傳輸協(xié)議。

          2. 都是應用層協(xié)議。

        不同點

          1. WebSocket是雙向通信協(xié)議,模擬Socket協(xié)議,可以雙向發(fā)送或接受信息。HTTP是單向的。

          2. WebSocket是需要握手進行建立連接的。

        聯(lián)系

        WebSocket在建立握手時,數(shù)據(jù)是通過HTTP傳輸?shù)摹5墙⒅?,在真正傳輸時候是不需要HTTP協(xié)議的。

        WebSocket與Socket的關(guān)系

        Socket其實并不是一個協(xié)議,而是為了方便使用TCP或UDP而抽象出來的一層,是位于應用層和傳輸控制層之間的一組接口。

        Socket是應用層與TCP/IP協(xié)議族通信的中間軟件抽象層,它是一組接口。在設計模式中,Socket其實就是一個門面模式,它把復雜的TCP/IP協(xié)議族隱藏在Socket接口后面,對用戶來說,一組簡單的接口就是全部,讓Socket去組織數(shù)據(jù),以符合指定的協(xié)議。

        當兩臺主機通信時,必須通過Socket連接,Socket則利用TCP/IP協(xié)議建立TCP連接。TCP連接則更依靠于底層的IP協(xié)議,IP協(xié)議的連接則依賴于鏈路層等更低層次。

        WebSocket則是一個典型的應用層協(xié)議。

        區(qū)別

        Socket是傳輸控制層協(xié)議,WebSocket是應用層協(xié)議。

        HTML5與WebSocket的關(guān)系

        WebSocket API 是 HTML5 標準的一部分, 但這并不代表 WebSocket 一定要用在 HTML 中,或者只能在基于瀏覽器的應用程序中使用。

        實際上,許多語言、框架和服務器都提供了 WebSocket 支持,例如:

        • * 基于 C 的 libwebsocket.org
        • * 基于 Node.js 的 Socket.io
        • * 基于 Python 的 ws4py
        • * 基于 C++ 的 WebSocket++
        • * Apache 對 WebSocket 的支持:Apache Module mod_proxy_wstunnel
        • * Nginx 對 WebSockets 的支持:NGINX as a WebSockets Proxy 、 NGINX Announces Support for WebSocket Protocol 、WebSocket proxying
        • * lighttpd 對 WebSocket 的支持:mod_websocket

        WebSocket 機制

        以下簡要介紹一下 WebSocket 的原理及運行機制。

        WebSocket 是 HTML5 一種新的協(xié)議。它實現(xiàn)了瀏覽器與服務器全雙工通信,能更好的節(jié)省服務器資源和帶寬并達到實時通訊,它建立在 TCP 之上,同 HTTP 一樣通過 TCP 來傳輸數(shù)據(jù),但是它和 HTTP 最大不同是:

        • WebSocket 是一種雙向通信協(xié)議,在建立連接后,WebSocket 服務器和 Browser/Client Agent 都能主動的向?qū)Ψ桨l(fā)送或接收數(shù)據(jù),就像 Socket 一樣;
        • WebSocket 需要類似 TCP 的客戶端和服務器端通過握手連接,連接成功后才能相互通信。

        非 WebSocket 模式傳統(tǒng) HTTP 客戶端與服務器的交互如下圖所示:

        圖 1. 傳統(tǒng) HTTP 請求響應客戶端服務器交互圖

        圖 1. 傳統(tǒng) HTTP 請求響應客戶端服務器交互圖

        使用 WebSocket 模式客戶端與服務器的交互如下圖:

        圖 2.WebSocket 請求響應客戶端服務器交互圖


        圖 2.WebSocket 請求響應客戶端服務器交互圖

        上圖對比可以看出,相對于傳統(tǒng) HTTP 每次請求-應答都需要客戶端與服務端建立連接的模式,WebSocket 是類似 Socket 的 TCP 長連接的通訊模式,一旦 WebSocket 連接建立后,后續(xù)數(shù)據(jù)都以幀序列的形式傳輸。在客戶端斷開 WebSocket 連接或 Server 端斷掉連接前,不需要客戶端和服務端重新發(fā)起連接請求。在海量并發(fā)及客戶端與服務器交互負載流量大的情況下,極大的節(jié)省了網(wǎng)絡帶寬資源的消耗,有明顯的性能優(yōu)勢,且客戶端發(fā)送和接受消息是在同一個持久連接上發(fā)起,實時性優(yōu)勢明顯。

        我們再通過客戶端和服務端交互的報文看一下 WebSocket 通訊與傳統(tǒng) HTTP 的不同:

        在客戶端,new WebSocket 實例化一個新的 WebSocket 客戶端對象,連接類似 ws://yourdomain:port/path 的服務端 WebSocket URL,WebSocket 客戶端對象會自動解析并識別為 WebSocket 請求,從而連接服務端端口,執(zhí)行雙方握手過程,客戶端發(fā)送數(shù)據(jù)格式類似:

        清單 1.WebSocket 客戶端連接報文
        GET?/webfin/websocket/?HTTP/1.1
        Host:?localhost
        Upgrade:?websocket
        Connection:?Upgrade
        Sec-WebSocket-Key:?xqBt3ImNzJbYqRINxEFlkg==
        Origin:?http://localhost:8080
        Sec-WebSocket-Version:?13

        可以看到,客戶端發(fā)起的 WebSocket 連接報文類似傳統(tǒng) HTTP 報文,”Upgrade:websocket”參數(shù)值表明這是 WebSocket 類型請求,“Sec-WebSocket-Key”是 WebSocket 客戶端發(fā)送的一個 base64 編碼的密文,要求服務端必須返回一個對應加密的“Sec-WebSocket-Accept”應答,否則客戶端會拋出“Error during WebSocket handshake”錯誤,并關(guān)閉連接。

        服務端收到報文后返回的數(shù)據(jù)格式類似:

        清單 2.WebSocket 服務端響應報文
        HTTP/1.1?101?Switching?Protocols
        Upgrade:?websocket
        Connection:?Upgrade
        Sec-WebSocket-Accept:?K7DJLdLooIwIG/MOpvWFB3y3FE8=

        “Sec-WebSocket-Accept”的值是服務端采用與客戶端一致的密鑰計算出來后返回客戶端的,“HTTP/1.1 101 Switching Protocols”表示服務端接受 WebSocket 協(xié)議的客戶端連接,經(jīng)過這樣的請求-響應處理后,客戶端服務端的 WebSocket 連接握手成功, 后續(xù)就可以進行 TCP 通訊了。

        在開發(fā)方面,WebSocket API 也十分簡單,我們只需要實例化 WebSocket,創(chuàng)建連接,然后服務端和客戶端就可以相互發(fā)送和響應消息,在下文 WebSocket 實現(xiàn)及案例分析部分,可以看到詳細的 WebSocket API 及代碼實現(xiàn)。

        WebSocket 實現(xiàn)

        如上文所述,WebSocket 的實現(xiàn)分為客戶端和服務端兩部分,客戶端(通常為瀏覽器)發(fā)出 WebSocket 連接請求,服務端響應,實現(xiàn)類似 TCP 握手的動作,從而在瀏覽器客戶端和 WebSocket 服務端之間形成一條 HTTP 長連接快速通道。兩者之間后續(xù)進行直接的數(shù)據(jù)互相傳送,不再需要發(fā)起連接和相應。

        以下簡要描述 WebSocket 服務端 API 及客戶端 API。

        WebSocket 服務端 API

        WebSocket 服務端在各個主流應用服務器廠商中已基本獲得符合 JEE JSR356 標準規(guī)范 API 的支持,以下列舉了部分常見的商用及開源應用服務器對 WebSocket Server 端的支持情況:

        表 1.WebSocket 服務端支持
        廠商應用服務器備注
        IBMWebSphereWebSphere 8.0 以上版本支持,7.X 之前版本結(jié)合 MQTT 支持類似的 HTTP 長連接
        甲骨文WebLogicWebLogic 12c 支持,11g 及 10g 版本通過 HTTP Publish 支持類似的 HTTP 長連接
        微軟IISIIS 7.0+支持
        ApacheTomcatTomcat 7.0.5+支持,7.0.2X 及 7.0.3X 通過自定義 API 支持

        JettyJetty 7.0+支持

        以下我們使用 Tomcat7.0.5 版本的服務端示例代碼說明 WebSocket 服務端的實現(xiàn):

        JSR356 的 WebSocket 規(guī)范使用 javax.websocket.*的 API,可以將一個普通 Java 對象(POJO)使用 @ServerEndpoint 注釋作為 WebSocket 服務器的端點,代碼示例如下:

        清單 3.WebSocket 服務端 API 示例
        ?@ServerEndpoint("/echo")
        ?public?class?EchoEndpoint?{

        ?@OnOpen
        ?public?void?onOpen(Session?session)?throws?IOException?{
        ?//以下代碼省略...
        ?}

        ?@OnMessage
        ?public?String?onMessage(String?message)?{
        ?//以下代碼省略...
        ?}

        ?@Message(maxMessageSize=6)
        ?public?void?receiveMessage(String?s)?{
        ?//以下代碼省略...
        ?}?

        ?@OnError
        ?public?void?onError(Throwable?t)?{
        ?//以下代碼省略...
        ?}

        ?@OnClose
        ?public?void?onClose(Session?session,?CloseReason?reason)?{
        ?//以下代碼省略...
        ?}?

        ?}

        代碼解釋:

        上文的簡潔代碼即建立了一個 WebSocket 的服務端,@ServerEndpoint(“/echo”) 的 annotation 注釋端點表示將 WebSocket 服務端運行在 ws://[Server 端 IP 或域名]:[Server 端口]/websockets/echo 的訪問端點,客戶端瀏覽器已經(jīng)可以對 WebSocket 客戶端 API 發(fā)起 HTTP 長連接了。

        使用 ServerEndpoint 注釋的類必須有一個公共的無參數(shù)構(gòu)造函數(shù),@onMessage 注解的 Java 方法用于接收傳入的 WebSocket 信息,這個信息可以是文本格式,也可以是二進制格式。

        OnOpen 在這個端點一個新的連接建立時被調(diào)用。參數(shù)提供了連接的另一端的更多細節(jié)。Session 表明兩個 WebSocket 端點對話連接的另一端,可以理解為類似 HTTPSession 的概念。

        OnClose 在連接被終止時調(diào)用。參數(shù) closeReason 可封裝更多細節(jié),如為什么一個 WebSocket 連接關(guān)閉。

        更高級的定制如 @Message 注釋,MaxMessageSize 屬性可以被用來定義消息字節(jié)最大限制,在示例程序中,如果超過 6 個字節(jié)的信息被接收,就報告錯誤和連接關(guān)閉。

        注意:早期不同應用服務器支持的 WebSocket 方式不盡相同,即使同一廠商,不同版本也有細微差別,如 Tomcat 服務器 7.0.5 以上的版本都是標準 JSR356 規(guī)范實現(xiàn),而 7.0.2x/7.0.3X 的版本使用自定義 API (WebSocketServlet 和 StreamInbound, 前者是一個容器,用來初始化 WebSocket 環(huán)境;后者是用來具體處理 WebSocket 請求和響應,詳見案例分析部分),且 Tomcat7.0.3x 與 7.0.2x 的 createWebSocketInbound 方法的定義不同,增加了一個 HttpServletRequest 參數(shù),使得可以從 request 參數(shù)中獲取更多 WebSocket 客戶端的信息,如下代碼所示:

        清單 4.Tomcat7.0.3X 版本 WebSocket API
        public?class?EchoServlet?extends?WebSocketServlet?{
        @Override
        protected?StreamInbound?createWebSocketInbound(String?subProtocol,
        HttpServletRequest?request)?{
        ?//以下代碼省略....
        return?new?MessageInbound()?{
        ?//以下代碼省略....
        }
        protected?void?onBinaryMessage(ByteBuffer?buffer)
        throws?IOException?{
        ?//以下代碼省略...
        }
        protected?void?onTextMessage(CharBuffer?buffer)?throws?IOException?{
        ?getWsOutbound().writeTextMessage(buffer);
        ?//以下代碼省略...
        }
        };
        }
        }

        因此選擇 WebSocket 的 Server 端重點需要選擇其版本,通常情況下,更新的版本對 WebSocket 的支持是標準 JSR 規(guī)范 API,但也要考慮開發(fā)易用性及老版本程序移植性等方面的問題,如下文所述的客戶案例,就是因為客戶要求統(tǒng)一應用服務器版本所以使用的 Tomcat 7.0.3X 版本的 WebSocketServlet 實現(xiàn),而不是 JSR356 的 @ServerEndpoint 注釋端點。

        WebSocket 客戶端 API

        對于 WebSocket 客戶端,主流的瀏覽器(包括 PC 和移動終端)現(xiàn)已都支持標準的 HTML5 的 WebSocket API,這意味著客戶端的 WebSocket JavaScirpt 腳本具備良好的一致性和跨平臺特性,以下列舉了常見的瀏覽器廠商對 WebSocket 的支持情況:

        表 2.WebSocket 客戶端支持
        瀏覽器支持情況
        ChromeChrome version 4+支持
        FirefoxFirefox version 5+支持
        IEIE version 10+支持
        SafariIOS 5+支持
        Android BrowerAndroid 4.5+支持

        客戶端 WebSocket API 基本上已經(jīng)在各個主流瀏覽器廠商中實現(xiàn)了統(tǒng)一,因此使用標準 HTML5 定義的 WebSocket 客戶端的 JavaScript API 即可,當然也可以使用業(yè)界滿足 WebSocket 標準規(guī)范的開源框架,如 Socket.io。

        以下以一段代碼示例說明 WebSocket 的客戶端實現(xiàn):

        清單 5.WebSocket 客戶端 API 示例
        var?ws?=?new?WebSocket(“ws://echo.websocket.org”);?
        ?ws.onopen?=?function(){ws.send(“Test!”);?};?
        ?ws.onmessage?=?function(evt){console.log(evt.data);ws.close();};?
        ?ws.onclose?=?function(evt){console.log(“WebSocketClosed!”);};?
        ?ws.onerror?=?function(evt){console.log(“WebSocketError!”);};

        第一行代碼是在申請一個 WebSocket 對象,參數(shù)是需要連接的服務器端的地址,同 HTTP 協(xié)議開頭一樣,WebSocket 協(xié)議的 URL 使用 ws://開頭,另外安全的 WebSocket 協(xié)議使用 wss://開頭。

        第二行到第五行為 WebSocket 對象注冊消息的處理函數(shù),WebSocket 對象一共支持四個消息 onopen, onmessage, onclose 和 onerror,有了這 4 個事件,我們就可以很容易很輕松的駕馭 WebSocket。

        當 Browser 和 WebSocketServer 連接成功后,會觸發(fā) onopen 消息;如果連接失敗,發(fā)送、接收數(shù)據(jù)失敗或者處理數(shù)據(jù)出現(xiàn)錯誤,browser 會觸發(fā) onerror 消息;當 Browser 接收到 WebSocketServer 發(fā)送過來的數(shù)據(jù)時,就會觸發(fā) onmessage 消息,參數(shù) evt 中包含 Server 傳輸過來的數(shù)據(jù);當 Browser 接收到 WebSocketServer 端發(fā)送的關(guān)閉連接請求時,就會觸發(fā) onclose 消息。我們可以看出所有的操作都是采用異步回調(diào)的方式觸發(fā),這樣不會阻塞 UI,可以獲得更快的響應時間,更好的用戶體驗。


        原文鏈接:https://blog.csdn.net/qq_35812160/article/details/78225823
        END/往期推薦:




        1.微服務實戰(zhàn)系列

        2.springboot從入門到精通

        3.java入門到精通

        4.中間件等

        5.程序人生

        更多信息請關(guān)注公眾號:「軟件老王」,關(guān)注不迷路,軟件老王和他的IT朋友們,分享一些他們的技術(shù)見解和生活故事。

        瀏覽 130
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
          
          

            1. 91处女在线 | 亚洲干综合 | 狠狠五月 | 三上悠亚禁欲snis986在线 | 成年人在线视频观看 |