WebSocket硬核入門:200行代碼,教你徒手擼一個WebSocket服務器

本文原題“Node.js - 200 多行代碼實現 Websocket 協議”,爲了提升內容品質,有較大修訂。

1、引言

最近正在研究 WebSocket 相關的知識,想着如何能自己實現 WebSocket 協議。到網上搜羅了一番資料後用 Node.js 實現了一個WebSocket協議服務器,倒也沒有想象中那麼複雜,除去註釋語句和 console 語句後,大約 200 行代碼左右。

本文分享了自已開發一個WebSocket服務端實現過程中需要的知識儲備,以及具體的代碼實現含義等,非常適合想在短時間內對WebSocket協議從入門到精通的Web端即時通訊開發者閱讀。

如果你想要寫一個WebSocket 服務器,首先需要讀懂對應的網絡協議 RFC6455,不過這對於一般人來說有些 “晦澀”,英文且不說,還得咬文嚼字理解 網絡編程 含義。

好在 WebSocket 技術出現比較早,所以早就有人翻譯了完整的 RFC6455中文版,網上也有很多針對該協議的剖析文章,很多文章裏還有現成的實現代碼可以參考,所以說實現一個簡單的 WebSocket 服務並非難事。

本文更偏向實戰(in action),會從知識儲備、具體代碼分析以及注意事項角度去講解如何用 Node.js 實現一個簡單的 WebSocket 服務,至於 WebSocket 概念、定義、解釋和用途等基礎知識不會涉及,因爲這些知識在本文所列的參考文章中輕鬆找到。

友情提示:本文對應的源碼,請從文末“11、代碼下載”一節下載之。

學習交流:

- 即時通訊技術交流羣:215477170 [推薦]

- 移動端IM開發入門文章:《新手入門一篇就夠:從零開發移動端IM

- 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK

(本文同步發佈於:http://www.52im.net/thread-3175-1-1.html

2、關於作者

3、基本常識

在學習本文內容之前,我認爲很有必要簡單瞭解一下Web端即時通訊技術的“過去”和“現在”,因爲新時代的開發者(沒有經歷過短輪詢、長輪詢、Comet技術的這波人),很難理解WebSocket對於Web端的即時通訊技術來說,意味着什麼。

所謂“憶苦思甜”,瞭解了Web端即時通訊技術的過去,方知WebSocket這種技術的珍貴。。。

3.1 舊時代的Web端即時通訊技術

自從Web端即時通訊的概念提出後,“實時”性便成爲了Web開發者們津津樂道的話題。實時化的Web應用,憑藉其響應迅速、無需刷新、節省網絡流量的特性,不僅讓開發者們眼前一亮,更是爲用戶帶來絕佳的網絡體驗。

但很多開發者可能並不清楚,舊時代的Web端“實時”通信,主要基於 Ajax的拉取和Comet的推送

大家都知道Ajax,這是一種藉助瀏覽器端JavaScript實現的異步無刷新請求功能:要客戶端按需向服務器發出請求,並異步獲取來自服務器的響應,然後按照邏輯更新當前頁面的相應內容。

但是這僅僅是拉取啊,這並不是真正的“實時”:缺少服務器端的自動推送!

因此,我們不得不使用另一種略複雜的技術 Comet,只有當這兩者配合起來,這個Web應用才勉強算是個“實時”的Web端應用!

▲ Ajax和Comet技術原理(圖片引用自《Web端即時通訊技術盤點》)

3.2 WebSocket協議出現

隨着HTML5標準的出現,WebSocket技術橫空出世,隨着HTML5標準的廣泛普及,越來越多的現代瀏覽器開始全面支持WebSocket技術了。

至於WebSocket,我想大家或多或少都聽說過。

WebSocket是一種全新的協議。它將TCP的Socket(套接字)應用在了web page上,從而使通信雙方建立起一個保持在活動狀態連接通道,並且屬於全雙工(雙方同時進行雙向通信)。

事實是:WebSocket協議是借用HTTP協議的 101 switch protocol 來達到協議轉換的,從HTTP協議切換成WebSocket通信協議。

再簡單點來說,它就好像將 Ajax 和 Comet 技術的特點結合到了一起,只不過性能要高並且使用起來要方便的多(方便當然是之指在客戶端方面了)。

4、WebSocket知識儲備

如果要自己寫一個 WebSocket 服務,主要有兩個難點:

  • 1)熟練掌握 WebSocket 的協議,這個需要多讀現有的解讀類文章(下面會給出參考文章);
  • 2)操作二進制數據流,在 Node.js 中需要對 Buffer 這個類稍微熟悉些。

