瞭解WebSocket及Socket.io

以前在項目中簡單的使用過,可是追究使用它的原因、優勢以及原理,在這以前筆者也是模糊不清,因此在這期間,作了比較系統的瞭解後,在此記錄一番。html

話很少說,切入正題。可能在瞭解到這個協議的時候,大多數人都不知道它是作什麼,或者說不知道爲何須要這個協議,那麼咱們就從基礎開始,一點點的瞭解。node

仍是如今這裏粘一個下面代碼的github地址。 git

1、WebSocket基本知識

1.1 WebSocket簡單介紹

WebSocket是一種 網絡傳輸協議(說到網絡協議,你們可能會立馬想到HTTP協議,下面會有二者的對比),可在單個TCP鏈接上進行全雙工通訊,位於OSI模型的應用層。github

WebSocket使得客戶端和服務器之間的數據交換變得更加簡單,容許服務端向客戶端推送數據。在WebSocket API中,瀏覽器和服務器只須要完成一次握手,二者之間就能夠建立持久性的連接,並進行雙向數據傳輸web

WebSocket協議規範將 ws(WebSocket)和 wss(WebSocket Secure)定義爲兩個新的統一資源標識符,分別對應明文和加密鏈接。ajax

1.2 爲何須要WebSocket

WebSocket最大特色就是,服務器能夠主動向客戶端推送信息,客戶端也能夠主動向服務器發送信息。算法

在這個協議以前,不少網站爲了實現 推送技術,所用的技術都是輪詢。輪詢是在特定的時間間隔,由瀏覽器對服務器發出HTTP請求,而後由服務器返回最新的數據給客戶端的瀏覽器。這種傳統的模式帶來很明顯的缺點,即瀏覽器須要不斷的向服務器發出請求,然而HTTP請求可能包含較長的頭部,其中真正有效的數據可能只是很小的一部分,顯然這樣會消耗不少的帶寬資源。npm

在這種狀況下,HTML5定義了WebSocket協議,能更好的節省服務器資源和帶寬,而且可以更實時地進行通信。api

1.3 WebSocket特色

  • 創建在 TCP 協議之上,服務器端的實現比較容易。
  • 與 HTTP 協議有着良好的兼容性。默認端口也是80和443,而且握手階段採用 HTTP 協議,所以握手時不容易屏蔽,能經過各類 HTTP 代理服務器。
  • 數據格式比較輕量,性能開銷小,通訊高效。
  • 能夠發送文本,也能夠發送二進制數據。
  • 沒有同源限制,客戶端能夠與任意服務器通訊。
  • 協議標識符是ws(若是加密,則爲wss),服務器網址就是 URL。

1.4 WebSocket優勢

在上面簡單的介紹WebSocket以後,想必你們也均可以總結出一些WebSocket的優勢,下面相較於HTTP再作進一步的總結瀏覽器

  • 較少的控制開銷:在鏈接建立後,服務器和客戶端之間交換數據時,用於協議控制的數據包頭部相對較小。在不包含擴展的狀況下,對於服務器到客戶端的內容,此頭部大小隻有2至10字節(和數據包長度有關);對於客戶端到服務器的內容,此頭部還須要加上額外的4字節的掩碼。相對於HTTP請求每次都要攜帶完整的頭部,此項開銷顯著減小了。

  • 更強的實時性:因爲協議是全雙工的,因此服務器能夠隨時主動給客戶端下發數據。相對於HTTP請求須要等待客戶端發起請求服務端才能響應,延遲明顯更少

  • 保持鏈接狀態:Websocket須要先建立鏈接,這就使得其成爲一種有狀態的協議,以後通訊時能夠省略部分狀態信息。而HTTP請求可能須要在每一個請求都攜帶狀態信息(如身份認證等)。

  • 更好的二進制支持:Websocket定義了二進制幀,相對HTTP,能夠更輕鬆地處理二進制內容

2、WebSocket進階知識

簡單來說,WebSocket協議由兩部分組成:創建鏈接過程(握手)數據傳輸

2.1 創建鏈接(握手)

