import type { LoggerFriendly } from "#Project/src/log/logger.ts" import { Logger } from "#Project/src/log/logger.ts" import type { BuildEvents, SubscriberEntry } from "#Source/event/index.ts" import { EventManager } from "#Source/event/index.ts" import type { SocketUnitEvents, SocketUnitOptions, SocketUnitSnapshot, SocketUnitStatus, } from "./socket-unit.ts" import { SocketUnit } from "./socket-unit.ts" /** * @description 表示客户端 Socket 进入 ready 态时的事件负载。 */ export interface SocketConnectedEvent { clientId: string | undefined } /** * @description 表示客户端 Socket 离开 ready 态时的事件负载。 */ export interface SocketClosedEvent { clientId: string | undefined } /** * @description 表示客户端 Socket 派发业务消息时的事件负载。 */ export interface SocketMessageEvent { clientId: string message: Message } /** * @description 表示客户端 Socket 对外派发的事件表。 */ export type SocketEvents = BuildEvents<{ connect: (payload: SocketConnectedEvent) => void close: (payload: SocketClosedEvent) => void message: (payload: SocketMessageEvent) => void }> /** * @description 表示客户端 Socket 的公开状态类型。 */ export type SocketStatus = SocketUnitStatus /** * @description 表示客户端 Socket 的构造参数。 */ export interface SocketOptions extends SocketUnitOptions {} /** * @description 此 Socket 类并不直接创建和管理 WebSocket 实例,而是管理很多 SocketUnit, * SocketUnit 是 WebSocket 实例的实际管理者。这样可以做到当 WebSocket 实例 * 出现连接不稳定或断线重连等情况时,Socket 的功能对使用者来说始终是稳定的。 */ export class Socket implements LoggerFriendly { protected readonly options: SocketUnitOptions readonly logger: Logger protected clientId: string | undefined protected legacyUnitSet: Set> protected unit: SocketUnit eventManager: EventManager> protected messageSubscriberEntry!: SubscriberEntry, "message"> protected statusSubscriberEntry!: SubscriberEntry, "status"> protected lastUnitReadyToWork: boolean constructor(options: SocketUnitOptions) { this.options = options this.logger = Logger.fromOptions(options).setDefaultName("Socket") this.clientId = undefined this.legacyUnitSet = new Set() this.unit = this.createUnit() this.lastUnitReadyToWork = this.unit.getSnapshot().isReadyToWork this.eventManager = new EventManager>() this.prepareEventManager() } protected createUnit(): SocketUnit { return new SocketUnit({ ...this.options, logger: this.logger.derive().setName("SocketUnit"), }) } protected replaceCurrentUnit(unit: SocketUnit): void { this.unit = unit this.lastUnitReadyToWork = unit.getSnapshot().isReadyToWork this.prepareEventManager() if (this.clientId !== undefined) { this.unit.setClientId(this.clientId) } } protected prepareEventManager(): void { if (this.messageSubscriberEntry !== undefined) { this.messageSubscriberEntry.unsubscribe() } if (this.statusSubscriberEntry !== undefined) { this.statusSubscriberEntry.unsubscribe() } this.messageSubscriberEntry = this.unit.eventManager.subscribe("message", (message) => { const clientId = this.unit.getSnapshot().clientId if (clientId === undefined) { this.logger.warn("Message event skipped because clientId is not ready.") return } this.eventManager.emit("message", { clientId, message }) }) this.statusSubscriberEntry = this.unit.eventManager.subscribe("status", (status) => { this.handleUnitStatusChange(status) }) } protected handleUnitStatusChange(_status: SocketUnitStatus): void { const previousReadyToWork = this.lastUnitReadyToWork const snapshot = this.unit.getSnapshot() const nextReadyToWork = snapshot.isReadyToWork this.lastUnitReadyToWork = nextReadyToWork if (previousReadyToWork === false && nextReadyToWork === true) { this.eventManager.emit("connect", { clientId: snapshot.clientId }) } if (previousReadyToWork === true && nextReadyToWork === false) { this.eventManager.emit("close", { clientId: snapshot.clientId }) } } /** * @description 设置当前 Socket 对外暴露的 clientId。 */ setClientId(clientId: string): void { this.clientId = clientId this.unit.setClientId(clientId) this.handleUnitStatusChange(this.unit.getStatus()) } /** * @description 返回当前活跃 SocketUnit 的状态。 */ getStatus(): SocketStatus { return this.unit.getStatus() } /** * @description 返回当前活跃 SocketUnit 的快照。 */ getSnapshot(): SocketUnitSnapshot { return this.unit.getSnapshot() } /** * @description 打开当前活跃的 SocketUnit。 */ async open(): Promise { await this.unit.open() } /** * @description 重建当前活跃的 SocketUnit 连接。 */ async reopen(): Promise { await this.unit.reopen() } /** * @description 关闭当前活跃的 SocketUnit 连接。 */ async close(): Promise { await this.unit.close() } /** * @description 异步回收当前 SocketUnit,并切换到新的运行单元。 */ reset(): void { const legacyUnit = this.unit this.legacyUnitSet.add(legacyUnit) void legacyUnit.reset(() => { this.legacyUnitSet.delete(legacyUnit) }) this.replaceCurrentUnit(this.createUnit()) } /** * @description 向当前活跃的 SocketUnit 发送业务消息。 */ sendMessage(message: Message): void { this.unit.sendMessage(message) } }