同時還需要具備兩個基礎知識點:

具體的做法如下,推薦先閱讀以下幾篇參考文章:

然後開始寫代碼。

在實現過程中的大部分代碼可以從下面幾篇文章中找到並借鑑(copy):

閱讀完上面的文章,你會有發現一個共同點,就是在實現 WebSockets 過程中,最最核心的部分就是 解析 或者 生成 Frame(幀)。

就是下面這結構:

▲ 截圖來自《rfc6455 - Base Framing Protocol

想要理解 frame 各個字段的含義,可參考《WebSocket詳解(三):深入WebSocket通信協議細節》,文中作者繪製了一副圖來解釋這個 frame 結構。

而在代碼層面,frame 的解析或生成可以在 RocketEngine - parser 或者 _processBuffer 中找到。

在完成上面幾個方面的知識儲備之後,而且大多有現成的代碼,所以自己邊抄邊寫一個 Websocket 服務端實現並不算太難。

對於 WebSocket 初學者,請務必閱讀以上參考文章,對 Websocket 協議有大概的瞭解之後再繼續本文剩下部分的閱讀,否則很有可能會覺得我寫得雲裏霧裏,不知所云。

5、實戰效果預覽

本次的實現代碼可以從文末“11、代碼下載”章節下載到:

(請從原文鏈接下載:http://www.52im.net/thread-3175-1-1.html)

下載後本地運行即可,執行:

node index.js

運行成功後,將會在 http://127.0.0.1:3000 創建服務。

運行服務之後,打開控制檯就能看到效果:

動圖中瀏覽器 console 所執行的 js 代碼步驟如下:

1)先建立連接:

var ws = new WebSocket("ws://127.0.0.1:3000");

ws.onmessage = function(evt) {

  console.log( "Received Message: "+ evt.data);

};

2)然後發送消息:(注意一定要在建立連接之後再執行該語句,否則發不出消息的)

ws.send('hello world');

從效果可見,我們已經實現 WebSocket 最基本的通訊功能了。

接下來我們詳細看一下具體實現的細節。

6、代碼解讀1:調用所寫的 WebSocket 類

站在使用者的角度,假設我們已經完成 WebSocket 類了,那麼應該怎麼使用?

客戶端通過 HTTP Upgrade 請求,即 101 Switching Protocol 到 HTTP 服務器,然後由服務器進行協議轉換。

在 Node.js 中我們通過 http.createServer 獲取 http.server 實例,然後監聽 upgrade 事件,在處理這個事件。

如下面的代碼所示:

// HTTP服務器部分

var server = http.createServer(function(req, res) {

  res.end('websocket test\r\n');

});

// Upgrade請求處理

server.on('upgrade', function(req, socket, upgradeHead){

  // 初始化 ws

  var ws = new WebSocket(req, socket, upgradeHead);

  // ... ws 監聽 data、error 的邏輯等

});

這裏監聽 upgrade 事件的回調函數中第二個參數 socket 是 net.Socket實例,這個類是 TCP 或 UNIX Socket 的抽象,同時一個 net.Socket 也是一個 duplex stream,所以它能被讀或寫,並且它也是一個 EventEmitter

我們就利用這個 socket 對象上進行 Websocket 類實例的初始化工作;

7、代碼解讀2:構造函數

所以不難理解 Websocket 的構造函數就是下面這個樣子:

class WebSocket extends EventEmitter {

  constructor(req, socket, upgradeHead){

    super(); // 調用 EventEmitter 構造函數

    // 1. 構造響應頭 resHeaders 部分

    // 2. 監聽 socket 的 data 事件,以及 error 事件

    // 3. 初始化成員屬性

  }

}

注意:我們需要繼承內置的 EventEmitter ,這樣生成的實例才能監聽、綁定事件。

Node.js 採用事件驅動、異步編程,天生就是爲了網絡服務而設計的,繼承 EventEmitter 就能享受到非阻塞模式的 IO 處理。

這裏特別講一下其中 響應頭的構造 和 事件監聽 部分。

7.1 返回響應頭(Response Header)

根據協議規範,我們能寫出響應頭的內容:

  • 1)將 Sec-WebSocket-Key 跟 258EAFA5-E914-47DA-95CA-C5AB0DC85B11 拼接;
  • 2)通過 SHA1 計算出摘要,並轉成 base64 字符串。