在第一部分的介紹中,咱們提到,WebSocket在建立持久性鏈接以前,須要進行一次握手,並且爲了兼容性考慮,WebSocket複用了HTTP的握手通道。具體指的是,客戶端經過HTTP請求與WebSocket服務端協商升級協議。協議升級完成以後,後續的數據交換則遵守WebSocket的協議。

客戶端:升級協議版本

首先,客戶端發起協議升級請求,從下圖能夠看到,採用的是標準的HTTP報文格式,且只支持 GET方法。

重點說明一下上面四處的意義:

  • Connection:Upgrade:表示要升級協議
  • Upgrade:WebSocket:表示要升級到WebSocket協議
  • Sec-WebSocket-Key與後面服務器端響應首部的Sec-WebSocket-Accept是配套的,提供基本的防禦,好比惡意的連接,或者無心的鏈接
  • Sec-WebSocket-Version: 13:表示WebSocket的版本。若是服務器不支持該版本,須要返回一個Sec-WebSocket-Versionheader,裏面包含服務端支持的版本號

注意,上面請求省略了部分非重點請求首部。因爲是標準的HTTP請求,相似Host、Origin、Cookie等請求首部會照常發送。在握手階段,能夠經過相關請求首部進行 安全限制、權限校驗等。

服務端:響應協議升級

服務端返回內容以下,狀態碼 101表示協議切換。到此完成協議升級,後序的數據交互都按照新的協議進行。

Sec-WebSocket-Accept的計算

Sec-WebSocket-Accept根據客戶端請求首部的Sec-WebSocket-Key計算出來。

計算公式爲:

Sec-WebSocket-Key258EAFA5-E914-47DA-95CA-C5AB0DC85B11拼接。 經過SHA1計算出摘要,並轉成base64字符串。 僞代碼以下:

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

2.2 數據傳遞

一旦WebSocket客戶端、服務端創建鏈接後,後續的操做都是基於數據幀的傳遞。

由於此處涉及到了數據幀的知識,因此能夠先查看2.3 數據幀格式的部分。

一、數據分片

WebSocket的每條消息可能被切分紅多個數據幀。當WebSocket的接收方收到一個數據幀時,會根據FIN的值來判斷,是否已經收到消息的最後一個數據幀。

FIN=1表示當前數據幀爲消息的最後一個數據幀,此時接收方已經收到完整的消息,能夠對消息進行處理。FIN=0,則接收方還須要繼續監聽接收其他的數據幀。

此外,opcode在數據交換的場景下,表示的是數據的類型。0x01表示文本,0x02表示二進制。而0x00比較特殊,表示延續幀(continuation frame),顧名思義,就是完整消息對應的數據幀還沒接收完。

二、數據分片例子

直接看例子更形象些。下面例子來自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!

複製代碼

2.3 數據幀格式

客戶端、服務端數據的交換,離不開數據幀格式的定義。因此咱們在這裏看一看WebSocket的數據幀格式。

客戶端、服務端數據的交換,離不開數據幀格式的定義。所以,在實際講解數據交換以前,咱們先來看下WebSocket的數據幀格式。

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

  • 發送端:將消息切割成多個幀,併發送給服務端;

  • 接收端:接收消息幀,並將關聯的幀從新組裝成完整的消息;

本節的重點,就是講解數據幀的格式。

一、數據幀格式概覽

下面給出了WebSocket數據幀的統一格式。

從左到右,單位是比特。好比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 ...                |
 +---------------------------------------------------------------+
複製代碼

二、數據幀格式詳解

針對前面的格式概覽圖,這裏逐個字段進行講解,能夠參考協議規範。

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:表示鏈接斷開。

  • %x9:表示這是一個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字節。全部的擴展都必須聲明擴展數據的長度,或者能夠如何計算出擴展數據的長度。此外,擴展如何使用必須在握手階段就協商好。若是擴展數據存在,那麼載荷數據長度必須將擴展數據的長度包含在內。

  • 應用數據:任意的應用數據,在擴展數據以後(若是存在擴展數據),佔據了數據幀剩餘的位置。載荷數據長度 減去 擴展數據長度,就獲得應用數據的長度。

三、掩碼算法 掩碼鍵(Masking-key)是由客戶端挑選出來的32位的隨機數。掩碼操做不會影響數據載荷的長度。掩碼、反掩碼操做都採用以下算法:

