自訂解析器
自 Socket.IO v2.0.0 起,現在可以提供您自己的解析器,以控制封包的封送 / 解封。
伺服器
import { Server } from "socket.io";
const io = new Server({
parser: myParser
});
用戶端
import { io } from "socket.io-client";
const socket = io({
parser: myParser
});
可用的解析器
除了 預設解析器,以下是可用的解析器清單
套件 | 說明 |
---|---|
socket.io-circular-parser | 類似於預設解析器,但處理循環參考。 |
socket.io-msgpack-parser | 使用 MessagePack 編碼封包(基於 notepack.io 套件)。 |
@skgdev/socket.io-msgpack-javascript | 使用 MessagePack 編碼封包(基於 @msgpack/msgpack 套件)。 |
socket.io-json-parser | 使用 JSON.stringify() 和 JSON.parse() 編碼封包。 |
socket.io-cbor-x-parser | 使用 cbor-x 編碼封包。 |
@socket.io/devalue-parser | 使用 devalue 編碼封包。 |
實作自己的解析器
以下是使用 JSON.stringify()
和 JSON.parse()
方法的解析器範例
import { Emitter } from "@socket.io/component-emitter"; // polyfill of Node.js EventEmitter in the browser
class Encoder {
/**
* Encode a packet into a list of strings/buffers
*/
encode(packet) {
return [JSON.stringify(packet)];
}
}
function isObject(value) {
return Object.prototype.toString.call(value) === "[object Object]";
}
class Decoder extends Emitter {
/**
* Receive a chunk (string or buffer) and optionally emit a "decoded" event with the reconstructed packet
*/
add(chunk) {
const packet = JSON.parse(chunk);
if (this.isPacketValid(packet)) {
this.emit("decoded", packet);
} else {
throw new Error("invalid format");
}
}
isPacketValid({ type, data, nsp, id }) {
const isNamespaceValid = typeof nsp === "string";
const isAckIdValid = id === undefined || Number.isInteger(id);
if (!isNamespaceValid || !isAckIdValid) {
return false;
}
switch (type) {
case 0: // CONNECT
return data === undefined || isObject(data);
case 1: // DISCONNECT
return data === undefined;
case 2: // EVENT
return Array.isArray(data) && typeof data[0] === "string";
case 3: // ACK
return Array.isArray(data);
case 4: // CONNECT_ERROR
return isObject(data);
default:
return false;
}
}
/**
* Clean up internal buffers
*/
destroy() {}
}
export const parser = { Encoder, Decoder };
預設解析器
預設解析器 (socket.io-parser
套件) 的原始碼可以在這裡找到:https://github.com/socketio/socket.io-parser
輸出範例
- 基本發射
socket.emit("test", 42);
將編碼為
2["test",42]
||
|└─ JSON-encoded payload
└─ packet type (2 => EVENT)
- 使用二進制、確認和自訂名稱空間發射
socket.emit("test", Uint8Array.from([42]), () => {
console.log("ack received");
});
將編碼為
51-/admin,13["test",{"_placeholder":true,"num":0}]
|||| || └─ JSON-encoded payload with placeholders for binary attachments
|||| |└─ acknowledgement id
|||| └─ separator
|||└─ namespace (not included when it's the main namespace)
||└─ separator
|└─ number of binary attachments
└─ packet type (5 => BINARY EVENT)
and an additional attachment (the extracted Uint8Array)
優點
- 二進制附件會經過 base64 編碼,因此這個解析器相容於不支援陣列緩衝區的瀏覽器,例如 IE9 (請參閱此處)
缺點
- 包含二進制內容的封包會以兩個不同的 WebSocket 框架傳送(如果建立了 WebSocket 連線)
msgpack 解析器
這個解析器使用 MessagePack 序列化格式。
這個解析器的原始碼可以在這裡找到:https://github.com/socketio/socket.io-msgpack-parser
範例用法
伺服器
import { Server } from "socket.io";
import customParser from "socket.io-msgpack-parser";
const io = new Server({
parser: customParser
});
用戶端 (Node.js)
import { io } from "socket.io-client";
import customParser from "socket.io-msgpack-parser";
const socket = io("https://example.com", {
parser: customParser
});
在瀏覽器中,現在有一個包含這個解析器的官方套件
- https://cdn.socket.io/4.7.5/socket.io.msgpack.min.js
- cdnjs:https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.5/socket.io.msgpack.min.js
- jsDelivr: https://cdn.jsdelivr.net/npm/socket.io-client@4.7.5/dist/socket.io.msgpack.min.js
- unpkg: https://unpkg.com/socket.io-client@4.7.5/dist/socket.io.msgpack.min.js
在這種情況下,您不需要指定 parser
選項。
優點
- 包含二進位內容的封包會作為單一 WebSocket 框架傳送(如果已建立 WebSocket 連線)
- 可能會產生較小的有效負載(特別是在使用大量數字時)
缺點
- 與不支援 Arraybuffer 的瀏覽器不相容,例如 IE9,請按此處
- 在瀏覽器的網路標籤中較難偵錯
資訊
請注意,socket.io-msgpack-parser
依賴於 notepack.io
MessagePack 實作。此實作主要著重於效能和最小套件大小,因此不支援擴充類型等功能。對於基於 官方 JavaScript 實作 的剖析器,請查看 此套件。