具體代碼如下:

var resKey = hashWebSocketKey(req.headers['sec-websocket-key']);

// 構造響應頭

var resHeaders = [

  'HTTP/1.1 101 Switching Protocols',

  'Upgrade: websocket',

  'Connection: Upgrade',

  'Sec-WebSocket-Accept: '+ resKey

]

  .concat('', '')

  .join('\r\n');

socket.write(resHeaders);

當執行 socket.write(resHeaders); 到後就和客戶端建立起 WebSocket 連接了,剩下去就是數據的處理。

7.2 監聽事件

socket 就是 TCP 協議的抽象,直接在上面監聽已有的 data 事件和 close 事件這兩個事件。

還有其他事件,比如 error、end 等,詳細參考 net.Socket 文檔。

socket.on('data', data => {

  this.buffer = Buffer.concat([this.buffer, data]);

  while(this._processBuffer()) {} // 循環處理返回的 data 數據

});

socket.on('close', had_error => {

  if(!this.closed) {

    this.emit('close', 1006);

    this.closed = true;

  }

});

close 的事件邏輯比較簡單,比較重要的是 data 的事件監聽部分。核心就是 this._processBuffer() 這個方法,用於處理客戶端傳送過來的數據(即 Frame 數據)。

注意:該方法是放在 while 循環語句裏,處理好邊界情況,防止死循環。

8、代碼解讀3:Frame 幀數據的處理

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

這 this._processBuffer() 部分代碼邏輯就是用來解析幀數據的,所以它是實現 WebSocket 代碼的關鍵;(該方法裏面用到了大量的位操作符以及 Buffer 類的操作)

幀數據結構詳細定義可參考 RFC6455 5.2節(英文不好的話,去下載中文翻譯版《WebSocket標準協議手冊(稀缺中文版+英文原版)》),上面羅列的參考文章都有詳細的解讀,我在這兒也不囉嗦講細節了,直接看代碼比聽我用文字講要好。

這裏就其中兩個細節需要鋪墊一下,方便更好地理解代碼。

8.1 操作碼(Opcode)

Opcode 即 操作代碼,Opcode 的值決定了應該如何解析後續的數據載荷(data payload)

根據 Opcode 我們可以大致將數據幀分成兩大類:數據幀 和 控制幀。

數據幀,目前只有 3 種,對應的 opcode 是:

  • 0x0:數據延續幀
  • 0x1:utf-8文本
  • 0x2:二進制數據;
  • 0x3 - 0x7:目前保留,用於後續定義的非控制幀。

控制幀,除了上述 3 種數據幀之外,剩下的都是控制幀:

  • 0x8:表示連接斷開
  • 0x9:表示 ping 操作
  • 0xA:表示 pong 操作
  • 0xB - 0xF:目前保留,用於後續定義的控制幀

在代碼裏,我們會先從幀數據中提取操作碼:

var opcode = byte1 & 0x0f; //截取第一個字節的後 4 位,即 opcode 碼

然後根據協議獲取到真正的數據載荷(data payload),然後將這兩部分傳給 _handleFrame 方法:

this._handleFrame(opcode, payload); // 處理操作碼

該方法會根據不同的 opcode 做出不同的操作:

_handleFrame(opcode, buffer) {

    var payload;

    switch(opcode) {

      case OPCODES.TEXT:

        payload = buffer.toString('utf8'); //如果是文本需要轉化爲utf8的編碼

        this.emit('data', opcode, payload); //Buffer.toString()默認utf8 這裏是故意指示的

        break;

      case OPCODES.BINARY: //二進制文件直接交付

        payload = buffer;

        this.emit('data', opcode, payload);

        break;

      case OPCODES.PING: // 發送 pong 做響應

        this._doSend(OPCODES.PONG, buffer);

        break;

      case OPCODES.PONG: //不做處理

        console.log('server receive pong');

        break;

      case OPCODES.CLOSE: // close有很多關閉碼

        let code, reason; // 用於獲取關閉碼和關閉原因

        if(buffer.length >= 2) {

          code = buffer.readUInt16BE(0);

          reason = buffer.toString('utf8', 2);

        }

        this.close(code, reason);

        this.emit('close', code, reason);

        break;

      default:

        this.close(1002, 'unhandle opcode:'+ opcode);

    }

  }

8.2 分片(Fragment)

