fix(reconnect): auto rejoin on socket connect and widen reconnection triggers for mobile foreground resume
- Attempt reconnection on 'disconnected' | 'failed' | 'closed' states (BaseWebRTC) - Relax gating: rejoin when roomId exists and any of isPeerDisconnected, isSocketDisconnected, or socketId changed - Auto re-join room on socket 'connect' if lastJoinedSocketId differs or not in room; send initiator-online for initiators - Track lastJoinedSocketId after successful join and reset isInRoom when socketId changes to bypass early-return - Update flows to document mobile background/foreground reconnection and socketId-based rejoin
This commit is contained in:
@@ -634,9 +634,15 @@ async requestWakeLock(): Promise<void> {
|
||||
**网络切换适应**:
|
||||
|
||||
- **连接检测**:监听 `connectionstatechange` 事件检测网络质量变化
|
||||
- **自动重连**:`iceConnectionState: 'disconnected'` 时触发重连流程
|
||||
- **自动重连**:`connectionState: 'disconnected' | 'failed' | 'closed'` 时均触发重连流程(统一走 attemptReconnection)
|
||||
- **状态恢复**:重连成功后恢复房间状态和传输进度
|
||||
|
||||
**移动端后台/前台切换补充策略**:
|
||||
|
||||
- **socket 连接恢复自动入房**:`socket.on('connect')` 时,若已持有 `roomId` 且(`lastJoinedSocketId !== socket.id` 或 `!isInRoom`),则强制重新 `joinRoom(roomId, isInitiator, isInitiator)`;发送端会自动广播 `initiator-online`,接收端回复 `recipient-ready`。
|
||||
- **身份追踪**:成功 `joinRoom` 后记录 `lastJoinedSocketId = socket.id`,用以检测“后台恢复时 socketId 更换”的情形。
|
||||
- **门槛放宽**:`attemptReconnection` 只要满足“`roomId` 存在,且满足任一:P2P 断开 / socket 断开 / socketId 改变”,即可发起重连;不再强依赖“socket 与 P2P 同时断开”。
|
||||
|
||||
### 重连调试要点
|
||||
|
||||
**关键日志点**:
|
||||
|
||||
@@ -66,6 +66,8 @@ export default class BaseWebRTC {
|
||||
protected wakeLockManager: WakeLockManager;
|
||||
// Graceful disconnect tracking
|
||||
protected gracefullyDisconnectedPeers: Set<string>;
|
||||
// Track last socket.id used to successfully join a room
|
||||
protected lastJoinedSocketId: string | null;
|
||||
|
||||
constructor(config: WebRTCConfig) {
|
||||
this.iceServers = config.iceServers;
|
||||
@@ -94,6 +96,7 @@ export default class BaseWebRTC {
|
||||
this.isPeerDisconnected = false;
|
||||
this.reconnectionInProgress = false;
|
||||
this.wakeLockManager = new WakeLockManager();
|
||||
this.lastJoinedSocketId = null;
|
||||
}
|
||||
// region Logging and Error Handling
|
||||
protected log(
|
||||
@@ -113,9 +116,41 @@ export default class BaseWebRTC {
|
||||
// endregion
|
||||
// Sets up event listeners for the signaling server to handle various signaling messages (connection, ICE candidates, offer, answer, etc.).
|
||||
setupCommonSocketListeners() {
|
||||
this.socket.on("connect", () => {
|
||||
this.socket.on("connect", async () => {
|
||||
this.peerId = this.socket.id; // Save own ID
|
||||
this.isSocketDisconnected = false;
|
||||
this.log("log", `Connected to signaling server, peerId: ${this.peerId}`);
|
||||
|
||||
// Auto re-join if we previously joined a room but socket.id changed
|
||||
const hasRoom = !!this.roomId;
|
||||
const currentSocketId = this.socket.id ?? null;
|
||||
const socketIdChanged =
|
||||
this.lastJoinedSocketId !== null &&
|
||||
this.lastJoinedSocketId !== currentSocketId;
|
||||
|
||||
if (hasRoom && (socketIdChanged || !this.isInRoom)) {
|
||||
// Ensure joinRoom does not early-return
|
||||
if (socketIdChanged) this.isInRoom = false;
|
||||
|
||||
if (!this.reconnectionInProgress) {
|
||||
this.reconnectionInProgress = true;
|
||||
try {
|
||||
const sendInitiatorOnline = this.isInitiator;
|
||||
await this.joinRoom(
|
||||
this.roomId as string,
|
||||
this.isInitiator,
|
||||
sendInitiatorOnline
|
||||
);
|
||||
// Reset flags after successful auto rejoin
|
||||
this.isSocketDisconnected = false;
|
||||
this.isPeerDisconnected = false;
|
||||
} catch (error) {
|
||||
this.fireError("Auto rejoin on socket connect failed", { error });
|
||||
} finally {
|
||||
this.reconnectionInProgress = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.on("error", (error) => {
|
||||
@@ -149,17 +184,25 @@ export default class BaseWebRTC {
|
||||
}
|
||||
protected async attemptReconnection(): Promise<void> {
|
||||
if (this.reconnectionInProgress) return;
|
||||
if (!this.roomId) return;
|
||||
|
||||
if (this.isSocketDisconnected && this.isPeerDisconnected && this.roomId) {
|
||||
// Start reconnection only after both socket and P2P connections are disconnected
|
||||
const currentSocketId = this.socket.id ?? null;
|
||||
const socketIdChanged =
|
||||
this.lastJoinedSocketId !== null &&
|
||||
this.lastJoinedSocketId !== currentSocketId;
|
||||
|
||||
// Widen condition: if either side disconnected or socketId changed, try to rejoin
|
||||
if (this.isPeerDisconnected || this.isSocketDisconnected || socketIdChanged) {
|
||||
this.reconnectionInProgress = true;
|
||||
if (developmentEnv === "development") {
|
||||
postLogToBackend(
|
||||
`Starting reconnection, socket and peer both disconnected. isInitiator:${this.isInitiator}`
|
||||
`Starting reconnection. socketDisc:${this.isSocketDisconnected}, peerDisc:${this.isPeerDisconnected}, socketIdChanged:${socketIdChanged}, isInitiator:${this.isInitiator}`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
// Ensure joinRoom does not early-return
|
||||
if (socketIdChanged) this.isInRoom = false;
|
||||
const sendInitiatorOnline = this.isInitiator;
|
||||
await this.joinRoom(this.roomId, this.isInitiator, sendInitiatorOnline);
|
||||
|
||||
@@ -323,11 +366,15 @@ export default class BaseWebRTC {
|
||||
failed: async () => {
|
||||
this.cleanupExistingConnection(peerId);
|
||||
this.isPeerDisconnected = true;
|
||||
// Attempt to reconnect as well when failed
|
||||
this.attemptReconnection();
|
||||
await this.wakeLockManager.releaseWakeLock();
|
||||
},
|
||||
closed: async () => {
|
||||
this.cleanupExistingConnection(peerId);
|
||||
this.isPeerDisconnected = true;
|
||||
// Attempt to reconnect when closed
|
||||
this.attemptReconnection();
|
||||
await this.wakeLockManager.releaseWakeLock();
|
||||
},
|
||||
// The following must be added to prevent errors
|
||||
@@ -437,6 +484,8 @@ export default class BaseWebRTC {
|
||||
if (response.success) {
|
||||
this.roomId = roomId;
|
||||
this.isInRoom = true;
|
||||
// Record the socket.id used for this successful join
|
||||
this.lastJoinedSocketId = this.socket.id ?? null;
|
||||
if (sendInitiatorOnline) {
|
||||
this.socket.emit("initiator-online", {
|
||||
roomId: this.roomId,
|
||||
|
||||
Reference in New Issue
Block a user