Skip to main content
Version: 4.x

服务器 API

服务器

¥Server

Server in the class diagram for the serverServer in the class diagram for the server

相关文档页面:

¥Related documentation pages:

构造函数

¥Constructor

new Server(httpServer[, options])

import { createServer } from "http";
import { Server } from "socket.io";

const httpServer = createServer();
const io = new Server(httpServer, {
// options
});

io.on("connection", (socket) => {
// ...
});

httpServer.listen(3000);

可用选项的完整列表可在 此处 中找到。

¥The complete list of available options can be found here.

new Server(port[, options])

import { Server } from "socket.io";

const io = new Server(3000, {
// options
});

io.on("connection", (socket) => {
// ...
});

可用选项的完整列表可在 此处 中找到。

¥The complete list of available options can be found here.

new Server(options)

import { Server } from "socket.io";

const io = new Server({
// options
});

io.on("connection", (socket) => {
// ...
});

io.listen(3000);

可用选项的完整列表可在 此处 中找到。

¥The complete list of available options can be found here.

事件

¥Events

事件:'connect'

¥Event: 'connect'

事件:"connection" 的同义词。

¥Synonym of Event: "connection".

事件:'connection'

¥Event: 'connection'

  • socket(Socket) 与客户端的套接字连接

    ¥socket (Socket) socket connection with client

在客户端连接时触发。

¥Fired upon a connection from client.

io.on("connection", (socket) => {
// ...
});

事件:'new_namespace'

¥Event: 'new_namespace'

创建新名称空间时触发:

¥Fired when a new namespace is created:

io.on("new_namespace", (namespace) => {
// ...
});

这可能很有用,例如:

¥This can be useful for example:

  • 将共享中间件附加到每个名称空间

    ¥to attach a shared middleware to each namespace

io.on("new_namespace", (namespace) => {
namespace.use(myMiddleware);
});
io.of(/\/nsp-\w+/);

io.on("new_namespace", (namespace) => {
console.log(namespace.name);
});

属性

¥Attributes

server.engine

对底层 Engine.IO 服务器的引用。参见 此处

¥A reference to the underlying Engine.IO server. See here.

server.sockets

主命名空间 (/) 的别名。

¥An alias for the main namespace (/).

io.sockets.emit("hi", "everyone");
// is equivalent to
io.of("/").emit("hi", "everyone");

方法

¥Methods

server.adapter([value])

设置适配器 value。默认为基于内存的 socket.io 附带的 Adapter 实例。参见 socket.io-adapter。如果未提供参数,则此方法返回当前值。

¥Sets the adapter value. Defaults to an instance of the Adapter that ships with socket.io which is memory based. See socket.io-adapter. If no arguments are supplied this method returns the current value.

import { Server } from "socket.io"; 
import { createAdapter } from "@socket.io/redis-adapter";
import { createClient } from "redis";

const io = new Server();

const pubClient = createClient({ host: "localhost", port: 6379 });
const subClient = pubClient.duplicate();

io.adapter(createAdapter(pubClient, subClient));

// redis@3
io.listen(3000);

// redis@4
Promise.all([pubClient.connect(), subClient.connect()]).then(() => {
io.listen(3000);
});

server.attach(httpServer[, options])

使用提供的 optionsServer 连接到 httpServer

¥Attaches the Server to an httpServer with the supplied options.

import { createServer } from "http";
import { Server } from "socket.io";

const httpServer = createServer();
const io = new Server();

io.attach(httpServer);

io.on("connection", (socket) => {
// ...
});

httpServer.listen(3000);

server.attach(port[, options])

使用提供的 optionsServer 连接到给定的 port 上。

¥Attaches the Server on the given port with the supplied options.

import { Server } from "socket.io";

const io = new Server();

io.attach(3000);

io.on("connection", (socket) => {
// ...
});

server.attachApp(app[, options])

将 Socket.IO 服务器附加到 µWebSockets.js 应用:

¥Attaches the Socket.IO server to an µWebSockets.js app:

import { App } from "uWebSockets.js";
import { Server } from "socket.io";

const app = App();
const io = new Server();

io.attachApp(app);

io.on("connection", (socket) => {
// ...
});

app.listen(3000, (token) => {
if (!token) {
console.warn("port already in use");
}
});

server.bind(engine)

仅供高级使用。将服务器绑定到特定的 engine.io Server(或兼容的 API)实例。

¥Advanced use only. Binds the server to a specific engine.io Server (or compatible API) instance.

import { Server } from "socket.io";
import { Server as Engine } from "engine.io";

const engine = new Engine();
const io = new Server();

io.bind(engine);

engine.listen(3000);

server.close([callback])

关闭 Socket.IO 服务器并断开所有客户端连接。callback 参数是可选的,将在所有连接关闭时调用。

¥Closes the Socket.IO server and disconnect all clients. The callback argument is optional and will be called when all connections are closed.

info

这也会关闭底层 HTTP 服务器。

¥This also closes the underlying HTTP server.

import { createServer } from "http";
import { Server } from "socket.io";

const PORT = 3030;
const io = new Server(PORT);

io.close();

const httpServer = createServer();

httpServer.listen(PORT); // PORT is free to use

io.attach(httpServer);
note

仅关闭底层 HTTP 服务器是不够的,因为它只会阻止服务器接受新连接,但与 WebSocket 连接的客户端不会立即断开连接。

¥Only closing the underlying HTTP server is not sufficient, as it will only prevent the server from accepting new connections but clients connected with WebSocket will not be disconnected right away.

参考:https://nodejs.cn/api/http.html#serverclosecallback

¥Reference: https://nodejs.cn/api/http.html#serverclosecallback

server.disconnectSockets([close])

v4.0.0 中添加

¥Added in v4.0.0

io.of("/").disconnectSockets(close) 的别名。

¥Alias for io.of("/").disconnectSockets(close).

// make all Socket instances disconnect
io.disconnectSockets();

// make all Socket instances in the "room1" room disconnect (and close the low-level connection)
io.in("room1").disconnectSockets(true);
tip

此方法也适用于具有 Postgres 适配器 等兼容适配器的多个 Socket.IO 服务器集群。

¥This method also works within a cluster of multiple Socket.IO servers, with a compatible adapter like the Postgres adapter.

在这种情况下,如果你只想影响给定节点上的套接字实例,则需要使用 local 标志:

¥In that case, if you only want to affect the socket instances on the given node, you need to use the local flag:

// make all Socket instances that are currently connected on the given node disconnect
io.local.disconnectSockets();

参见 此处

¥See here.

server.emit(eventName[, ...args])

History
版本变化
v4.5.0io.emit() 现在支持确认。
v1.0.0初步实现。

向主命名空间中所有连接的客户端触发事件。

¥Emits an event to all connected clients in the main namespace.

io.emit("hello");

可以包含任意数量的参数,并且支持所有可序列化的数据结构:

¥Any number of parameters can be included, and all serializable data structures are supported:

io.emit("hello", 1, "2", { "3": 4 }, Buffer.from([5]));

在接收端:

¥And on the receiving side:

socket.on("hello", (arg1, arg2, arg3, arg4) => {
console.log(arg1); // 1
console.log(arg2); // "2"
console.log(arg3); // { "3": 4 }
console.log(arg4); // ArrayBuffer or Buffer, depending on the platform
});
info

参数将自动序列化,因此不需要调用 JSON.stringify()

¥The arguments will automatically be serialized, so calling JSON.stringify() is not needed.

你可以使用 to()except() 将数据包发送到特定客户端:

¥You can use to() and except() to send the packet to specific clients:

// the “hello” event will be broadcast to all connected clients that are either
// in the "room1" room or in the "room2" room, excluding those in the "room3" room
io.to("room1").to("room2").except("room3").emit("hello");

从版本 4.5.0 开始,现在可以在广播时使用确认:

¥Starting with version 4.5.0, it is now possible to use acknowledgements when broadcasting:

io.timeout(10000).emit("some-event", (err, responses) => {
if (err) {
// some clients did not acknowledge the event in the given delay
} else {
console.log(responses); // one response per client
}
});
caution

在这种情况下,必须调用 timeout()

¥Calling timeout() is mandatory in that case.

server.emitWithAck(eventName[, ...args])

v4.6.0 中添加

¥Added in v4.6.0

基于 promise 的广播版本并期望所有目标客户端的确认:

¥Promised-based version of broadcasting and expecting an acknowledgement from all targeted clients:

try {
const responses = await io.timeout(10000).emitWithAck("some-event");
console.log(responses); // one response per client
} catch (e) {
// some clients did not acknowledge the event in the given delay
}

上面的例子相当于:

¥The example above is equivalent to:

io.timeout(10000).emit("some-event", (err, responses) => {
if (err) {
// some clients did not acknowledge the event in the given delay
} else {
console.log(responses); // one response per client
}
});

在接收端:

¥And on the receiving side:

socket.on("some-event", (callback) => {
callback("got it"); // only one argument is expected
});

server.except(rooms)

v4.0.0 中添加

¥Added in v4.0.0

为后续事件触发设置修饰符,该事件将仅广播给尚未加入给定 rooms 的客户端。

¥Sets a modifier for a subsequent event emission that the event will only be broadcast to clients that have not joined the given rooms.

// the "foo" event will be broadcast to all connected clients, except the ones that are in the "room-101" room
io.except("room-101").emit("foo", "bar");

// with an array of rooms
io.except(["room-101", "room-102"]).emit("foo", "bar");

// with multiple chained calls
io.except("room-101").except("room-102").emit("foo", "bar");

server.fetchSockets()

v4.0.0 中添加

¥Added in v4.0.0

io.of("/").fetchSocket() 的别名。

¥Alias for io.of("/").fetchSocket().

// return all Socket instances of the main namespace
const sockets = await io.fetchSockets();

// return all Socket instances in the "room1" room of the main namespace
const sockets = await io.in("room1").fetchSockets();

使用示例:

¥Sample usage:

io.on("connection", (socket) => {
const userId = computeUserId(socket);

socket.join(userId);

socket.on("disconnect", async () => {
const sockets = await io.in(userId).fetchSockets();
if (sockets.length === 0) {
// no more active connections for the given user
}
});
});
tip

此方法也适用于具有 Postgres 适配器 等兼容适配器的多个 Socket.IO 服务器集群。

¥This method also works within a cluster of multiple Socket.IO servers, with a compatible adapter like the Postgres adapter.

在这种情况下,如果你只想返回给定节点上的套接字实例,则需要使用 local 标志:

¥In that case, if you only want to return the socket instances on the given node, you need to use the local flag:

// return all Socket instances that are currently connected on the given node
const sockets = await io.local.fetchSockets();

参见 此处

¥See here.

server.in(room)

v1.0.0 中添加

¥Added in v1.0.0

server.to(room) 的同义词,但在某些情况下可能感觉更清晰:

¥Synonym of server.to(room), but might feel clearer in some cases:

// disconnect all clients in the "room-101" room
io.in("room-101").disconnectSockets();

server.listen(httpServer[, options])

server.attach(httpServer[, options]) 的同义词。

¥Synonym of server.attach(httpServer[, options]).

server.listen(port[, options])

server.attach(port[, options]) 的同义词。

¥Synonym of server.attach(port[, options]).

server.of(nsp)

通过路径名标识符 nsp 初始化并检索给定的 Namespace。如果命名空间已经初始化,它会立即返回它。

¥Initializes and retrieves the given Namespace by its pathname identifier nsp. If the namespace was already initialized it returns it immediately.

const adminNamespace = io.of("/admin");

还可以提供正则表达式或函数,以便以动态方式创建命名空间:

¥A regex or a function can also be provided, in order to create namespace in a dynamic way:

const dynamicNsp = io.of(/^\/dynamic-\d+$/).on("connection", (socket) => {
const newNamespace = socket.nsp; // newNamespace.name === "/dynamic-101"

// broadcast to all clients in the given sub-namespace
newNamespace.emit("hello");
});

// client-side
const socket = io("/dynamic-101");

// broadcast to all clients in each sub-namespace
dynamicNsp.emit("hello");

// use a middleware for each sub-namespace
dynamicNsp.use((socket, next) => { /* ... */ });

有一个功能:

¥With a function:

io.of((name, query, next) => {
// the checkToken method must return a boolean, indicating whether the client is able to connect or not.
next(null, checkToken(query.token));
}).on("connection", (socket) => { /* ... */ });

server.on(eventName, listener)

继承自 EventEmitter 类

¥Inherited from the EventEmitter class.

listener 函数添加到名为 eventName 的事件的监听器数组的末尾。

¥Adds the listener function to the end of the listeners array for the event named eventName.

可用事件:

¥Available events:

io.on("connection", (socket) => {
// ...
});

server.onconnection(socket)

仅供高级使用。从传入的 engine.io(或兼容的 API)Socket 创建一个新的 socket.io 客户端。

¥Advanced use only. Creates a new socket.io client from the incoming engine.io (or compatible API) Socket.

import { Server } from "socket.io";
import { Server as Engine } from "engine.io";

const engine = new Engine();
const io = new Server();

engine.on("connection", (socket) => {
io.onconnection(socket);
});

engine.listen(3000);

server.path([value])

设置路径 value,在该路径下提供 engine.io 和静态文件。默认为 /socket.io/。如果未提供参数,则此方法返回当前值。

¥Sets the path value under which engine.io and the static files will be served. Defaults to /socket.io/. If no arguments are supplied this method returns the current value.

import { Server } from "socket.io";

const io = new Server();

io.path("/myownpath/");
danger

path 值必须与客户端的值匹配:

¥The path value must match the one on the client side:

import { io } from "socket.io-client";

const socket = io({
path: "/myownpath/"
});

server.serveClient([value])

如果 valuetrue,则连接的服务器将为客户端文件提供服务。默认为 true。该方法在调用 listen 后无效。如果未提供参数,则此方法返回当前值。

¥If value is true the attached server will serve the client files. Defaults to true. This method has no effect after listen is called. If no arguments are supplied this method returns the current value.

import { Server } from "socket.io";

const io = new Server();

io.serveClient(false);

io.listen(3000);

server.serverSideEmit(eventName[, ...args][, ack])

v4.1.0 中添加

¥Added in v4.1.0

别名为:io.of("/").serverSideEmit(/* ... */);

¥Alias for: io.of("/").serverSideEmit(/* ... */);

cluster 的其他 Socket.IO 服务器发送消息。

¥Sends a message to the other Socket.IO servers of the cluster.

语法:

¥Syntax:

io.serverSideEmit("hello", "world");

在接收端:

¥And on the receiving side:

io.on("hello", (arg1) => {
console.log(arg1); // prints "world"
});

也支持回执:

¥Acknowledgements are supported too:

// server A
io.serverSideEmit("ping", (err, responses) => {
console.log(responses[0]); // prints "pong"
});

// server B
io.on("ping", (cb) => {
cb("pong");
});

注意:

¥Notes:

  • connectionconnectnew_namespace 字符串是保留的,不能在你的应用中使用。

    ¥the connection, connect and new_namespace strings are reserved and cannot be used in your application.

  • 你可以发送任意数量的参数,但当前不支持二进制结构(参数数组将是 JSON.stringify-ed)

    ¥you can send any number of arguments, but binary structures are currently not supported (the array of arguments will be JSON.stringify-ed)

示例:

¥Example:

io.serverSideEmit("hello", "world", 1, "2", { 3: "4" });
  • 如果其他 Socket.IO 服务器在给定的延迟后没有响应,则调用确认回调可能会出现错误

    ¥the acknowledgement callback might be called with an error, if the other Socket.IO servers do not respond after a given delay

io.serverSideEmit("ping", (err, responses) => {
if (err) {
// at least one Socket.IO server has not responded
// the 'responses' array contains all the responses already received though
} else {
// success! the 'responses' array contains one object per other Socket.IO server in the cluster
}
});

server.serverSideEmitWithAck(eventName[, ...args])

v4.6.0 中添加

¥Added in v4.6.0

别名为:io.of("/").serverSideEmitWithAck(/* ... */);

¥Alias for: io.of("/").serverSideEmitWithAck(/* ... */);

基于 promise 的广播版本并期望来自 cluster 的其他 Socket.IO 服务器的确认。

¥Promised-based version of broadcasting and expecting an acknowledgement from the other Socket.IO servers of the cluster.

try {
const responses = await io.serverSideEmitWithAck("some-event");
console.log(responses); // one response per server (except itself)
} catch (e) {
// some servers did not acknowledge the event in the given delay
}