首先,假設:

  • original-octet-i:爲原始數據的第i字節。

  • transformed-octet-i:爲轉換後的數據的第i字節。

  • j:爲i mod 4的結果。

  • masking-key-octet-j:爲mask key第j字節。

算法描述爲: original-octet-imasking-key-octet-j異或後,獲得 transformed-octet-i

j = i MOD 4
transformed-octet-i = original-octet-i XOR masking-key-octet-j
複製代碼

2.4 鏈接保持(保持長鏈接)

WebSocket爲了保持客戶端、服務端的實時雙向通訊,須要確保客戶端、服務端之間的TCP通道保持鏈接沒有斷開。然而,對於長時間沒有數據往來的鏈接,若是依舊長時間保持着,可能會浪費包括的鏈接資源。

但不排除有些場景,客戶端、服務端雖然長時間沒有數據往來,但仍須要保持鏈接。這個時候,能夠採用心跳來實現。

  • 發送方->接收方:ping 接收方->發送方:`pong` `ping`、`pong的操做`,對應的是WebSocket的兩個控制幀,`opcode分別是0x90xA`。

在這一部分最後,在說明兩個知識點(不作詳細說明)

1.Sec-WebSocket-Key/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的轉換計算公式是公開的,並且很是簡單,最主要的做用是預防一些常見的意外狀況(非故意的)。

2. 數據掩碼的做用:

WebSocket協議中,數據掩碼的做用是加強協議的安全性(並非爲了防止數據泄密,而是爲了防止早期版本的協議中存在的代理緩存污染攻擊(proxy cache poisoning attacks)等問題。)。但數據掩碼並非爲了保護數據自己,由於算法自己是公開的,運算也不復雜。除了加密通道自己,彷佛沒有太多有效的保護通訊安全的辦法。

3、 WebSocket實例

3.1 客戶端代碼示例

<input type="text" id="sendTxt">
  <button id="sendBtn">發送</button>

  <div id="recv"></div>
  
  <script>
    /**
     * WebSocket對象做爲一個構造函數,用於新建WebSocket實例
     * 執行下面的語句以後,客戶端就會個服務器進行鏈接
     */
    let webSocket = new WebSocket("wss://echo.websocket.org");

    /**
     * 下面結合實際講一下WebSocket實例對象的屬性和方法
     * 1. 屬性
     * 1.1 webSocket.readyState(屬性返回實例對象的當前狀態)
     *     . CONNECTING:值爲0,表示正在鏈接。
     *     . OPEN:值爲1,表示鏈接成功,能夠通訊了。
     *     . CLOSING:值爲2,表示鏈接正在關閉。
     *     . CLOSED:值爲3,表示鏈接已經關閉,或者打開鏈接失敗。
     */


    /**
     * 1.2 webSocket.onopen(用於指定鏈接成功後的回調函數)
     */
    webSocket.onopen = function () {
      console.log("webSocket open");
      document.getElementById('recv').innerHTML = "Connected";
    };

    /**
     * 1.3 webSocket.onclose(用於指定鏈接關閉以後的回調函數)
     */
    webSocket.onclose = function () {
      console.log("webSocket close");
    }

    /**
     * 1.4 webSocket.onmessage(用於指定收到服務器數據後的回調函數)
     */
    webSocket.onmessage = function (e) {
      console.log(e.data);
      document.getElementById('recv').innerHTML = e.data;
    }

    //發送信息
    document.getElementById('sendBtn').onclick = function () {
      var text = document.getElementById('sendTxt').value;
      /**
       * 2. 方法
       * 2.1 webSocket.send() (用於向服務器發送數據)
       */
      webSocket.send(text);
    }
  </script>
複製代碼

客戶端的API上面的代碼中有簡單的介紹以及使用,若是想要查看更加具體的文檔說明,能夠在MDN進行查看。

對比於服務端的實現,客戶端的使用略顯簡單,那麼接下來咱們繼續實現服務端的WebSocket。

3.2 服務端的實現

由於筆者目前侷限於JS,因此服務端的實現是使用的Node,經常使用的Node實現有如下三種:

  • µWebSockets

  • Socket.IO

  • WebSocket-Node

