Files
ts-mobile-go/docs/流程/01_连接服务器.md
T

245 lines
11 KiB
Markdown
Raw Normal View History

2026-07-20 19:01:03 +08:00
# 连接服务器
> 用户流程:输入服务器地址 → 输入昵称 → 输入密码(可选) → 点击进入服务器 → 连接成功 → 首次同步
> 对应程序流程:准备配置 → TSBridge.connect() → EventBus 收到 Connected 事件 → 首次同步
---
## 一、生命周期
### 1.1 完整生命周期
```mermaid
flowchart TD
A[准备配置<br/>地址、昵称、密码] --> B[TSBridge.connect<br/>组装连接参数并注册 EventCallback]
B --> C[Go SDK Connect<br/>通过 gomobile 发起 UDP 会话与协议握手]
C --> D{连接请求是否成功<br/>判断 connect 返回值}
D -- 否 --> E[连接失败处理<br/>记录错误并释放客户端资源]
D -- 是 --> F{是否已收到 Connected 事件<br/>等待 EventBus 连接就绪事件}
F -- 否 --> G[等待失败处理<br/>处理超时或握手异常]
F -- 是 --> H[EventBus Connected 事件<br/>接收连接成功事件]
H --> I[首次同步<br/>请求频道、成员和自身身份信息]
I --> J[运行就绪<br/>允许发送命令、聊天和语音]
J --> K[持续接收 EventBus 事件<br/>归并成员、移动、消息和 Poke]
K --> J
J --> L{结束原因<br/>区分主动断开与被动中断}
L -- 用户主动退出 --> M[TSBridge.disconnect<br/>发送优雅关闭请求]
L -- 网络或服务端中断 --> N[EventBus Disconnected 事件<br/>接收断开原因并停止写操作]
L -- 自己被踢出 --> O[EventBus Kicked 事件<br/>接收频道踢或服务器踢原因]
M --> P[清理运行资源<br/>停止语音、取消请求并清空会话状态]
N --> P
O --> P
P --> Q[生命周期结束<br/>不再承担当前会话请求]
```
### 生命周期约束
1. `TSBridge.connect()` 发起连接,连接就绪通过 EventBus `Connected` 事件确认。
2. EventBus 事件监听应在 `connect()` 之前开始收集,避免连接早期事件无人接收。
3. `connect()` 成功只表示连接流程已启动;发送普通命令前必须等待 `Connected` 事件。
4. `Connected` 是 SDK 推送的已连接事实,可作为首次同步入口。
5. `TSBridge.disconnect()` 用于主动优雅退出;`Disconnected` 事件用于观察最终断开结果或异常断开。
6. `Kicked` 事件来源于 `notifyclientleftview` 的特定原因,必须与普通成员离开区分。
7. 断开后应停止语音、拒绝新命令,并清理只属于当前会话的状态。
### 1.2 生命周期阶段与允许操作
| 阶段 | 允许操作 | 禁止或不建议操作 | 中文说明 |
| --- | --- | --- | --- |
| 构造前 | 准备配置 | 调用任何 TSBridge 方法 | 连接尚未建立 |
| 已配置 | 注册 EventBus 事件收集、读取本地配置 | 发送服务器命令 | 尚未建立网络会话 |
| 连接中 | 等待 `Connected` 事件、响应取消 | `getClientsJSON()``moveToChannel()` 等业务命令 | 握手尚未确认完成 |
| 已连接 | 同步频道和成员、发送聊天、发送语音 | 重复调用 `connect()` | SDK 已具备业务通信条件 |
| 断开中 | 停止上层写操作、等待清理 | 发起新业务命令 | 会话正在关闭 |
| 已断开 | 释放资源或重新连接 | 复用失效会话发送命令 | 旧会话不再可信 |
---
## 二、初始化配置
### 2.1 初始化配置结构
```mermaid
flowchart TD
A[应用启动配置<br/>收集身份、地址、昵称和可选参数] --> B[Identity<br/>提供 TeamSpeak 加密身份]
A --> C[服务器地址 addr<br/>提供域名、IP 或 TSDNS 地址]
A --> D[昵称 nickname<br/>提供当前连接显示名称]
A --> E[连接选项<br/>提供可选运行能力]
E --> E3[serverPassword<br/>设置服务器连接密码]
E --> E4[defaultChannel<br/>设置连接后默认进入的频道名称]
E --> E5[defaultChannelPassword<br/>设置默认频道密码]
B --> F[TSBridge.connect<br/>组装参数并通过 gomobile 发起连接]
C --> F
D --> F
E3 --> F
E4 --> F
E5 --> F
F --> G[注册 EventCallback<br/>JNI 回调转 EventBus 事件]
G --> H[Go SDK Connect<br/>UDP 会话与协议握手]
```
### 2.2 推荐初始化顺序
```kotlin
// TSBridge.connect() 内部注册 JNI EventCallback
// 将所有 Go 回调转为 EventBus.emit() 事件。
// ViewModel 通过 EventBus.events.collect() 接收事件并更新状态。
// 示例:ServerViewModel 中收集连接事件
viewModelScope.launch {
EventBus.events.collect { event ->
when (event) {
is TSEvent.Connected -> {
// 开始首次同步
performInitialSync()
}
is TSEvent.Disconnected -> {
// 清理会话
handleDisconnect(event.message)
}
else -> { /* 其他事件由对应 ViewModel 处理 */ }
}
}
}
// connect 发起网络连接,Connected 事件确认握手真正完成。
val error = TSBridge.connect(host, nickname, password, defaultChannel, defaultChannelPassword, callbacks)
// error 为空字符串表示连接流程已启动,等待 EventBus Connected 事件
```
### 2.3 配置之间的依赖
| 配置 | 前置条件 | 影响阶段 | 依赖说明 |
| --- | --- | --- | --- |
| `Identity` | 必须可用 | 连接、握手 | 缺少身份无法正确创建客户端 |
| `addr` | 必须非空且可解析 | 地址解析、连接 | 由 SDK 内部解析器解析 |
| `nickname` | 必须满足服务器命名规则 | 握手、上线 | 服务器可能拒绝无效或冲突昵称 |
| `serverPassword` | 服务器启用密码时需要 | 握手 | 密码错误会导致连接失败 |
| `defaultChannel` | 目标频道名称存在 | 连接完成阶段 | SDK 尝试在连接后自动进入该频道 |
| `defaultChannelPassword` | 已设置默认频道且频道有密码 | 进入默认频道 | 单独设置密码而无默认频道没有明确目标 |
---
## 三、状态树
### Client 总状态树(连接相关部分)
```mermaid
flowchart TD
ROOT[Client 生命周期状态树<br/>描述一个连接从创建到结束的完整状态]
ROOT --> U[未配置 Uninitialized<br/>仅准备 Identity 和连接参数]
ROOT --> C[已配置 Configured<br/>EventBus 已注册但尚未连接]
ROOT --> N[连接过程 Connecting<br/>正在解析地址并执行握手]
ROOT --> R[已连接 Connected<br/>握手完成且可以执行业务命令]
U --> U1[配置 Identity<br/>准备加密身份]
U --> U2[配置地址与昵称<br/>准备基础连接参数]
U --> U3[配置连接选项<br/>准备密码和默认频道]
C --> C1[注册 EventBus 事件收集<br/>绑定服务端推送处理器]
C --> C2[等待 connect 调用<br/>尚不能发送业务命令]
N --> N1[Resolving<br/>SDK 内部解析地址]
N --> N2[Handshaking<br/>通过 UDP 建立协议会话]
N --> N3[WaitingReady<br/>等待 Connected 事件]
N --> N4[ConnectFailed<br/>解析、密码、网络或握手失败]
R --> R1[Syncing<br/>通过列表命令建立频道和成员基线]
R --> R2[Ready<br/>允许聊天、移动、查询和语音]
```
---
## 四、时序:构造、连接与首次同步
```mermaid
sequenceDiagram
participant UI as 上层应用<br/>发起连接并展示状态
participant VM as ViewModel<br/>收集 EventBus 事件
participant BRIDGE as TSBridge<br/>管理连接生命周期
participant SDK as Go SDK (gomobile)<br/>管理协议通信
participant TS as TeamSpeak 服务器<br/>执行握手和列表请求
participant STORE as 业务状态仓库<br/>保存频道和成员基线
UI->>BRIDGE: connect(host, nickname, password, ...)
BRIDGE->>SDK: gomobile 调用 Go Connect
SDK->>TS: 连接握手请求<br/>协商身份、密码和会话
TS-->>SDK: 握手响应
SDK-->>BRIDGE: connect 返回 ""
SDK->>BRIDGE: OnConnected 回调<br/>gomobile 转换为 JNI 回调
BRIDGE->>BRIDGE: EventBus.emit(Connected)
BRIDGE->>VM: EventBus SharedFlow<br/>分发 Connected 事件
VM->>VM: 更新连接状态
par 请求频道基线
VM->>BRIDGE: TSBridge.getChannelsJSON()
BRIDGE->>SDK: gomobile 调用 GetChannelsJSON
SDK->>TS: channellist
TS-->>SDK: JSON 字符串
SDK-->>BRIDGE: 频道 JSON
BRIDGE-->>VM: JSON 字符串
VM->>STORE: 解析并保存频道基线
and 请求成员基线
VM->>BRIDGE: TSBridge.getClientsJSON()
BRIDGE->>SDK: gomobile 调用 GetClientsJSON
SDK->>TS: clientlist
TS-->>SDK: JSON 字符串
SDK-->>BRIDGE: 成员 JSON
BRIDGE-->>VM: JSON 字符串
VM->>STORE: 解析并保存成员基线
and 读取自身 ID
VM->>BRIDGE: TSBridge.getClientID()
BRIDGE-->>VM: selfClientID
VM->>STORE: 保存自身 ID
end
VM->>STORE: 原子提交频道、成员和自身 ID
STORE-->>UI: 同步完成<br/>连接状态进入业务就绪
```
---
## 五、事件依赖
### 连接相关前置依赖矩阵
| 操作或事件 | 必须依赖 | 建议依赖 | 依赖失败时的处理 |
| --- | --- | --- | --- |
| `TSBridge.connect` | 地址、昵称 | 已注册 EventBus 事件收集 | 参数无效时不进入连接流程 |
| `connect` 成功 | 已注册 EventBus 事件收集 | — | 返回错误信息并停止等待连接 |
| `Connected` 事件 | 已调用 `connect` | — | 超时后终止本次初始化 |
| 首次同步 | `Connected` 事件 | 同时读取频道、成员和自身 ID | 任一核心请求失败则不标记业务就绪 |
### 事件依赖总图(连接阶段)
```mermaid
flowchart TD
CONFIG[初始化配置完成<br/>地址、昵称已准备] --> REG[注册 EventBus 事件收集<br/>确保早期服务端推送可被处理]
REG --> CONNECT[TSBridge.connect<br/>启动地址解析、UDP 会话和握手]
CONNECT --> CONNECTED[EventBus Connected 事件<br/>SDK 推送连接成功事实]
CONNECTED --> CID[TSBridge.getClientID<br/>读取当前用户客户端 ID]
CONNECTED --> CHANNELS[TSBridge.getChannelsJSON<br/>建立频道基线]
CONNECTED --> CLIENTS[TSBridge.getClientsJSON<br/>建立在线成员基线]
CID --> SELFREADY[当前用户身份就绪<br/>能够识别自己的移动和离开事件]
CHANNELS --> DATAREADY[服务器实体就绪<br/>频道和成员引用关系可被验证]
CLIENTS --> DATAREADY
SELFREADY --> READY[业务就绪<br/>允许移动、聊天和语音]
DATAREADY --> READY
```
### 权威性划分
| 数据 | 推荐权威来源 | 原因 |
| --- | --- | --- |
| 是否完成连接 | EventBus `Connected` 事件 | `connect()` 只负责启动连接 |
| 自身客户端 ID | `TSBridge.getClientID()` | SDK 本地缓存服务器分配的 ID |
| 初始频道列表 | `TSBridge.getChannelsJSON()` 响应 | SDK 未列出频道创建、更新、删除事件 |
| 初始在线成员 | `TSBridge.getClientsJSON()` 响应 | 建立完整在线成员基线 |