上面的例子相当于:

¥The example above is equivalent to:

io.serverSideEmit("some-event", (err, responses) => {
if (err) {
// some servers did not acknowledge the event in the given delay
} else {
console.log(responses); // one response per server (except itself)
}
});

在接收端:

¥And on the receiving side:

io.on("some-event", (callback) => {
callback("got it"); // only one argument is expected
});

server.socketsJoin(rooms)

v4.0.0 中添加

¥Added in v4.0.0

io.of("/").socketsJoin(rooms) 的别名。

¥Alias for io.of("/").socketsJoin(rooms).

// make all Socket instances join the "room1" room
io.socketsJoin("room1");

// make all Socket instances in the "room1" room join the "room2" and "room3" rooms
io.in("room1").socketsJoin(["room2", "room3"]);

// this also works with a single socket ID
io.in(theSocketId).socketsJoin("room1");
tip

此方法也适用于具有 Postgres 适配器 等兼容适配器的多个 Socket.IO 服务器集群。

¥This method also works within a cluster of multiple Socket.IO servers, with a compatible adapter like the Postgres adapter.

在这种情况下,如果你只想影响给定节点上的套接字实例,则需要使用 local 标志:

¥In that case, if you only want to affect the socket instances on the given node, you need to use the local flag:

// make all Socket instances that are currently connected on the given node join the "room1" room
io.local.socketsJoin("room1");

参见 此处

¥See here.

server.socketsLeave(rooms)

v4.0.0 中添加

¥Added in v4.0.0

io.of("/").socketsLeave(rooms) 的别名。

¥Alias for io.of("/").socketsLeave(rooms).

// make all Socket instances leave the "room1" room
io.socketsLeave("room1");

// make all Socket instances in the "room1" room leave the "room2" and "room3" rooms
io.in("room1").socketsLeave(["room2", "room3"]);

// this also works with a single socket ID
io.in(theSocketId).socketsLeave("room1");
tip

此方法也适用于具有 Postgres 适配器 等兼容适配器的多个 Socket.IO 服务器集群。

¥This method also works within a cluster of multiple Socket.IO servers, with a compatible adapter like the Postgres adapter.

在这种情况下,如果你只想影响给定节点上的套接字实例,则需要使用 local 标志:

¥In that case, if you only want to affect the socket instances on the given node, you need to use the local flag:

// make all Socket instances that are currently connected on the given node leave the "room1" room
io.local.socketsLeave("room1");

参见 此处

¥See here.

server.timeout(value)

v4.5.0 中添加

¥Added in v4.5.0

  • value <number>

  • 返回 BroadcastOperator

    ¥Returns BroadcastOperator

为后续事件触发设置一个修饰符,当给定的毫秒数过去而没有得到所有目标客户端的确认时,将调用回调并出现错误:

¥Sets a modifier for a subsequent event emission that the callback will be called with an error when the given number of milliseconds have elapsed without an acknowledgement from all targeted clients:

io.timeout(10000).emit("some-event", (err, responses) => {
if (err) {
// some clients did not acknowledge the event in the given delay
} else {
console.log(responses); // one response per client
}
});

server.to(room)

History
版本变化
v4.0.0允许通过一系列房间。
v1.0.0初步实现。
  • room <string> | <string[]>

  • 返回 BroadcastOperator 进行链接

    ¥Returns BroadcastOperator for chaining

为后续事件触发设置修饰符,该事件将仅广播到已加入给定 room 的客户端。

¥Sets a modifier for a subsequent event emission that the event will only be broadcast to clients that have joined the given room.

要发送到多个房间,你可以多次调用 to

¥To emit to multiple rooms, you can call to several times.

// the “foo” event will be broadcast to all connected clients in the “room-101” room
io.to("room-101").emit("foo", "bar");

// with an array of rooms (a client will be notified at most once)
io.to(["room-101", "room-102"]).emit("foo", "bar");

// with multiple chained calls
io.to("room-101").to("room-102").emit("foo", "bar");

server.use(fn)

v1.0.0 中添加

¥Added in v1.0.0

io.of("/").use(fn) 的别名。

¥Alias for io.of("/").use(fn).

为主命名空间注册一个中间件,该中间件是为每个传入的 Socket 执行的函数,并接收套接字作为参数和一个函数,可以选择将执行推迟到下一个注册的中间件。

¥Registers a middleware for the main namespace, which is a function that gets executed for every incoming Socket, and receives as parameters the socket and a function to optionally defer execution to the next registered middleware.

传递给中间件回调的错误将作为特殊的 connect_error 数据包发送给客户端。

¥Errors passed to middleware callbacks are sent as special connect_error packets to clients.

服务器

¥Server

io.use((socket, next) => {
const err = new Error("not authorized");
err.data = { content: "Please retry later" }; // additional details
next(err);
});

客户端

¥Client

socket.on("connect_error", err => {
console.log(err instanceof Error); // true
console.log(err.message); // not authorized
console.log(err.data); // { content: "Please retry later" }
});

更多信息可参见 此处

¥More information can be found here.

info

如果你正在寻找 Express 中间件,请检查 本节

¥If you are looking for Express middlewares, please check this section.

命名空间

¥Namespace

Namespace in the class diagram for the serverNamespace in the class diagram for the server

表示在由路径名(例如:/chat)标识的给定范围内连接的套接字池。

¥Represents a pool of sockets connected under a given scope identified by a pathname (eg: /chat).

更多信息可参见 此处

¥More information can be found here.

属性

¥Attributes

namespace.adapter

用于命名空间的 "适配器"

¥The "Adapter" used for the namespace.

注意:主命名空间的适配器可以通过 io.of("/").adapter 访问。

¥Note: the adapter of the main namespace can be accessed with io.of("/").adapter.

有关它的更多信息 此处

¥More information about it here.

const adapter = io.of("/my-namespace").adapter;

namespace.name

命名空间标识符属性。

¥The namespace identifier property.

namespace.sockets

连接到此命名空间的 套接字 实例的映射。

¥A map of Socket instances that are connected to this namespace.

// number of sockets in this namespace (on this node)
const socketCount = io.of("/admin").sockets.size;

事件

¥Events

事件:'connect'

¥Event: 'connect'

事件:"connection" 的同义词。

¥Synonym of Event: "connection".

事件:'connection'

¥Event: 'connection'

在客户端连接时触发。

¥Fired upon a connection from client.

// main namespace
io.on("connection", (socket) => {
// ...
});

// custom namespace
io.of("/admin").on("connection", (socket) => {
// ...
});

方法

¥Methods

namespace.allSockets()

  • 返回 Promise<Set<SocketId>>

    ¥Returns Promise<Set<SocketId>>

caution

该方法将在下一个主要版本中删除,请使用 serverSideEmit()fetchSockets() 代替。

¥This method will be removed in the next major release, please use serverSideEmit() or fetchSockets() instead.

获取连接到此命名空间的套接字 ID 列表(如果适用,跨所有节点)。

¥Gets a list of socket IDs connected to this namespace (across all nodes if applicable).

// all sockets in the main namespace
const ids = await io.allSockets();

// all sockets in the main namespace and in the "user:1234" room
const ids = await io.in("user:1234").allSockets();

// all sockets in the "chat" namespace
const ids = await io.of("/chat").allSockets();

// all sockets in the "chat" namespace and in the "general" room
const ids = await io.of("/chat").in("general").allSockets();

namespace.disconnectSockets([close])

v4.0.0 中添加

¥Added in v4.0.0

  • close <boolean> 是否关闭底层连接

    ¥close <boolean> whether to close the underlying connection

  • 返回 void

    ¥Returns void

使匹配的 Socket 实例断开连接。

¥Makes the matching Socket instances disconnect.

// make all Socket instances disconnect
io.disconnectSockets();

// make all Socket instances in the "room1" room disconnect (and discard the low-level connection)
io.in("room1").disconnectSockets(true);

// make all Socket instances in the "room1" room of the "admin" namespace disconnect
io.of("/admin").in("room1").disconnectSockets();

// this also works with a single socket ID
io.of("/admin").in(theSocketId).disconnectSockets();

命名空间.emit(eventName[, ...args])

¥namespace.emit(eventName[, ...args])

History
版本变化
v4.5.0io.emit() 现在支持确认。
v1.0.0初步实现。

向给定命名空间中所有连接的客户端触发事件。

