Socket 实例(服务器端)

Socket是与客户端交互的基础类。它继承了 Node.jsEventEmitter的所有方法,例如emit, on, onceremoveListener.

Bidirectional communication between server and clientBidirectional communication between server and client

除了:

Socket 实例有一些可能在您的应用程序中使用的属性:

Socket#id

每个新连接都分配有一个随机的 20 个字符的标识符。

此标识符与客户端的值同步。

  1. // server-side
  2. io.on("connection", (socket) => {
  3. console.log(socket.id); // ojIckSD2jqNzOqIrAGzL
  4. });
  5. // client-side
  6. socket.on("connect", () => {
  7. console.log(socket.id); // ojIckSD2jqNzOqIrAGzL
  8. });

创建后,Socket 会加入由其自己的 id 标识的房间,这意味着您可以将其用于私人消息传递:

  1. io.on("connection", socket => {
  2. socket.on("private message", (anotherSocketId, msg) => {
  3. socket.to(anotherSocketId).emit("private message", socket.id, msg);
  4. });
  5. });

注意:您不能覆盖此标识符,因为它在 Socket.IO 代码库的多个部分中使用。

Socket#handshake

此对象包含有关在 Socket.IO 会话开始时发生的握手的一些详细信息。

  1. {
  2. headers: /* the headers of the initial request */
  3. query: /* the query params of the initial request */
  4. auth: /* the authentication payload */
  5. time: /* the date of creation (as string) */
  6. issued: /* the date of creation (unix timestamp) */
  7. url: /* the request URL string */
  8. address: /* the ip of the client */
  9. xdomain: /* whether the connection is cross-domain */
  10. secure: /* whether the connection is secure */
  11. }

例子:

  1. {
  2. "headers": {
  3. "user-agent": "xxxx",
  4. "accept": "*/*",
  5. "host": "example.com",
  6. "connection": "close"
  7. },
  8. "query": {
  9. "EIO": "4",
  10. "transport": "polling",
  11. "t": "NNjNltH"
  12. },
  13. "auth": {
  14. "token": "123"
  15. },
  16. "time": "Sun Nov 22 2020 01:33:46 GMT+0100 (Central European Standard Time)",
  17. "issued": 1606005226969,
  18. "url": "/socket.io/?EIO=4&transport=polling&t=NNjNltH",
  19. "address": "::ffff:1.2.3.4",
  20. "xdomain": false,
  21. "secure": true
  22. }

Socket#rooms

这是对 Socket 当前所在房间的引用。

  1. io.on("connection", (socket) => {
  2. console.log(socket.rooms); // Set { <socket.id> }
  3. socket.join("room1");
  4. console.log(socket.rooms); // Set { <socket.id>, "room1" }
  5. });

Socket#data

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

  1. // server A
  2. io.on("connection", (socket) => {
  3. socket.data.username = "alice";
  4. });
  5. // server B
  6. const sockets = await io.fetchSockets();
  7. console.log(sockets[0].data.username); // "alice"

更多信息在这里.

Socket#conn

对底层 Engine.IO 套接字的引用(参见此处)。

  1. io.on("connection", (socket) => {
  2. console.log("initial transport", socket.conn.transport.name); // prints "polling"
  3. socket.conn.once("upgrade", () => {
  4. // called when the transport is upgraded (i.e. from HTTP long-polling to WebSocket)
  5. console.log("upgraded transport", socket.conn.transport.name); // prints "websocket"
  6. });
  7. socket.conn.on("packet", ({ type, data }) => {
  8. // called for each packet received
  9. });
  10. socket.conn.on("packetCreate", ({ type, data }) => {
  11. // called for each packet sent
  12. });
  13. socket.conn.on("drain", () => {
  14. // called when the write buffer is drained
  15. });
  16. socket.conn.on("close", (reason) => {
  17. // called when the underlying connection is closed
  18. });
  19. });

Additional attributes

只要您不覆盖任何现有属性,您就可以将任何属性附加到 Socket 实例并在以后使用它:

  1. // in a middleware
  2. io.use(async (socket, next) => {
  3. try {
  4. const user = await fetchUser(socket);
  5. socket.user = user;
  6. } catch (e) {
  7. next(new Error("unknown user"));
  8. }
  9. });
  10. io.on("connection", (socket) => {
  11. console.log(socket.user);
  12. // in a listener
  13. socket.on("set username", (username) => {
  14. socket.username = username;
  15. });
  16. });

Socket middlewares

这些中间件看起来很像通常的中间件,除了它们是为每个传入的数据包调用的:

  1. socket.use(([event, ...args], next) => {
  2. // do something with the packet (logging, authorization, rate limiting...)
  3. // do not forget to call next() at the end
  4. next();
  5. });

next也可以使用错误对象调用该方法。在这种情况下,事件将不会到达注册的事件处理程序,而error是会发出一个事件:

  1. io.on("connection", (socket) => {
  2. socket.use(([event, ...args], next) => {
  3. if (isUnauthorized(event)) {
  4. return next(new Error("unauthorized event"));
  5. }
  6. next();
  7. });
  8. socket.on("error", (err) => {
  9. if (err && err.message === "unauthorized event") {
  10. socket.disconnect();
  11. }
  12. });
  13. });

注意:此功能仅存在于服务器端。对于客户端,您可能对catch-all listeners感兴趣。

Events

在服务器端,Socket 实例发出两个特殊事件:

disconnect

此事件由 Socket 实例在断开连接时触发。

  1. io.on("connection", (socket) => {
  2. socket.on("disconnect", (reason) => {
  3. // ...
  4. });
  5. });

以下是可能的原因列表:

ReasonDescription
server namespace disconnectsocket被socket.disconnect强行断开
client namespace disconnect客户端使用socket.disconnect()手动断开socket
server shutting down服务器正在关闭
ping timeoutpingTimeout 客户端在延迟中没有发送 PONG 数据包
transport close连接已关闭(例如:用户失去连接,或网络从 WiFi 更改为 4G)
transport error连接遇到错误

disconnecting

Socket#rooms集不为空时,此事件类似于disconnect但更早触发。

  1. io.on("connection", (socket) => {
  2. socket.on("disconnecting", (reason) => {
  3. for (const room of socket.rooms) {
  4. if (room !== socket.id) {
  5. socket.to(room).emit("user has left", socket.id);
  6. }
  7. }
  8. });
  9. });

注意:这些事件以及connect, connect_error, newListenerremoveListener是不应在您的应用程序中使用的特殊事件:

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

完整API

可以在此处找到 Socket 实例公开的完整 API 。