Namespaces

A Namespace is a communication channel that allows you to split the logic of your application over a single shared connection.

命名空间是一个通信通道,它使您可以在单个共享连接上拆分应用程序的逻辑。

Namespace diagram

Possible use cases:

  • you want to create an admin namespace that only authorized users have access to, so the logic related to those users is separated from the rest of the application

可能的用例:

  • 您想要创建仅授权用户有权访问的管理名称空间,因此与这些用户相关的逻辑与应用程序的其余部分分开
1
2
3
4
5
6
7
8
9
10
11
12
const adminNamespace = io.of('/admin');

adminNamespace.use((socket, next) => {
// ensure the user has sufficient rights
next();
});

adminNamespace.on('connection', socket => {
socket.on('delete user', () => {
// ...
});
});
  • your application has multiple tenants so you want to dynamically create one namespace per tenant
  • 您的应用程序有多个租户,因此您想为每个租户动态创建一个命名空间
1
2
3
4
5
6
7
8
9
10
11
12
13
const workspaces = io.of(/^\/\w+$/);

workspaces.on('connection', socket => {
const workspace = socket.nsp;

workspace.emit('hello');
});

// this middleware will be assigned to each namespace
workspaces.use((socket, next) => {
// ensure the user has access to the workspace
next();
});

Default namespace

We call the default namespace / and it’s the one Socket.IO clients connect to by default, and the one the server listens to by default.

This namespace is identified by io.sockets or simply io:

我们将默认名称空间称为/,它是一个默认连接的Socket.IO客户端,一个默认情况下服务器监听的名称空间。

该名称空间由io.sockets或仅由io标识:

1
2
3
// the following two will emit to all the sockets connected to `/`
io.sockets.emit('hi', 'everyone');
io.emit('hi', 'everyone'); // short form

Each namespace emits a connection event that receives each Socket instance as a parameter

每个名称空间都发出一个连接事件,该事件接收每个Socket实例作为参数

1
2
3
io.on('connection', socket => {
socket.on('disconnect', () => {});
});

Custom namespaces

To set up a custom namespace, you can call the of function on the server-side:

要设置自定义名称空间,可以在服务器端调用of函数:

1
2
3
4
5
6
7
const nsp = io.of('/my-namespace');

nsp.on('connection', socket => {
console.log('someone connected');
});

nsp.emit('hi', 'everyone!');

On the client side, you tell Socket.IO client to connect to that namespace:

在客户端,您告诉Socket.IO客户端连接到该名称空间:

1
const socket = io('/my-namespace');

Important note: The namespace is an implementation detail of the Socket.IO protocol, and is not related to the actual URL of the underlying transport, which defaults to /socket.io/….

重要说明:名称空间是Socket.IO协议的实现细节,并且与基础传输的实际URL不相关,该URL默认为/socket.io/…。

Namespace middleware(命名空间中间件)

A middleware 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. A Socket.IO middleware is very similar to what you can find in Express.

中间件是为每个传入的Socket执行的函数,并接收该套接字和参数作为选择将执行推迟到下一个注册的中间件作为参数。 Socket.IO中间件与Express中非常相似。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// registers a middleware for the default namespace
io.use((socket, next) => {
if (isValid(socket.request)) {
next();
} else {
next(new Error('invalid'));
}
});

// registers a middleware for a custom namespace
io.of('/admin').use(async (socket, next) => {
const user = await fetchUser(socket.handshake.query);
if (user.isAdmin) {
socket.user = user;
next();
} else {
next(new Error('forbidden'));
}
});

You can register several middleware functions for the same namespace. They will be executed sequentially:

您可以为同一名称空间注册多个中间件功能。 它们将按顺序执行:

1
2
3
4
5
6
7
8
9
10
11
12
io.use((socket, next) => {
next();
});

io.use((socket, next) => {
next(new Error('thou shall not pass'));
});

io.use((socket, next) => {
// not executed, since the previous middleware has returned an error
next();
});

Handling middleware error

If the next method is called with an Error object, the client will receive an error event.

如果使用错误对象调用“下一个”方法,则客户端将收到一个“错误”事件。

1
2
3
4
5
6
7
import io from 'socket.io-client';

const socket = io();

socket.on('error', (reason) => {
console.log(reason); // prints the message associated with the error, e.g. "thou shall not pass" in the example above
});

Compatibility with Express middleware(与Express中间件的兼容性)

Most existing Express middleware modules should be compatible with Socket.IO, you just need a little wrapper function to make the method signatures match:

大多数现有的Express中间件模块应与Socket.IO兼容,您只需要一个包装函数即可使方法签名匹配:

1
const wrap = middleware => (socket, next) => middleware(socket.request, {}, next);

The middleware functions that end the request-response cycle and do not call next() will not work though.

Example with express-session:

但是,终止请求-响应周期并且不调用next()的中间件函数将无法工作。

快速会话示例:

1
2
3
4
5
6
7
const session = require('express-session');

io.use(wrap(session({ secret: 'cats' })));

io.on('connect', (socket) => {
const session = socket.request.session;
});

Example with Passport:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const session = require('express-session');
const passport = require('passport');

io.use(wrap(session({ secret: 'cats' })));
io.use(wrap(passport.initialize()));
io.use(wrap(passport.session()));

io.use((socket, next) => {
if (socket.request.user) {
next();
} else {
next(new Error('unauthorized'))
}
});

A complete example with Passport can be found here.

Caught a mistake? Edit this page on GitHub