由於在項目中使用的是Socket.IO,因此在這裏筆者就結合本身的親身經歷去講解如下,其餘的實現方式應該也是差很少的,有興趣的話是能夠本身實現一些的。

Socket.IO想實現雙向通訊,固然WebSocket是必不可少的技術了,不過Socket.IO不只僅是WebSocket的封裝,在不支持WebSocket的環境中,Socket.IO還有多種輪詢解決方案,確保它可以正常運行。

既然用到了Socket.IO,那咱們就要扒一扒有關於它的介紹,基本使用等等內容,先在這裏貼一個官方文檔。由於官方文檔爲全英,這裏筆者找到了一個中文文檔,建議二者對比着看,有能力的固然仍是看全英的比較好,內容更加準確。

Socket.IO

Socket.Io主要由兩個部分組成:

  • socket.io模塊,集成到Node.jshttp模塊的服務器

  • socket.io-client,在瀏覽器中運行的客戶端 Socket.Io支持多種傳輸機制,例如WebSocket、Adobe Flash Sockets、XHR輪詢、JsonP輪詢,它們被隔離在統一的接口之下,這意味着任何瀏覽器均可以做爲客戶端。

標準的WebSocket服務器並不能和Socket.Io客戶端進行直接通訊,須要注意這一點。

1.1 介紹

Socket.io是一個WebSocket庫,包括了客戶端的js和服務器端的nodejs,它的目標是構建能夠在不一樣瀏覽器和移動設備上使用的實時應用。它會自動根據瀏覽器從WebSocketAJAX長輪詢、Iframe流等等各類方式中選擇最佳的方式來實現網絡實時應用,很是方便和人性化,並且支持的瀏覽器最低達IE5.5。

1.2 使用

如今Node.js的框架很是的多,譬如:Express、ThinkJS、Koa、Egg.js等,每個框架有可能進一步對Socket.io進行了封裝,好比筆者使用過的Egg.js框架就提供了 egg-socket.io插件,使用這些插件就要遵循框架的一些約束,因此對於框架中的使用,仍是須要讀者根據文檔要求使用,由於這個因素,因此讀者只在這裏介紹在不使用任何框架的狀況下的使用。

安裝

$ npm install socket.io
複製代碼

使用 Node http 服務器

先直接上代碼(最基礎),以後會根據官方文檔講解其餘內容。

//  index.html
    <script src="./node_modules/socket.io-client/dist/socket.io.js"></script>
    <script>
        let socket = io('http://localhost');
        socket.on('news', (data) => {   //監聽'news'事件,有結果後輸出
            console.log(data);
            socket.emit('my other event', {  //觸發'my other event'事件
                my: 'data'
            })
        })
    </script>
    
    
    
// app.js
    let app = require('http').createServer(handler);//使用Node建立一個Http服務
    let io = require('socket.io')(app); //此處爲綁定上面建立的服務器
    let fs = require('fs');

    app.listen(80);

    var handler = (req, res) => {
        fs.readFile(__dirname + './index.html', (err, data) => {
            if (err) {
               res.writeHead(500);
               return res.end('Error loading index.html');
            }
            res.writeHead(200);
            res.end(data);
        })
    }
    io.on('connection', (socket) => {
        socket.emit('news', {  //觸發'news'事件
            hello: 'world'
        });
        socket.on('my other event', (data) => {  //監聽'my other event'事件,有結果後輸出
            console.log(data);
    })
})
複製代碼

上面代碼完成後,運行 node app.js,以後打開 index.html,以後再打開瀏覽器的控制檯,會發現瀏覽器的控制檯上先打印出{hello: "world"},以後編輯器的控制檯上打印出{my: "data"},注意兩個是有前後順序的,這個看代碼就明白了,很少說。

1.3 emit和on

emiton 是最重要的兩個api,分別對應 發送監聽 事件。

  • socket.emit(eventName[, ...args]):發射(觸發)一個事件

  • socket.on(eventName, callback):監聽一個emit發射的事件

咱們能夠很是自由的在服務端定義併發送一個事件emit,而後在客戶端監聽 on,反過來也同樣。 發送的內容格式也很是自由,既能夠是基本數據類型 Number,String,Boolean等,也能夠是Object,Array 類型,甚至還能夠是函數。而用回調函數的方式則能夠進行更便攜的交互。