¥Emits an event to all connected clients in the given namespace.

io.of("/chat").emit("hello");

可以包含任意数量的参数,并且支持所有可序列化的数据结构:

¥Any number of parameters can be included, and all serializable data structures are supported:

io.of("/chat").emit("hello", 1, "2", { "3": 4 }, Buffer.from([5]));

在接收端:

¥And on the receiving side:

socket.on("hello", (arg1, arg2, arg3, arg4) => {
console.log(arg1); // 1
console.log(arg2); // "2"
console.log(arg3); // { "3": 4 }
console.log(arg4); // ArrayBuffer or Buffer, depending on the platform
});
info

参数将自动序列化,因此不需要调用 JSON.stringify()

¥The arguments will automatically be serialized, so calling JSON.stringify() is not needed.

你可以使用 to()except() 将数据包发送到特定客户端:

¥You can use to() and except() to send the packet to specific clients:

// the “hello” event will be broadcast to all connected clients that are either
// in the "room1" room or in the "room2" room, excluding those in the "room3" room
io.of("/chat").to("room1").to("room2").except("room3").emit("hello");

从版本 4.5.0 开始,现在可以在广播时使用确认:

¥Starting with version 4.5.0, it is now possible to use acknowledgements when broadcasting:

io.of("/chat").timeout(10000).emit("some-event", (err, responses) => {
if (err) {
// some clients did not acknowledge the event in the given delay
} else {
console.log(responses); // one response per client
}
});
caution

在这种情况下,必须调用 timeout()

¥Calling timeout() is mandatory in that case.

命名空间.emitWithAck(eventName[, ...args])

¥namespace.emitWithAck(eventName[, ...args])

v4.6.0 中添加

¥Added in v4.6.0

基于 promise 的广播版本并期望给定命名空间中的所有目标客户端确认:

¥Promised-based version of broadcasting and expecting an acknowledgement from all targeted clients in the given namespace:

try {
const responses = await io.of("/chat").timeout(10000).emitWithAck("some-event");
console.log(responses); // one response per client
} catch (e) {
// some clients did not acknowledge the event in the given delay
}

上面的例子相当于:

¥The example above is equivalent to:

io.of("/chat").timeout(10000).emit("some-event", (err, responses) => {
if (err) {
// some clients did not acknowledge the event in the given delay
} else {
console.log(responses); // one response per client
}
});

在接收端:

¥And on the receiving side:

socket.on("some-event", (callback) => {
callback("got it"); // only one argument is expected
});

namespace.except(rooms)

v4.0.0 中添加

¥Added in v4.0.0

为后续事件触发设置修饰符,该事件将仅广播给尚未加入给定 rooms 的客户端。

¥Sets a modifier for a subsequent event emission that the event will only be broadcast to clients that have not joined the given rooms.

const myNamespace = io.of("/my-namespace");

// the "foo" event will be broadcast to all connected clients, except the ones that are in the "room-101" room
myNamespace.except("room-101").emit("foo", "bar");

// with an array of rooms
myNamespace.except(["room-101", "room-102"]).emit("foo", "bar");

// with multiple chained calls
myNamespace.except("room-101").except("room-102").emit("foo", "bar");

namespace.fetchSockets()

v4.0.0 中添加

¥Added in v4.0.0

返回匹配的 Socket 实例:

¥Returns the matching Socket instances:

// return all Socket instances in the main namespace
const sockets = await io.fetchSockets();

// return all Socket instances in the "room1" room of the main namespace
const sockets = await io.in("room1").fetchSockets();

// return all Socket instances in the "room1" room of the "admin" namespace
const sockets = await io.of("/admin").in("room1").fetchSockets();

// this also works with a single socket ID
const sockets = await io.in(theSocketId).fetchSockets();

上例中的 sockets 变量是一个对象数组,公开了常用 Socket 类的子集:

¥The sockets variable in the example above is an array of objects exposing a subset of the usual Socket class:

for (const socket of sockets) {
console.log(socket.id);
console.log(socket.handshake);
console.log(socket.rooms);
console.log(socket.data);
socket.emit(/* ... */);
socket.join(/* ... */);
socket.leave(/* ... */);
socket.disconnect(/* ... */);
}

data 属性是一个任意对象,可用于在 Socket.IO 服务器之间共享信息:

¥The data attribute is an arbitrary object that can be used to share information between Socket.IO servers:

// server A
io.on("connection", (socket) => {
socket.data.username = "alice";
});

// server B
const sockets = await io.fetchSockets();
console.log(sockets[0].data.username); // "alice"

重要的提示:此方法(以及 socketsJoinsocketsLeavedisconnectSockets)与 Redis 适配器(从 socket.io-redis@6.1.0 开始)兼容,这意味着它们将跨 Socket.IO 服务器工作。

¥Important note: this method (and socketsJoin, socketsLeave and disconnectSockets too) is compatible with the Redis adapter (starting with socket.io-redis@6.1.0), which means that they will work across Socket.IO servers.

namespace.in(room)

v1.0.0 中添加

¥Added in v1.0.0

namespace.to(room) 的同义词,但在某些情况下可能感觉更清晰:

¥Synonym of namespace.to(room), but might feel clearer in some cases:

const myNamespace = io.of("/my-namespace");

// disconnect all clients in the "room-101" room
myNamespace.in("room-101").disconnectSockets();

namespace.serverSideEmit(eventName[, ...args][, ack])

v4.1.0 中添加

¥Added in v4.1.0

cluster 的其他 Socket.IO 服务器发送消息。

¥Sends a message to the other Socket.IO servers of the cluster.

语法:

¥Syntax:

io.of("/chat").serverSideEmit("hello", "world");

在接收端:

¥And on the receiving side:

io.of("/chat").on("hello", (arg1) => {
console.log(arg1); // prints "world"
});

也支持回执:

¥Acknowledgements are supported too:

// server A
io.of("/chat").serverSideEmit("ping", (err, responses) => {
console.log(responses[0]); // prints "pong"
});

// server B
io.of("/chat").on("ping", (cb) => {
cb("pong");
});

注意:

¥Notes:

  • connectionconnectnew_namespace 字符串是保留的,不能在你的应用中使用。

    ¥the connection, connect and new_namespace strings are reserved and cannot be used in your application.

  • 你可以发送任意数量的参数,但当前不支持二进制结构(参数数组将是 JSON.stringify-ed)

    ¥you can send any number of arguments, but binary structures are currently not supported (the array of arguments will be JSON.stringify-ed)

示例:

¥Example:

io.of("/chat").serverSideEmit("hello", "world", 1, "2", { 3: "4" });
  • 如果其他 Socket.IO 服务器在给定的延迟后没有响应,则调用确认回调可能会出现错误

    ¥the acknowledgement callback might be called with an error, if the other Socket.IO servers do not respond after a given delay

io.of("/chat").serverSideEmit("ping", (err, responses) => {
if (err) {
// at least one Socket.IO server has not responded
// the 'responses' array contains all the responses already received though
} else {
// success! the 'responses' array contains one object per other Socket.IO server in the cluster
}
});

命名空间.serverSideEmitWithAck(eventName[, ...args])

¥namespace.serverSideEmitWithAck(eventName[, ...args])

v4.6.0 中添加

¥Added in v4.6.0

基于 promise 的广播版本并期望来自 cluster 的其他 Socket.IO 服务器的确认。

¥Promised-based version of broadcasting and expecting an acknowledgement from the other Socket.IO servers of the cluster.

try {
const responses = await io.of("/chat").serverSideEmitWithAck("some-event");
console.log(responses); // one response per server (except itself)
} catch (e) {
// some servers did not acknowledge the event in the given delay
}

上面的例子相当于:

¥The example above is equivalent to:

io.of("/chat").serverSideEmit("some-event", (err, responses) => {
if (err) {
// some servers did not acknowledge the event in the given delay
} else {
console.log(responses); // one response per server (except itself)
}
});

在接收端:

¥And on the receiving side:

io.of("/chat").on("some-event", (callback) => {
callback("got it"); // only one argument is expected
});

namespace.socketsJoin(rooms)

v4.0.0 中添加

¥Added in v4.0.0

使匹配的 Socket 实例加入指定的房间:

¥Makes the matching Socket instances join the specified rooms:

// make all Socket instances join the "room1" room
io.socketsJoin("room1");