本節代碼對應的標準文檔:5.4 - Fragmentation(英文不好的話,去下載中文翻譯版《WebSocket標準協議手冊(稀缺中文版+英文原版)》)。

一旦 WebSocket 客戶端、服務端建立連接後,後續的操作都是基於數據幀的傳遞。理論上來說,每個幀(Frame)的大小是沒有限制的。

對於大塊的數據,WebSocket 協議建議對數據進行分片(Fragment)操作。

分片的意義主要是兩方面:

  • 1)主要目的是允許當消息開始但不必緩衝該消息時發送一個未知大小的消息。如果消息不能被分片,那麼端點將不得不緩衝整個消息以便在首字節發生之前統計出它的長度。對於分片,服務器或中間件可以選擇一個合適大小的緩衝,當緩衝滿時,再寫一個片段到網絡;
  • 2)另一方面分片傳輸也能更高效地利用多路複用提高帶寬利用率,一個邏輯通道上的一個大消息獨佔輸出通道是不可取的,因此多路複用需要可以分割消息爲更小的分段來更好的共享輸出通道。參考文檔《I/O多路複用(multiplexing)是什麼?》。

WebSocket 協議提供的分片方法,是將原本一個大的幀拆分成數個小的幀。

下面是把一個大的Frame分片的圖示: 

由圖可知,第一個分片的 FIN 爲 0,Opcode 爲非0值(0x1 或 0x2),最後一個分片的FIN爲1,Opcode爲 0。中間分片的 FIN 和 opcode 二者均爲 0。

根據 FIN 的值來判斷,是否已經收到消息的最後一個數據幀:

  • 1)FIN=1 表示當前數據幀爲消息的最後一個數據幀,此時接收方已經收到完整的消息,可以對消息進行處理;
  • 2)FIN=0,則接收方還需要繼續監聽接收其餘的數據幀。

opcode在數據交換的場景下,表示的是數據的類型:

  • 1)0x01 表示文本,永遠是 utf8 編碼的;
  • 2)0x02 表示二進制;
  • 3)0x00 比較特殊,表示 延續幀(continuation frame),顧名思義,就是完整消息對應的數據幀還沒接收完。

代碼裏,我們需要檢測 FIN 的值,如果爲 0 說明有分片,需要記錄第一個 FIN 爲 0 時的 opcode 值,緩存到 this.frameOpcode 屬性中,將載荷緩存到 this.frames 屬性中。

如下所示:

var FIN = byte1 & 0x80; // 如果爲0x80,則標誌傳輸結束,獲取高位 bit

// 如果是 0 的話,說明是延續幀,需要保存好 opCode

if(!FIN) {

  this.frameOpcode = opcode || this.frameOpcode; // 確保不爲 0;

}

//....

// 有可能是分幀,需要拼接數據

this.frames = Buffer.concat([this.frames, payload]); // 保存到 frames 中

當接收到最後一個 FIN 幀的時候,就可以組裝後給 _handleFrame 方法:

if(FIN) {

  payload = this.frames.slice(0); // 獲取所有拼接完整的數據

  opcode = opcode || this.frameOpcode; // 如果是 0 ,則保持獲取之前保存的 code

  this.frames = Buffer.alloc(0); // 清空 frames

  this.frameOpcode = 0; // 清空 opcode

  this._handleFrame(opcode, payload); // 處理操作碼

}

8.3 發送數據幀

上面講的都是接收並解析來自客戶端的數據幀,當我們想給客戶端發送數據幀的時候,也得按協議來。

這部分操作相當於是上述 _processBuffer 方法的逆向操作,在代碼裏我們使用 encodeMessage 方法(爲了簡單起見,我們發送給客戶端的數據沒有經過掩碼處理)將發送的數據分裝成數據幀的格式,然後調用 socket.write 方法發送給客戶端。

如下所示:

_doSend(opcode, payload) {

  // 1. 考慮數據分片

  this.socket.write(

    encodeMessage(count > 0 ? OPCODES.CONTINUE : opcode, payload)

  ); //編碼後直接通過socket發送

爲了考慮分片場景,特意設置 MAX_FRAME_SIZE 來對每次發送的數據長度做截斷做分片:

  // ...

  var len = Buffer.byteLength(payload);

  // 分片的距離邏輯

  var count = 0;

  // 這裏可以針對 payload 的長度做分片

  while(len > MAX_FRAME_SIZE) {

    var framePayload = payload.slice(0, MAX_FRAME_SIZE);

    payload = payload.slice(MAX_FRAME_SIZE);

    this.socket.write(

      encodeMessage(

        count > 0 ? OPCODES.CONTINUE : opcode,

        framePayload,

        false

      )

    ); //編碼後直接通過socket發送

    count++;

    len = Buffer.byteLength(payload);

  }

// ...

至此已經實現 WebSocket 協議的關鍵部分,所組裝起來的代碼就能和客戶端建立 WebSocket 連接並進行數據交互了。

9、有關WebSocket的常見疑問

9.1 字符串 “258EAFA5-E914-47DA-95CA-C5AB0DC85B11” 怎麼來的?

這個標誌性字符串是專門標示 WebSocket 協議的 UUID;UUID 是長度爲 16-byte(128-bit)的ID,一般以形如f81d4fae-7dec-11d0-a765-00a0c91e6bf6的字符串作爲 URN(Uniform Resource Name,統一資源名稱)。

UUID 可以移步到《UUID原理》和 RFC 4122 獲取更多知識。

爲啥選擇這個字符串?

在WebSocket標準協議文檔的第七頁已經有明確的說明了:

英文不好的話,見中文翻譯版《WebSocket標準協議手冊(稀缺中文版+英文原版)

之所以選用這個 UUID ,主要該 ID 極大不太可能被其他不瞭解 WebSocket 協議的網絡終端所使用。

我也不曉得該怎麼翻譯。總之,就說這個 ID 就相當於 WebSocket 協議的 “身份證號” 了。

9.2 Websocket 和 HTTP 什麼關係?

HTTP、WebSocket 等應用層協議,都是基於 TCP 協議來傳輸數據的,我們可以把這些高級協議理解成對 TCP 的封裝。

既然大家都使用 TCP 協議,那麼大家的連接和斷開,都要遵循 TCP 協議中的三次握手和四次握手 ,只是在連接之後發送的內容不同,或者是斷開的時間不同。

對於 WebSocket 來說,它必須依賴 HTTP 協議進行一次握手 ,握手成功後,數據就直接從 TCP 通道傳輸,與 HTTP 無關了。

更詳細的解釋,可以移步:

9.3 瀏覽器中 Websocket 會自動分片麼?

答案是:看具體瀏覽器的實現。

WebSocket是一個 message based 的協議,它可以自動將數據分片,並且自動將分片的數據組裝。每個 message 可以是一個或多個分片。message 不記錄長度,分片才記錄長度。

根據協議 websocket 協議中幀長度上限爲 2^63 byte(爲 8388608 TB),可以認爲沒有限制,很明顯按協議的最大上限來傳輸數據是不靠譜的。所以在實際使用中 websocket 消息長度限制取決於具體的實現。

關於這方面,找了兩篇參考文章:

在文章《WebSocket探祕》中,作者就做了一個實驗,作者發送 27378 個字節,結果被迫分包了;如果是大數據量,就會被socket自動分包發送。

而經過我本人試驗,發現 Chrome 瀏覽器(版本 68.0.3440.106 - 64bit)會針對 131072(=2^17)bytes 大小進行自動分包。

我是通過以下測試代碼驗證:

var ws = new WebSocket("ws://127.0.0.1:3000");

ws.onmessage = function(evt) {

  console.log( "Received Message: "+ evt.data);

};

var myArray = new ArrayBuffer(131072 * 2 + 1);

ws.send(myArray);

服務端日誌:

server detect fragment, sizeof payload: 131072

server detect fragment, sizeof payload: 131072

receive data: 2 262145

客戶端日誌:

Received Message: good job

截圖如下:

而以同樣的方式去測試一些自己機器上的瀏覽器:

  • 1)Firefox(62.0,64bit);
  • 2)safari (11.1.2 - 13605.3.8);
  • 3)IE 11。

這些客戶端上的 WebSocket 幾乎沒有大小的分片(隨着數據量增大,發送會減緩,但並沒有發現分片現象)。

10、本文小結

從剛開始決定閱讀 WebSocket 協議,到自己使用 Node.js 實現一套簡單的 WebSocket 協議,到這篇文章的產出,前後耗費大約 1 個月時間(拖延症。。。)。

感謝文中所提及的參考文獻所給予的幫助,讓我實現過程中事半功倍。

