> ## Documentation Index
> Fetch the complete documentation index at: https://docs.arupa.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Socket.IO

> 在插件中处理 Socket.IO 命名空间和事件。

Socket.IO 允许插件通过内核的 Socket.IO 服务器接收和发送实时事件。
它可用于需要持久客户端连接、事件通知或服务器端更新的交互式功能。

插件在注册期间声明命名空间及其处理的事件。

内核拥有 Socket.IO 连接，应用访问策略，
并将声明的事件转发给插件。

## 声明命名空间

命名空间声明包含：

| 属性             | 含义                              |
| -------------- | ------------------------------- |
| `name`         | Socket.IO 命名空间，例如 `/my-plugin`。 |
| `events`       | 插件在该命名空间中接受的事件名称。               |
| `access`       | 命名空间的可选访问策略。                    |
| `event_access` | 针对单个事件的可选访问策略。                  |

具体的声明语法取决于编程语言和生成的 SDK。以下

与语言无关的声明形式如下：

```text theme={null}
name: /my-plugin
events: [update, refresh]
access: <可选策略>
```

命名空间名称在运行的插件中必须是唯一的。
使用插件拥有的命名空间，通常位于插件特定的路径下，以避免冲突。

## 连接到命名空间

客户端连接到插件声明的命名空间。内核在连接握手期间检查插件范围和命名空间访问策略。

如果任何策略拒绝用户，则连接将被拒绝。
当没有运行的插件拥有该命名空间时，该命名空间不可用。
停止拥有该命名空间的插件会断开现有套接字与该命名空间的连接。
未来的连接将被拒绝，直到插件重新启动。

## 接收事件

只有 `events` 中列出的事件名称才会转发给插件。未声明的事件会被内核忽略。
发送到插件的事件是 Protobuf `SocketEvent` 消息。
请参阅[Protobuf](./proto) 文档了解共享契约。

其字段如下：

| 字段          | Protobuf 类型 | 定义               |
| ----------- | ----------- | ---------------- |
| `namespace` | `string`    | 接收事件的命名空间。       |
| `event`     | `string`    | 事件名称。            |
| `socket_id` | `string`    | 发送事件的套接字 ID。     |
| `payload`   | `bytes`     | 事件参数的 JSON 编码数组。 |
| `user`      | `User`      | 已验证用户（如果可用）。     |

内核不会对 `payload` 强制执行应用程序模式。
请在客户端和插件之间定义有效负载结构，然后在插件中对其进行解码和验证。
由于有效载荷是一个数组，因此包含一个对象参数的事件在概念上可以表示为：

```json theme={null}
[{"id": 42, "enabled": true}]
```

未经身份验证的事件没有 `user` 值。`User` 消息包含一个字符串 `username` 和一个字符串列表 `groups`。

## 发送事件

插件使用 emit 指令发送事件。emit 指令包含：

| 字段          | Protobuf 类型 | 定义                          |
| ----------- | ----------- | --------------------------- |
| `namespace` | `string`    | 要发送事件的命名空间。                 |
| `target`    | `string`    | 定向 emit 的套接字 ID。留空则广播到命名空间。 |
| `event`     | `string`    | 发送给客户端的事件名称。                |
| `payload`   | `bytes`     | 事件参数的 JSON 编码数组。            |

事件处理程序可以在其响应中返回一个或多个 emit 指令。
这是可移植的请求/响应表单。对于 WASM 插件来说，
这是响应传入事件的常规方式，因为处理程序无法主动推送事件。
gRPC 插件也可以使用宿主的 emit 功能，当需要在当前处理程序调用之外发送事件时。

对于定向事件，请将 `target` 设置为接收套接字的 ID。
对于命名空间广播，请将 `target` 留空：

```text theme={null}
namespace: /my-plugin
target: <套接字 ID> # 定向事件
event: update
payload: [{"status": "ready"}]
```

相同的有效负载规则也适用于已发出的事件：`payload` 必须包含一个 JSON 参数数组。
空有效负载会发出一个不带任何参数的事件。

## 访问控制

内核会在连接时和事件发生时评估访问权限：

1. 插件范围策略必须允许用户。
2. 命名空间策略必须允许用户。
3. 如果事件在 `event_access` 中有条目，则该事件策略也必须允许用户访问。

所有适用的策略都必须允许该操作。命名空间策略不会覆盖更严格的插件级策略，
事件策略也不会将受限命名空间设为公共命名空间。

当套接字连接后事件被拒绝时，内核会向该套接字发出`error` 事件。
该错误会标识是否需要身份验证或用户是否被禁止访问。该事件不会转发给插件。

有关策略模型，请参阅[访问控制](../kernel/config-access)。

## 命名空间冲突和生命周期

一个命名空间一次只能有一个拥有它的插件。
如果另一个正在运行的插件已经拥有该命名空间，内核不会附加新插件的命名空间声明。

命名空间冲突仅影响该声明：插件的启动，但内核将其标记为“降级”。其他不冲突的资源可以保持可用。

解决冲突并重启插件以声明命名空间。停止插件会释放其命名空间所有权并断开所有连接到该命名空间的套接字。

重启插件会替换所有者并允许客户端再次连接。

## 事件处理检查清单

加载插件之前，请验证：

* 命名空间以 `/` 开头，并且对插件是唯一的；
* 插件期望的每个事件都列在 `events` 中；
* 客户端和插件都同意每个有效负载都是一个 JSON 参数数组；
* 插件使用有效负载之前会对其进行验证；
* 命名空间和事件访问策略保护预期用户；以及
* 发出的事件使用正确的命名空间和目标。

使用 Socket.IO 实现面向连接、事件驱动的行为。
使用[HTTP 传输](./transport-http) 进行请求/响应交互，
使用[静态传输](./transport-static) 进行可以直接提供的文件传输。