// make all Socket instances in the "room1" room join the "room2" and "room3" rooms
io.in("room1").socketsJoin(["room2", "room3"]);

// make all Socket instances in the "room1" room of the "admin" namespace join the "room2" room
io.of("/admin").in("room1").socketsJoin("room2");

// this also works with a single socket ID
io.in(theSocketId).socketsJoin("room1");

更多信息可参见 此处

¥More information can be found here.

namespace.socketsLeave(rooms)

v4.0.0 中添加

¥Added in v4.0.0

使匹配的 Socket 实例离开指定的房间:

¥Makes the matching Socket instances leave the specified rooms:

// make all Socket instances leave the "room1" room
io.socketsLeave("room1");

// make all Socket instances in the "room1" room leave the "room2" and "room3" rooms
io.in("room1").socketsLeave(["room2", "room3"]);

// make all Socket instances in the "room1" room of the "admin" namespace leave the "room2" room
io.of("/admin").in("room1").socketsLeave("room2");

// this also works with a single socket ID
io.in(theSocketId).socketsLeave("room1");

namespace.timeout(value)

v4.5.0 中添加

¥Added in v4.5.0

  • value <number>

  • 返回 BroadcastOperator

    ¥Returns BroadcastOperator

为后续事件触发设置一个修饰符,当给定的毫秒数过去而没有得到客户端的确认时,回调将被调用并出现错误:

¥Sets a modifier for a subsequent event emission that the callback will be called with an error when the given number of milliseconds have elapsed without an acknowledgement from the client:

io.of("/chat").timeout(10000).emit("some-event", (err, responses) => {
if (err) {
// some clients did not acknowledge the event in the given delay
} else {
console.log(responses); // one response per client
}
});

namespace.to(room)

History
版本变化
v4.0.0允许通过一系列房间。
v1.0.0初步实现。
  • room <string> | <string[]>

  • 返回 BroadcastOperator 进行链接

    ¥Returns BroadcastOperator for chaining

为后续事件触发设置修饰符,该事件将仅广播到已加入给定 room 的客户端。

¥Sets a modifier for a subsequent event emission that the event will only be broadcast to clients that have joined the given room.

要发送到多个房间,你可以多次调用 to

¥To emit to multiple rooms, you can call to several times.

const myNamespace = io.of("/my-namespace");

// the “foo” event will be broadcast to all connected clients in the “room-101” room
myNamespace.to("room-101").emit("foo", "bar");

// with an array of rooms (a client will be notified at most once)
myNamespace.to(["room-101", "room-102"]).emit("foo", "bar");

// with multiple chained calls
myNamespace.to("room-101").to("room-102").emit("foo", "bar");

namespace.use(fn)

v1.0.0 中添加

¥Added in v1.0.0

为给定的命名空间注册一个中间件,该中间件是为每个传入的 Socket 执行的函数,并接收套接字和函数作为参数,以选择性地将执行推迟到下一个注册的中间件。

¥Registers a middleware for the given namespace, which is a function that gets executed for every incoming Socket, and receives as parameters the socket and a function to optionally defer execution to the next registered middleware.

传递给中间件回调的错误将作为特殊的 connect_error 数据包发送给客户端。

¥Errors passed to middleware callbacks are sent as special connect_error packets to clients.

服务器

¥Server

io.of("/chat").use((socket, next) => {
const err = new Error("not authorized");
err.data = { content: "Please retry later" }; // additional details
next(err);
});

客户端

¥Client

socket.on("connect_error", err => {
console.log(err instanceof Error); // true
console.log(err.message); // not authorized
console.log(err.data); // { content: "Please retry later" }
});

更多信息可参见 此处

¥More information can be found here.

info

如果你正在寻找 Express 中间件,请检查 本节

¥If you are looking for Express middlewares, please check this section.

标志

¥Flags

标志:'local'

¥Flag: 'local'

设置后续事件触发的修饰符,即事件数据将仅广播到当前节点(当 扩展到多个节点 时)。

¥Sets a modifier for a subsequent event emission that the event data will only be broadcast to the current node (when scaling to multiple nodes).

io.local.emit("an event", { some: "data" });

标志:'volatile'

¥Flag: 'volatile'

为后续事件触发设置一个修饰符,如果客户端未准备好接收消息(由于网络缓慢或其他问题,或者因为它们通过长轮询连接并且处于请求过程中,则事件数据可能会丢失) -响应周期)。

¥Sets a modifier for a subsequent event emission that the event data may be lost if the clients are not ready to receive messages (because of network slowness or other issues, or because they’re connected through long polling and is in the middle of a request-response cycle).

io.volatile.emit("an event", { some: "data" }); // the clients may or may not receive it

套接字

¥Socket

Socket in the class diagram for the serverSocket in the class diagram for the server

Socket 是与浏览器客户端交互的基础类。Socket 属于某个 Namespace(默认为 /),并使用底层 Client 进行通信。

¥A Socket is the fundamental class for interacting with browser clients. A Socket belongs to a certain Namespace (by default /) and uses an underlying Client to communicate.

应该注意的是,Socket 并不直接与实际的底层 TCP/IP socket 相关,它只是类的名称。

¥It should be noted the Socket doesn't relate directly to the actual underlying TCP/IP socket and it is only the name of the class.