之所以能夠使用較少的代碼實現 WebSocket,是因爲 Node.js 體系本身了很好的基礎,比如其所提供的 EventEmitter 類自帶事件循環,http 模塊讓你直接使用封裝好的 socket 對象,我們只要按照 WebSocket 協議實現 Frame(幀)的解析和組裝即可。

在實現一遍 WebSocket 協議後,就能較爲深刻地理解以下知識點(一切都是那麼自然而然):

  • 1)Websocket 是一種應用層協議,是爲了提供 Web 應用程序和服務端全雙工通信而專門制定的;
  • 2)WebSocket 和 HTTP 都是基於 TCP 協議實現的;
  • 3)WebSocket和 HTTP 的唯一關聯就是 HTTP 服務器需要發送一個 “Upgrade” 請求,即 101 Switching Protocol 到 HTTP 服務器,然後由服務器進行協議轉換。
  • 4)WebSocket使用 HTTP 來建立連接,但是定義了一系列新的 header 域,這些域在 HTTP 中並不會使用;
  • 5)WebSocket 可以和 HTTP Server 共享同一 port
  • 6)WebSocket 的 數據幀有序
  • ...

本文僅僅是協議的簡單實現,對於 WebSocket 的其實還有很多事情可以做(比如支持 命名空間、流式 API 等),有興趣的可以參考業界流行的 WebSocket 倉庫,去練習鍛造一個健壯的 WebSocket 工具庫輪子。

比如下面這些:

  • 1)socketio/socket.io:43.5k star,不多說,業界權威龍頭老大。(不過這實際上不是一個 WebSocket 庫,而是一個實時 pub/sub 框架。簡單地說,Socket.IO 只是包含 WebSocket 功能的一個框架,如果要使用該庫作爲 server 端的服務,則 client 也必須使用該庫,因爲它不是標準的 WebSocket 協議,而是基於 WebSocket 再包裝的消息通信協議)
  • 2)websockets/ws:9k star,強大易用的 websocket 服務端、客戶端實現,還有提供很多強大的特性
  • 3)uNetworking/uWebSockets:9.5k star,小巧高性能的 WebSocket實現,C++ 寫的,想更多瞭解 WebSocket 的底層實現,該庫是不錯的案例。
  • 4)theturtle32/WebSocket-Node:2.3k star,大部分使用 JavaScript,性能關鍵部分使用 C++ node-gyp 實現的庫。其所列的 測試用例 有挺好的參考價值。

11、代碼下載

因無法上傳源碼附件,如有需要,請從此鏈接下載:http://www.52im.net/thread-3175-1-1.html)

12、參考資料

[1]《新手入門貼:史上最全Web端即時通訊技術原理詳解

[2]《Web端即時通訊技術盤點:短輪詢、Comet、Websocket、SSE

[3]《SSE技術詳解:一種全新的HTML5服務器推送事件技術

[4]《Comet技術詳解:基於HTTP長連接的Web端實時通信技術

[5]《新手快速入門:WebSocket簡明教程

[6]《WebSocket詳解(一):初步認識WebSocket技術

[7]《WebSocket詳解(二):技術原理、代碼演示和應用案例

[8]《WebSocket詳解(三):深入WebSocket通信協議細節

[9]《WebSocket詳解(四):刨根問底HTTP與WebSocket的關係(上篇)

[10]《WebSocket詳解(五):刨根問底HTTP與WebSocket的關係(下篇)

[11]《WebSocket詳解(六):刨根問底WebSocket與Socket的關係

[12]《Web端即時通訊技術的發展與WebSocket、Socket.io的技術實踐

[13]《使用WebSocket和SSE技術實現Web端消息推送

[14]《詳解Web端通信方式的演進:從Ajax、JSONP 到 SSE、Websocket

[15]《MobileIMSDK-Web的網絡層框架爲何使用的是Socket.io而不是Netty?

[16]《理論聯繫實際:從零理解WebSocket的通信原理、協議格式、安全性

[17]《微信小程序中如何使用WebSocket實現長連接(含完整源碼)

[18]《八問WebSocket協議:爲你快速解答WebSocket熱門疑問

[19]《Web端即時通訊實踐乾貨:如何讓你的WebSocket斷網重連更快速?

[20]《WebSocket從入門到精通,半小時就夠!

(本文同步發佈於:http://www.52im.net/thread-3175-1-1.html

本文將同步發佈於“即時通訊技術圈”公衆號,歡迎關注:

▲ 本文在公衆號上的鏈接是:點此進入

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章