服务器 API
服务器
¥Server
相关文档页面:
¥Related documentation pages:
构造函数
¥Constructor
new Server(httpServer[, options])
httpServer
http.Server
|https.Server
options
<Object>
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)
options
<Object>
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'
namespace
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);
});
跟踪 动态创建的 命名空间
¥to track the dynamically created namespaces
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])
httpServer
http.Server
|https.Server
options
<Object>
使用提供的 options
将 Server
连接到 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])
使用提供的 options
将 Server
连接到给定的 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 { createServer } from "node:http";
import { Server as Engine } from "engine.io";
import { Server } from "socket.io";
const httpServer = createServer((req, res) => {
res.writeHead(404).end();
});
const engine = new Engine();
engine.attach(httpServer, {
path: "/socket.io/"
});
const io = new Server();
io.bind(engine);
httpServer.listen(3000);
server.close([callback])
callback
<Function>
关闭 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.
这也会关闭底层 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);
仅关闭底层 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);
此方法也适用于具有 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.0 | io.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
});
参数将自动序列化,因此不需要调用 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
}
});
server.emitWithAck(eventName[, ...args])
v4.6.0 中添加
¥Added in v4.6.0
args
any[]
¥Returns
Promise<any[]>
基于 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
<string>
|<string[]>
返回
BroadcastOperator
¥Returns
BroadcastOperator
为后续事件触发设置修饰符,该事件将仅广播给尚未加入给定 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
¥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
}
});
});
此方法也适用于具有 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
<string>
|<RegExp>
|<Function>
返回
<Namespace>
¥Returns
<Namespace>
通过路径名标识符 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
<Function>
返回
<Server>
¥Returns
<Server>
将 listener
函数添加到名为 eventName
的事件的监听器数组的末尾。
¥Adds the listener
function to the end of the listeners array for the event named eventName
.
可用事件:
¥Available events:
来自
serverSideEmit
方法的任何自定义事件¥any custom event from the
serverSideEmit
method
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/");
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])
如果 value
是 true
,则连接的服务器将为客户端文件提供服务。默认为 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(/* ... */);
eventName
<string>
args
<any[]>
ack
<Function>
返回
true
¥Returns
true
向 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:
connection
、connect
和new_namespace
字符串是保留的,不能在你的应用中使用。¥the
connection
,connect
andnew_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(/* ... */);
eventName
<string>
args
<any[]>
ack
<Function>
¥Returns
Promise<any[]>
基于 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");
此方法也适用于具有 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");
此方法也适用于具有 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)
.
fn
<Function>
为主命名空间注册一个中间件,该中间件是为每个传入的 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.
如果你正在寻找 Express 中间件,请检查 本节。
¥If you are looking for Express middlewares, please check this section.
命名空间
¥Namespace
表示在由路径名(例如:/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'
socket
<Socket>
在客户端连接时触发。
¥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>>
该方法将在下一个主要版本中删除,请使用 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.0 | io.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
});
参数将自动序列化,因此不需要调用 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
}
});
命名空间.emitWithAck(eventName[, ...args])
¥namespace.emitWithAck(eventName[, ...args])
v4.6.0 中添加
¥Added in v4.6.0
args
any[]
¥Returns
Promise<any[]>
基于 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
<string>
|<string[]>
返回
BroadcastOperator
¥Returns
BroadcastOperator
为后续事件触发设置修饰符,该事件将仅广播给尚未加入给定 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"
重要的提示:此方法(以及 socketsJoin
、socketsLeave
和 disconnectSockets
)与 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
eventName
<string>
args
<any[]>
ack
<Function>
返回
true
¥Returns
true
向 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:
connection
、connect
和new_namespace
字符串是保留的,不能在你的应用中使用。¥the
connection
,connect
andnew_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
eventName
<string>
args
<any[]>
ack
<Function>
¥Returns
Promise<any[]>
基于 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
rooms
<string>
|<string[]>
返回
void
¥Returns
void
使匹配的 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
rooms
<string>
|<string[]>
返回
void
¥Returns
void
使匹配的 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
fn
<Function>
为给定的命名空间注册一个中间件,该中间件是为每个传入的 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.
如果你正在寻找 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
是与浏览器客户端交互的基础类。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 Socket
s (see Socket#to
below).
Socket
类继承自 EventEmitter。Socket
类重写 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);
});
});
这些事件以及 connect
、connect_error
、newListener
和 removeListener
都是特殊事件,不应在你的应用中使用:
¥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"
这也适用于 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
.
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 Client
的 request
的引用。对于访问 Cookie
或 User-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
<boolean>
是否关闭底层连接¥
close
<boolean>
whether to close the underlying connection返回
Socket
¥Returns
Socket
断开此套接字。如果 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
)
args
<any[]>
ack
<Function>
返回
true
¥Returns
true
向由字符串名称标识的套接字触发事件。可以包括任何其他参数。支持所有可序列化的数据结构,包括 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
args
any[]
返回
Promise<any>
¥Returns
Promise<any>
基于 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
<string>
|<string[]>
返回
BroadcastOperator
¥Returns
BroadcastOperator
为后续事件触发设置修饰符,该事件将仅广播到尚未加入给定 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
<string>
|<string[]>
返回
void
|Promise
¥Returns
void
|Promise
将套接字添加到给定的 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`);
});
断线后自动离开房间。
¥Rooms are left automatically upon disconnection.
socket.listenersAny()
返回
<Function[]>
¥Returns
<Function[]>
返回已注册的捕获所有监听器的列表。
¥Returns the list of registered catch-all listeners.
const listeners = socket.listenersAny();
socket.listenersAnyOutgoing()
v4.5.0 中添加
¥Added in v4.5.0
返回
<Function[]>
¥Returns
<Function[]>
返回已注册的传出数据包捕获所有监听器的列表。
¥Returns the list of registered catch-all listeners for outgoing packets.
const listeners = socket.listenersAnyOutgoing();
socket.offAny([listener])
listener
<Function>
删除先前注册的监听器。如果未提供监听器,则删除所有捕获所有监听器。
¥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
listener
<Function>
删除先前注册的监听器。如果未提供监听器,则删除所有捕获所有监听器。
¥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.
callback
<Function>
返回
<Socket>
¥Returns
<Socket>
为给定事件注册一个新的处理程序。
¥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)
callback
<Function>
注册一个新的包罗万象的监听器。
¥Register a new catch-all listener.
socket.onAny((event, ...args) => {
console.log(`got ${event}`);
});
回执 没有被捕获在包罗万象的监听器中。
¥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
callback
<Function>
为传出数据包注册一个新的捕获所有监听器。
¥Register a new catch-all listener for outgoing packets.
socket.onAnyOutgoing((event, ...args) => {
console.log(`got ${event}`);
});
回执 没有被捕获在包罗万象的监听器中。
¥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)
callback
<Function>
注册一个新的包罗万象的监听器。监听器被添加到监听器数组的开头。
¥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
callback
<Function>
为传出数据包注册一个新的捕获所有监听器。监听器被添加到监听器数组的开头。
¥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])
args
<any[]>
ack
<Function>
返回
Socket
¥Returns
Socket
发送 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
<string>
|<string[]>
返回
Socket
进行链接¥Returns
Socket
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
(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.2 | error 事件直接发送到客户端。 |
v1.6.0 | 首次实现。 |
fn
<Function>
注册一个中间件,它是一个为每个传入的 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
类表示传入传输 (engine.io) 连接。一个 Client
可以与属于不同 Namespace
的多个复用 Socket
相关联。
¥The Client
class represents an incoming transport (engine.io) connection. A Client
can be associated with many multiplexed Socket
s that belong to different Namespace
s.
属性
¥Attributes
client.conn
<engine.Socket>
对底层 engine.io
Socket
连接的引用。
¥A reference to the underlying engine.io
Socket
connection.
client.request
一个 getter 代理,返回对发起 engine.io 连接的 request
的引用。对于访问 Cookie
或 User-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
error
<Error>
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
headers
<Object>
标头的哈希值,按标头名称索引¥
headers
<Object>
a hash of headers, indexed by header namerequest
http.IncomingMessage
传入请求¥
request
http.IncomingMessage
the incoming request
该事件将在写入会话的每个 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
headers
<Object>
标头的哈希值,按标头名称索引¥
headers
<Object>
a hash of headers, indexed by header namerequest
http.IncomingMessage
传入请求¥
request
http.IncomingMessage
the incoming request
该事件将在写入会话的第一个 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
request
http.IncomingMessage
传入请求¥
request
http.IncomingMessage
the incoming requestsocket
<stream.Duplex>
服务器和客户端之间的网络套接字¥
socket
<stream.Duplex>
the network socket between the server and clienthead
<Buffer>
升级流的第一个数据包(可以为空)¥
head
<Buffer>
the first packet of the upgraded stream (may be empty)
此方法可用于注入 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());