在每个 Namespace 中,你还可以定义 Socket 可以加入和离开的任意通道(称为 room)。这提供了一种向 Socket 组广播的便捷方式(参见下面的 Socket#to)。

¥Within each Namespace, you can also define arbitrary channels (called room) that the Socket can join and leave. That provides a convenient way to broadcast to a group of Sockets (see Socket#to below).

Socket 类继承自 EventEmitterSocket 类重写 emit 方法,并且不修改任何其他 EventEmitter 方法。此处记录的所有方法也显示为 EventEmitter 方法(emit 除外),均由 EventEmitter 实现,并且适用 EventEmitter 的文档。

¥The Socket class inherits from EventEmitter. The Socket class overrides the emit method, and does not modify any other EventEmitter method. All methods documented here which also appear as EventEmitter methods (apart from emit) are implemented by EventEmitter, and documentation for EventEmitter applies.

更多信息可参见 此处

¥More information can be found here.

事件

¥Events

事件:'disconnect'

¥Event: 'disconnect'

  • reason <string> 断开原因(客户端或服务器端)

    ¥reason <string> the reason of the disconnection (either client or server-side)

断开连接时触发。

¥Fired upon disconnection.

io.on("connection", (socket) => {
socket.on("disconnect", (reason) => {
// ...
});
});

可能的原因:

¥Possible reasons:

原因描述
server namespace disconnect套接字与 socket.disconnect() 被强制断开。
client namespace disconnect客户端已使用 socket.disconnect() 手动断开套接字。
server shutting down服务器已经关闭了。
ping timeout客户端在 pingTimeout 延迟内没有发送 PONG 数据包。
transport close连接已关闭(例如:用户失去连接,或网络从 WiFi 更改为 4G)。
transport error连接遇到错误。
parse error服务器收到来自客户端的无效数据包。
forced close服务器收到来自客户端的无效数据包。
forced server close客户端没有及时加入命名空间(参见 connectTimeout 选项),被强制关闭。

事件:'disconnecting'

¥Event: 'disconnecting'

v1.5.0 中添加

¥Added in v1.5.0

  • reason <string> 断开原因(客户端或服务器端)

    ¥reason <string> the reason of the disconnection (either client or server-side)

当客户端将要断开连接(但尚未离开其 rooms)时触发。

¥Fired when the client is going to be disconnected (but hasn't left its rooms yet).

io.on("connection", (socket) => {
socket.on("disconnecting", (reason) => {
console.log(socket.rooms); // Set { ... }
});
});

对于异步处理程序,你需要创建 rooms 属性的副本:

¥With an asynchronous handler, you will need to create a copy of the rooms attribute:

io.on("connection", (socket) => {
socket.on("disconnecting", async (reason) => {
const rooms = new Set(socket.rooms);

await someLongRunningOperation();

// socket.rooms will be empty there
console.log(rooms);
});
});
caution

这些事件以及 connectconnect_errornewListenerremoveListener 都是特殊事件,不应在你的应用中使用:

¥Those events, along with connect, connect_error, newListener and removeListener, are special events that shouldn't be used in your application:

// BAD, will throw an error
socket.emit("disconnect");

属性

¥Attributes

socket.client

对底层 Client 对象的引用。

¥A reference to the underlying Client object.

socket.conn

  • <engine.Socket>

对底层 Client 传输连接(engine.io Socket 对象)的引用。这允许访问 IO 传输层,该层仍然(大部分)抽象实际的 TCP/IP 套接字。

¥A reference to the underlying Client transport connection (engine.io Socket object). This allows access to the IO transport layer, which still (mostly) abstracts the actual TCP/IP socket.

io.on("connection", (socket) => {
console.log("initial transport", socket.conn.transport.name); // prints "polling"

socket.conn.once("upgrade", () => {
// called when the transport is upgraded (i.e. from HTTP long-polling to WebSocket)
console.log("upgraded transport", socket.conn.transport.name); // prints "websocket"
});

socket.conn.on("packet", ({ type, data }) => {
// called for each packet received
});

socket.conn.on("packetCreate", ({ type, data }) => {
// called for each packet sent
});

socket.conn.on("drain", () => {
// called when the write buffer is drained
});

socket.conn.on("close", (reason) => {
// called when the underlying connection is closed
});
});

socket.data

v4.0.0 中添加

¥Added in v4.0.0

可以与 fetchSockets() 实用程序方法结合使用的任意对象:

¥An arbitrary object that can be used in conjunction with the fetchSockets() utility method:

io.on("connection", (socket) => {
socket.data.username = "alice";
});

const sockets = await io.fetchSockets();
console.log(sockets[0].data.username); // "alice"
tip

这也适用于 Socket.IO 集群,带有 Postgres 适配器 等兼容适配器。

¥This also works within a Socket.IO cluster, with a compatible adapter like the Postgres adapter.

socket.handshake

握手细节:

¥The handshake details:

字段类型描述
标头IncomingHttpHeaders作为握手的一部分发送的标头。
时间<string>创建日期(作为字符串)。
地址<string>客户端的 IP 地址。
域名<boolean>连接是否跨域。
安全的<boolean>是否通过 SSL 建立连接。
发布<number>创建日期(作为 unix 时间戳)。
网址<string>请求 URL 字符串。
查询Record<string, string or string[]>第一个请求的查询参数。
授权Record<string, any>身份验证有效负载。另见 此处

用法:

¥Usage:

io.use((socket, next) => {
let handshake = socket.handshake;
// ...
});

io.on("connection", (socket) => {
let handshake = socket.handshake;
// ...
});

示例:

¥Example:

const handshake = {
headers: {
"user-agent": "node-XMLHttpRequest",
accept: "*/*",
host: "localhost:3000",
connection: "close"
},
time: "Wed Jan 01 2020 01:00:00 GMT+0100 (Central European Standard Time)",
address: "::ffff:127.0.0.1",
xdomain: false,
secure: false,
issued: 1577836800000,
url: "/socket.io/?EIO=4&transport=polling&t=OPAfXv5&b64=1",
query: {
EIO: "4",
transport: "polling",
t: "OPAfXv5",
b64: "1"
},
auth: {}
}

注意:headers 属性指的是会话的第一个 HTTP 请求的标头,并且不会被后续的 HTTP 请求更新。

¥Note: the headers attribute refers to the headers of the first HTTP request of the session, and won't be updated by the subsequent HTTP requests.

io.on("connection", (socket) => {
console.log(socket.handshake.headers === socket.request.headers); // prints "true"
});

socket.id

会话的唯一标识符,来自底层 Client

¥A unique identifier for the session, that comes from the underlying Client.

caution

id 属性是一个临时 ID,不应在你的应用中使用(或仅用于调试目的),因为:

¥The id attribute is an ephemeral ID that is not meant to be used in your application (or only for debugging purposes) because:

  • 每次重新连接后都会重新生成此 ID(例如当 WebSocket 连接被切断时,或者当用户刷新页面时)

    ¥this ID is regenerated after each reconnection (for example when the WebSocket connection is severed, or when the user refreshes the page)

  • 两个不同的浏览器选项卡将有两个不同的 ID

    ¥two different browser tabs will have two different IDs

  • 服务器上没有为给定 ID 存储消息队列(即如果客户端断开连接,从服务器发送到该 ID 的消息就会丢失)

    ¥there is no message queue stored for a given ID on the server (i.e. if the client is disconnected, the messages sent from the server to this ID are lost)

请改用常规会话 ID(在 cookie 中发送,或存储在 localStorage 中并在 auth 有效负载中发送)。

¥Please use a regular session ID instead (either sent in a cookie, or stored in the localStorage and sent in the auth payload).

也可以看看:

¥See also:

socket.recovered

v4.6.0 中添加

¥Added in v4.6.0

上次重新连接时连接状态是否成功恢复。

¥Whether the connection state was successfully recovered during the last reconnection.

io.on("connection", (socket) => {
if (socket.recovered) {
// recovery was successful: socket.id, socket.rooms and socket.data were restored
} else {
// new or unrecoverable session
}
});

有关此功能的更多信息 此处

¥More information about this feature here.

socket.request

一个 getter 代理,返回对源自底层 engine.io Clientrequest 的引用。对于访问 CookieUser-Agent 等请求标头很有用。

¥A getter proxy that returns the reference to the request that originated the underlying engine.io Client. Useful for accessing request headers such as Cookie or User-Agent.

import { parse } from "cookie";

io.on("connection", (socket) => {
const cookies = parse(socket.request.headers.cookie || "");
});

注意:socket.request 指会话的第一个 HTTP 请求,不会被后续的 HTTP 请求更新。

¥Note: socket.request refers to the first HTTP request of the session, and won't be updated by the subsequent HTTP requests.

io.on("connection", (socket) => {
console.log(socket.request.headers === socket.handshake.headers); // prints "true"
});

如果不需要此引用,可以丢弃它以减少内存占用:

¥If you don't need this reference, you can discard it in order to reduce the memory footprint:

io.on("connection", (socket) => {
delete socket.conn.request;
});

socket.rooms

一组字符串,用于标识该客户端所在的房间。

¥A Set of strings identifying the rooms this client is in.

io.on("connection", (socket) => {

console.log(socket.rooms); // Set { <socket.id> }

socket.join("room1");

console.log(socket.rooms); // Set { <socket.id>, "room1" }

});

方法

¥Methods

socket.compress(value)

  • value <boolean> 后面的数据包是否会被压缩

    ¥value <boolean> whether to following packet will be compressed

  • 返回 Socket 进行链接

    ¥Returns Socket for chaining

设置后续事件触发的修饰符,仅当值为 true 时才会压缩事件数据。当你不调用该方法时默认为 true

¥Sets a modifier for a subsequent event emission that the event data will only be compressed if the value is true. Defaults to true when you don't call the method.

io.on("connection", (socket) => {
socket.compress(false).emit("uncompressed", "that's rough");
});

socket.disconnect([close])

断开此套接字。如果 close 的值为 true,则关闭底层连接。否则,它只是断开命名空间。

¥Disconnects this socket. If value of close is true, closes the underlying connection. Otherwise, it just disconnects the namespace.

io.on("connection", (socket) => {
setTimeout(() => socket.disconnect(true), 5000);
});

socket.emit(eventName[, ...args][, ack])

(覆盖 EventEmitter.emit

¥(overrides EventEmitter.emit)

向由字符串名称标识的套接字触发事件。可以包括任何其他参数。支持所有可序列化的数据结构,包括 Buffer

¥Emits an event to the socket identified by the string name. Any other parameters can be included. All serializable data structures are supported, including Buffer.

io.on("connection", () => {
socket.emit("hello", "world");
socket.emit("with-binary", 1, "2", { 3: "4", 5: Buffer.from([6]) });
});

ack 参数是可选的,将与客户端的答案一起调用。

¥The ack argument is optional and will be called with the client's answer.

服务器

¥Server

io.on("connection", (socket) => {
socket.emit("hello", "world", (response) => {
console.log(response); // "got it"
});
});

客户端

¥Client

socket.on("hello", (arg, callback) => {
console.log(arg); // "world"
callback("got it");
});

socket.emitWithAck(eventName[, ...args])

v4.6.0 中添加

¥Added in v4.6.0

基于 promise 的版本,触发并期望来自给定客户端的确认:

¥Promised-based version of emitting and expecting an acknowledgement from the given client:

io.on("connection", async (socket) => {
// without timeout
const response = await socket.emitWithAck("hello", "world");

// with a specific timeout
try {
const response = await socket.timeout(10000).emitWithAck("hello", "world");
} catch (err) {
// the client did not acknowledge the event in the given delay
}
});

上面的例子相当于:

¥The example above is equivalent to:

io.on("connection", (socket) => {
// without timeout
socket.emit("hello", "world", (val) => {
// ...
});

// with a specific timeout
socket.timeout(10000).emit("hello", "world", (err, val) => {
// ...
});
});

在接收端:

¥And on the receiving side:

socket.on("hello", (arg1, callback) => {
callback("got it"); // only one argument is expected
});

socket.eventNames()

继承自 EventEmitter(以及这里未提及的其他方法)。请参阅 events 模块的 Node.js 文档。

¥Inherited from EventEmitter (along with other methods not mentioned here). See the Node.js documentation for the events module.

socket.except(rooms)

v4.0.0 中添加

¥Added in v4.0.0

为后续事件触发设置修饰符,该事件将仅广播到尚未加入给定 rooms 的客户端(套接字本身被排除)。

¥Sets a modifier for a subsequent event emission that the event will only be broadcast to clients that have not joined the given rooms (the socket itself being excluded).

// to all clients except the ones in "room1" and the sender
socket.broadcast.except("room1").emit(/* ... */);

// same as above
socket.except("room1").emit(/* ... */);

// to all clients in "room4" except the ones in "room5" and the sender
socket.to("room4").except("room5").emit(/* ... */);

socket.in(room)

v1.0.0 中添加

¥Added in v1.0.0

socket.to(room) 的同义词。

¥Synonym of socket.to(room).

socket.join(room)

将套接字添加到给定的 room 或房间列表中。

¥Adds the socket to the given room or to the list of rooms.

io.on("connection", (socket) => {
socket.join("room 237");

console.log(socket.rooms); // Set { <socket.id>, "room 237" }

socket.join(["room 237", "room 238"]);

io.to("room 237").emit("a new user has joined the room"); // broadcast to everyone in the room
});

加入房间的机制由已配置的 Adapter 处理(参见上面的 Server#adapter),默认为 socket.io-adapter

¥The mechanics of joining rooms are handled by the Adapter that has been configured (see Server#adapter above), defaulting to socket.io-adapter.

为了你的方便,每个套接字自动加入一个由其 id 标识的房间(参见 Socket#id)。这使得向其他套接字广播消息变得容易:

¥For your convenience, each socket automatically joins a room identified by its id (see Socket#id). This makes it easy to broadcast messages to other sockets:

io.on("connection", (socket) => {
socket.on("say to someone", (id, msg) => {
// send a private message to the socket with the given id
socket.to(id).emit("my message", msg);
});
});

socket.leave(room)

  • room <string>

  • 返回 void | Promise

    ¥Returns void | Promise

从给定的 room 中删除套接字。

¥Removes the socket from the given room.

io.on("connection", (socket) => {
socket.leave("room 237");

io.to("room 237").emit(`user ${socket.id} has left the room`);
});
info

断线后自动离开房间。

¥Rooms are left automatically upon disconnection.

socket.listenersAny()

返回已注册的捕获所有监听器的列表。

¥Returns the list of registered catch-all listeners.

const listeners = socket.listenersAny();

socket.listenersAnyOutgoing()

v4.5.0 中添加

¥Added in v4.5.0

返回已注册的传出数据包捕获所有监听器的列表。

¥Returns the list of registered catch-all listeners for outgoing packets.

const listeners = socket.listenersAnyOutgoing();

socket.offAny([listener])

删除先前注册的监听器。如果未提供监听器,则删除所有捕获所有监听器。

¥Removes the previously registered listener. If no listener is provided, all catch-all listeners are removed.

const myListener = () => { /* ... */ };

socket.onAny(myListener);

// then, later
socket.offAny(myListener);

socket.offAny();

socket.offAnyOutgoing([listener])

v4.5.0 中添加

¥Added in v4.5.0

删除先前注册的监听器。如果未提供监听器,则删除所有捕获所有监听器。

¥Removes the previously registered listener. If no listener is provided, all catch-all listeners are removed.

const myListener = () => { /* ... */ };

socket.onAnyOutgoing(myListener);

// remove a single listener
socket.offAnyOutgoing(myListener);

// remove all listeners
socket.offAnyOutgoing();

socket.on(eventName, callback)

继承自 EventEmitter 类

¥Inherited from the EventEmitter class.

为给定事件注册一个新的处理程序。

¥Register a new handler for the given event.

socket.on("news", (data) => {
console.log(data);
});
// with several arguments
socket.on("news", (arg1, arg2, arg3) => {
// ...
});
// or with acknowledgement
socket.on("news", (data, callback) => {
callback(0);
});

socket.onAny(callback)

注册一个新的包罗万象的监听器。

¥Register a new catch-all listener.

socket.onAny((event, ...args) => {
console.log(`got ${event}`);
});
caution

回执 没有被捕获在包罗万象的监听器中。

¥Acknowledgements are not caught in the catch-all listener.

socket.emit("foo", (value) => {
// ...
});

socket.onAnyOutgoing(() => {
// triggered when the event is sent
});

socket.onAny(() => {
// not triggered when the acknowledgement is received
});

socket.onAnyOutgoing(callback)

v4.5.0 中添加

¥Added in v4.5.0

为传出数据包注册一个新的捕获所有监听器。

¥Register a new catch-all listener for outgoing packets.

socket.onAnyOutgoing((event, ...args) => {
console.log(`got ${event}`);
});
caution

回执 没有被捕获在包罗万象的监听器中。

¥Acknowledgements are not caught in the catch-all listener.

socket.on("foo", (value, callback) => {
callback("OK");
});

socket.onAny(() => {
// triggered when the event is received
});

socket.onAnyOutgoing(() => {
// not triggered when the acknowledgement is sent
});

socket.once(eventName, listener)

继承自 EventEmitter(以及这里未提及的其他方法)。请参阅 events 模块的 Node.js 文档。

¥Inherited from EventEmitter (along with other methods not mentioned here). See the Node.js documentation for the events module.

socket.prependAny(callback)

注册一个新的包罗万象的监听器。监听器被添加到监听器数组的开头。

¥Register a new catch-all listener. The listener is added to the beginning of the listeners array.

socket.prependAny((event, ...args) => {
console.log(`got ${event}`);
});

socket.prependAnyOutgoing(callback)

v4.5.0 中添加

¥Added in v4.5.0

为传出数据包注册一个新的捕获所有监听器。监听器被添加到监听器数组的开头。

¥Register a new catch-all listener for outgoing packets. The listener is added to the beginning of the listeners array.

socket.prependAnyOutgoing((event, ...args) => {
console.log(`got ${event}`);
});

socket.removeAllListeners([eventName])

继承自 EventEmitter(以及这里未提及的其他方法)。请参阅 events 模块的 Node.js 文档。

¥Inherited from EventEmitter (along with other methods not mentioned here). See the Node.js documentation for the events module.

socket.removeListener(eventName, listener)

继承自 EventEmitter(以及这里未提及的其他方法)。请参阅 events 模块的 Node.js 文档。

¥Inherited from EventEmitter (along with other methods not mentioned here). See the Node.js documentation for the events module.

socket.send([...args][, ack])

发送 message 事件。参见 socket.emit(eventName[, ...args][, ack])

¥Sends a message event. See socket.emit(eventName[, ...args][, ack]).

socket.timeout(value)

v4.4.0 中添加

¥Added in v4.4.0

为后续事件触发设置一个修饰符,当给定的毫秒数过去而没有得到客户端的确认时,回调将被调用并出现错误:

¥Sets a modifier for a subsequent event emission that the callback will be called with an error when the given number of milliseconds have elapsed without an acknowledgement from the client:

socket.timeout(5000).emit("my-event", (err) => {
if (err) {
// the client did not acknowledge the event in the given delay
}
});

socket.to(room)

History
版本变化
v4.0.0允许通过一系列房间。
v1.0.0初步实现。

为后续事件触发设置修饰符,该事件将仅广播到已加入给定 room 的客户端(套接字本身被排除)。

¥Sets a modifier for a subsequent event emission that the event will only be broadcast to clients that have joined the given room (the socket itself being excluded).

要发送到多个房间,你可以多次调用 to

¥To emit to multiple rooms, you can call to several times.

io.on("connection", (socket) => {

// to one room
socket.to("others").emit("an event", { some: "data" });

// to multiple rooms
socket.to("room1").to("room2").emit("hello");

// or with an array
socket.to(["room1", "room2"]).emit("hello");

// a private message to another socket
socket.to(/* another socket id */).emit("hey");

// WARNING: `socket.to(socket.id).emit()` will NOT work
// Please use `io.to(socket.id).emit()` instead.
});

注意:广播时不支持确认。

¥Note: acknowledgements are not supported when broadcasting.

socket.use(fn)

History
版本变化
v3.0.5恢复第一次执行。
v3.0.0删除有利于 socket.onAny()
v1.7.2error 事件直接发送到客户端。
v1.6.0首次实现。

注册一个中间件,它是一个为每个传入的 Packet 执行的函数,并接收数据包作为参数,以及一个可以选择将执行推迟到下一个注册的中间件的函数。

¥Registers a middleware, which is a function that gets executed for every incoming Packet and receives as parameter the packet and a function to optionally defer execution to the next registered middleware.

然后,传递给中间件回调的错误将作为 error 事件在服务器端触发:

¥Errors passed to the middleware callback are then emitted as error events on the server-side:

io.on("connection", (socket) => {
socket.use(([event, ...args], next) => {
if (isUnauthorized(event)) {
return next(new Error("unauthorized event"));
}
// do not forget to call next
next();
});

socket.on("error", (err) => {
if (err && err.message === "unauthorized event") {
socket.disconnect();
}
});
});

标志

¥Flags

标志:'广播'

¥Flag: 'broadcast'

为后续事件触发设置修饰符,事件数据将仅广播到除发送者之外的每个套接字。

¥Sets a modifier for a subsequent event emission that the event data will only be broadcast to every sockets but the sender.

io.on("connection", (socket) => {
socket.broadcast.emit("an event", { some: "data" }); // everyone gets it but the sender
});

标志:'volatile'

¥Flag: 'volatile'

为后续事件触发设置一个修饰符,如果客户端未准备好接收消息(由于网络缓慢或其他问题,或者因为它们通过长轮询连接并且处于请求中间,则事件数据可能会丢失) -响应周期)。

¥Sets a modifier for a subsequent event emission that the event data may be lost if the client is not ready to receive messages (because of network slowness or other issues, or because they’re connected through long polling and is in the middle of a request-response cycle).

io.on("connection", (socket) => {
socket.volatile.emit("an event", { some: "data" }); // the client may or may not receive it
});

客户端

¥Client

Client in the class diagram for the serverClient in the class diagram for the server

Client 类表示传入传输 (engine.io) 连接。一个 Client 可以与属于不同 Namespace 的多个复用 Socket 相关联。

¥The Client class represents an incoming transport (engine.io) connection. A Client can be associated with many multiplexed Sockets that belong to different Namespaces.

属性

¥Attributes

client.conn

  • <engine.Socket>

对底层 engine.io Socket 连接的引用。

¥A reference to the underlying engine.io Socket connection.

client.request

一个 getter 代理,返回对发起 engine.io 连接的 request 的引用。对于访问 CookieUser-Agent 等请求标头很有用。

¥A getter proxy that returns the reference to the request that originated the engine.io connection. Useful for accessing request headers such as Cookie or User-Agent.

引擎

¥Engine

Engine.IO 服务器,管理 WebSocket / HTTP 长轮询连接。更多信息在 此处

¥The Engine.IO server, which manages the WebSocket / HTTP long-polling connections. More information here.

它的源代码可以在这里找到:https://github.com/socketio/engine.io

¥Its source code can be found here: https://github.com/socketio/engine.io

事件

¥Events

事件:'connection_error'

¥Event: 'connection_error'

v4.1.0 中添加

¥Added in v4.1.0

io.engine.on("connection_error", (err) => {
console.log(err.req); // the request object
console.log(err.code); // the error code, for example 1
console.log(err.message); // the error message, for example "Session ID unknown"
console.log(err.context); // some additional error context
});

当连接异常关闭时将触发此事件。以下是可能的错误代码列表:

¥This event will be emitted when a connection is abnormally closed. Here is the list of possible error codes:

代码消息
0"运输未知"
1"会话 ID 未知"
2"错误的握手方式"
3"错误的请求"
4"禁止"
5"不支持的协议版本"

事件:'标头'

¥Event: 'headers'

v4.1.0 中添加

¥Added in v4.1.0

该事件将在写入会话的每个 HTTP 请求的响应标头(包括 WebSocket 升级)之前触发,允许你自定义它们。

¥This event will be emitted just before writing the response headers of each HTTP request of the session (including the WebSocket upgrade), allowing you to customize them.

import { serialize, parse } from "cookie";

io.engine.on("headers", (headers, request) => {
if (!request.headers.cookie) return;
const cookies = parse(request.headers.cookie);
if (!cookies.randomId) {
headers["set-cookie"] = serialize("randomId", "abc", { maxAge: 86400 });
}
});

事件:'initial_headers'

¥Event: 'initial_headers'

v4.1.0 中添加

¥Added in v4.1.0

该事件将在写入会话的第一个 HTTP 请求(握手)的响应标头之前触发,允许你自定义它们。

¥This event will be emitted just before writing the response headers of the first HTTP request of the session (the handshake), allowing you to customize them.

import { serialize } from "cookie";

io.engine.on("initial_headers", (headers, request) => {
headers["set-cookie"] = serialize("uid", "1234", { sameSite: "strict" });
});

如果需要执行一些异步操作,则需要使用 allowRequest 选项:

¥If you need to perform some asynchronous operations, you will need to use the allowRequest option:

import { serialize } from "cookie";

const io = new Server(httpServer, {
allowRequest: async (req, callback) => {
const session = await fetchSession(req);
req.session = session;
callback(null, true);
}
});

io.engine.on("initial_headers", (headers, req) => {
if (req.session) {
headers["set-cookie"] = serialize("sid", req.session.id, { sameSite: "strict" });
}
});

也可以看看:

¥See also:

属性

¥Attributes

engine.clientsCount

v1.0.0 中添加

¥Added in v1.0.0

当前连接的客户端数量。

¥The number of currently connected clients.

const count = io.engine.clientsCount;
// may or may not be similar to the count of Socket instances in the main namespace, depending on your usage
const count2 = io.of("/").sockets.size;

方法

¥Methods

engine.generateId

该函数用于生成新的会话 ID。默认为 Base64id

¥The function used to generate a new session ID. Defaults to base64id.

const uuid = require("uuid");

io.engine.generateId = () => {
return uuid.v4(); // must be unique across all Socket.IO servers
}

engine.handleUpgrade(request, socket, head)

v1.0.0 中添加

¥Added in v1.0.0

此方法可用于注入 HTTP 升级:

¥This method can be used to inject an HTTP upgrade:

同时使用 Socket.IO 服务器和普通 WebSocket 服务器的示例:

¥Example with both a Socket.IO server and a plain WebSocket server:

import { createServer } from "http";
import { Server as WsServer } from "ws";
import { Server } from "socket.io";

const httpServer = createServer();
const wss = new WsServer({ noServer: true });
const io = new Server(httpServer);

httpServer.removeAllListeners("upgrade");

httpServer.on("upgrade", (req, socket, head) => {
if (req.url === "/") {
wss.handleUpgrade(req, socket, head, (ws) => {
wss.emit("connection", ws, req);
});
} else if (req.url.startsWith("/socket.io/")) {
io.engine.handleUpgrade(req, socket, head);
} else {
socket.destroy();
}
});

httpServer.listen(3000);

engine.use(middleware)

v4.6.0 中添加

¥Added in v4.6.0

添加新的 Express 中间件

¥Adds a new Express middleware.

io.engine.use((req, res, next) => {
// do something

next();
});

每个传入的 HTTP 请求(包括升级请求)都会调用中间件。

¥The middlewares will be called for each incoming HTTP requests, including upgrade requests.

express-session 为例:

¥Example with express-session:

import session from "express-session";

io.engine.use(session({
secret: "keyboard cat",
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}));

helmet 为例:

¥Example with helmet:

import helmet from "helmet";

io.engine.use(helmet());