跳至主要内容
版本:4.x

自定义解析器

从 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

输出示例

  • 基本 emit
socket.emit("test", 42);

将被编码为

2["test",42]
||
|└─ JSON-encoded payload
└─ packet type (2 => EVENT)
  • 带有二进制、确认和自定义命名空间的 emit
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 编码,因此此解析器与不支持 Arraybuffers 的浏览器(如 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
});

在浏览器中,现在有一个官方捆绑包,其中包含此解析器

在这种情况下,您不需要指定 parser 选项。

优点

  • 包含二进制内容的数据包将作为单个 WebSocket 帧发送(如果 WebSocket 连接已建立)。
  • 可能会导致更小的有效负载(尤其是在使用大量数字时)。

缺点

  • 与不支持 Arraybuffers 的浏览器(如 IE9)不兼容。
  • 在浏览器的网络选项卡中更难调试。
信息

请注意,socket.io-msgpack-parser 依赖于 notepack.io MessagePack 实现。此实现主要关注性能和最小捆绑包大小,因此不支持扩展类型等功能。有关基于 官方 JavaScript 实现 的解析器,请查看 此包