1.2 部分的示例代碼就是這兩個api的使用,這裏就很少說了。

1.4 廣播(broadcast)

broadcast默認是向全部的socket鏈接進行廣播,可是不包括髮送者自身。

注意:socket鏈接要確保是同一個命名空間下的

代碼解釋:

io.on('connection', (socket) => {
    //發送給除本身之外的其餘客戶端
    socket.broadcast.emit('news', {
        hello: 'world'
    })
})
複製代碼

此時,要想查看效果,能夠在建立一個HTML頁面,代碼同樣便可,以後在瀏覽器上同時打開兩個頁面,刷新一個頁面時(刷新一次頁面就至關於觸發一次事件),本頁面控制檯沒有輸出任何內容,另外一個頁面的控制檯則會輸出內容(能夠建立更多頁面查看效果)。

若是想要自身也能夠收到消息,此時能夠

io.on('connection', (socket) => {
    //發送給本身
    socket.emit('news', {
        hello: 'world'
    })
})
複製代碼

1.5 命名空間(namespace)

所謂的命名空間,就是指在不一樣的域當中發消息只能給當前的域的socket收到。

做用:能夠最大限度地減小資源(TCP鏈接)的數量,,併爲應用提供頻道劃分功能。(這樣多個應用模塊能夠共享單個TCP鏈接)

若是想隔離做用域,或者劃分業務模塊,這時候就可使用命名空間,命名空間至關於創建新的頻道,使你能夠在一個socket.io服務上隔離不一樣的鏈接,時間和中間件。

默認的命名空間是/,Socket.IO 客戶端默認鏈接到這個命名空間,服務端默認監聽的也是這個命名空間。

自定義命名空間

重要提示:命名空間是 Socket.IO 協議的一個實現細節, 與底層傳輸的實際 URL 無關。底層傳輸的實際 URL 默認是/socket.io/…

使用命名空間的方式一:直接在連接後面加子域名,這種方式其實仍是用同一個socket服務進程---軟隔離

服務端代碼:

io
    .of('my-nsp')
    .on('connection', (socket) => {
        // 發送給除本身之外的其餘客戶端
        socket.broadcast.emit('news', {
            hello: 'world'
        })
        //發送給本身
        socket.emit('news', {
            hello: 'world'
        })
    })
複製代碼

客戶端須要修改的代碼:

let socket = io('http://localhost:3000/my-nsp');
複製代碼

使用命名空間的方式二path參數,這種方式就是真正的從新開啓了一個socket服務。

1.6 Room

這裏說一下,namespaceroomsocket的關係

socket會屬於某一個 room,若是沒有指定,那麼會有一個默認的room。這個room又會屬於某個namespace,若是沒有指定,那麼就是默認的/。(一個命名空間下能夠有多個room)

客戶端鏈接時指定本身屬於哪個 namespace,服務端看到namespace就會把這個socket加入到指定的namespace中,若是客戶端沒有指定具體的room,則服務端會放入默認的room,或者服務端經過代碼socket.join(bar)放入barroom中。

默認狀況下,每個id便自成一個房間,房間名是 socket.id(指定命名空間以後,前面會帶上命名空間,socket會自動加入到以此ID來標識的房間);自定義房間以後,原先的默認控件仍然存在;房間爲一個對象,包含當前進入房間的sockets以及長度。

代碼示例:

io
    .on('connection', (socket) => {
        //在服務端將一個socket加入到一個房間中
        socket.join('manannan', () => {
            console.log(socket.rooms);
        });
        //進入到該房間中,以後的事件發佈僅僅在這個房間
        io.to('manannan').emit('news', {
            hello: 'world'
        })
        //離開房間
        socket.leave('mananan')
    })
複製代碼

以上內容就是基本的使用,然而在實際項目中,確定會比這些更加複雜,這裏就不一一贅述,當咱們用到咱們以前沒有用過的東西時,必定要善於查看官方文檔以及百度。

因此關於客戶端API服務端API等更多的內容,須要時查看官方文檔就能夠了。

相關文章
相關標籤/搜索