Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix websocket connection bugs #41

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

80 changes: 79 additions & 1 deletion src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const forceCloseEvents = [
RequestApi.Logout,
CbEvents.OnKickedOffline,
CbEvents.OnUserTokenExpired,
CbEvents.OnConnectLimitFailed,
];

function isEventInCallbackEvents(event: string): event is CbEvents {
Expand All @@ -51,6 +52,11 @@ class OpenIMSDK
private wsManager?: WebSocketManager;
private requestMap = new Map<string, PromiseMap>();

private requestTimeoutMap = new Map<string, number>();
private timeoutHandler?: any = undefined;
private timeoutTimeout = 60000;
private timeoutInterval = 15000;

constructor() {
super();
Object.assign(this, setupUser(this));
Expand All @@ -60,6 +66,64 @@ class OpenIMSDK
Object.assign(this, setupConversation(this));
}

private rejectAllRequest = (event: (RequestApi | CbEvents)) => {
console.debug("============ rejectAllRequest ", event)
this.requestTimeoutMap.forEach((time, operationID) => {
const errResp: WsResponse = {
event: event,
errCode: -1,
errMsg: 'request all reject',
data: '',
operationID: operationID
};
const promiseHandlers = this.requestMap.get(operationID);
promiseHandlers?.reject(errResp)
})
}

private startTimeoutCleaner = () => {
if(this.timeoutHandler) {
return;
}
console.debug("============ startTimeoutCleaner")
const startTime = new Date().getTime()
// 定时器清理超时请求
this.timeoutHandler = setInterval(() => {
console.debug("============ check Timeout operations ", startTime)
const outTime = new Date().getTime() - this.timeoutTimeout;
const operationIDTimeoutList : string[]= [];
// 查找超时请求
this.requestTimeoutMap.forEach((time, operationID) => {
if(outTime >= time) {
operationIDTimeoutList.push(operationID);
}
})
// 清理超时请求
operationIDTimeoutList.forEach((operationID) => {
const errResp: WsResponse = {
event: CbEvents.OnRequestTimeout,
errCode: -1,
errMsg: 'request timeout',
data: '',
operationID: operationID
};
const promiseHandlers = this.requestMap.get(operationID);
promiseHandlers?.reject(errResp)
console.debug("============ promise Timeout reject ", operationID)
this.requestMap.delete(operationID)
this.requestTimeoutMap.delete(operationID)
})
}, this.timeoutInterval)
};

private stopTimeoutCleaner = () => {
console.debug("============ stopTimeoutCleaner")
if(this.timeoutHandler) {
clearInterval(this.timeoutHandler)
this.timeoutHandler = undefined
}
};

private sendRequest = <T>(requestObj: WsRequest): Promise<WsResponse<T>> => {
return new Promise((resolve, reject) => {
if (!this.wsManager) {
Expand All @@ -77,6 +141,11 @@ class OpenIMSDK
resolve: resolve as unknown as (value: WsResponse<unknown>) => void,
reject,
});
this.requestTimeoutMap.set(requestObj.operationID, new Date().getTime())
// // vvv 纯测试
// if(RequestApi.GetAdvancedHistoryMessageList === requestObj.reqFuncName) {
// return;
// }
this.wsManager?.sendMessage(requestObj);
});
};
Expand Down Expand Up @@ -126,6 +195,8 @@ class OpenIMSDK
} catch (error) {}

if (forceCloseEvents.includes(data.event)) {
this.rejectAllRequest(data.event)
this.stopTimeoutCleaner()
this.wsManager?.close();
this.wsManager = undefined;
}
Expand All @@ -134,6 +205,7 @@ class OpenIMSDK
this.emit(data.event, data);
if (forceCloseEvents.includes(data.event)) {
this.requestMap.clear();
this.requestTimeoutMap.clear();
}
return;
}
Expand All @@ -143,15 +215,17 @@ class OpenIMSDK
data.errCode === 0 ? promiseHandlers.resolve : promiseHandlers.reject;
promiseHandler(data);
this.requestMap.delete(data.operationID);
this.requestTimeoutMap.delete(data.operationID)
}
if (forceCloseEvents.includes(data.event)) {
this.requestMap.clear();
this.requestTimeoutMap.clear();
}
};

private handleReconnectSuccess = () => {
if (!this.userID) return;

this.startTimeoutCleaner()
this.sendRequest({
data: JSON.stringify([this.userID, this.token]),
operationID: uuid(),
Expand All @@ -164,6 +238,7 @@ class OpenIMSDK
params: LoginParams,
operationID = uuid()
): Promise<WsResponse> => {

if (this.wsManager) {
return Promise.resolve({
data: '',
Expand All @@ -182,9 +257,11 @@ class OpenIMSDK
this.handleMessage,
this.handleReconnectSuccess
);

try {
await this.wsManager.connect();
} catch (error) {
this.wsManager = undefined
return Promise.reject({
data: '',
operationID,
Expand All @@ -193,6 +270,7 @@ class OpenIMSDK
event: RequestApi.Login,
});
}
this.startTimeoutCleaner();
return this.sendRequest({
data: JSON.stringify([params.userID, params.token]),
operationID,
Expand Down
3 changes: 3 additions & 0 deletions src/constant/callback.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
export enum CbEvents {
OnConnectLimitFailed = 'OnConnectLimitFailed',
OnRequestTimeout = 'OnRequestTimeout',

OnConnectFailed = 'OnConnectFailed',
OnConnectSuccess = 'OnConnectSuccess',
OnConnecting = 'OnConnecting',
Expand Down
22 changes: 21 additions & 1 deletion src/utils/webSocketManager.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { WsRequest, WsResponse } from '@/types/entity';
import { utf8Decode, utf8Encode } from './textCoder';
import { RequestApi } from '@/constant/api';
import { CbEvents } from '@/constant/callback';

type AppPlatform = 'unknow' | 'web' | 'uni' | 'wx';

Expand Down Expand Up @@ -52,6 +53,7 @@ class WebSocketManager {
};

public connect = (): Promise<void> => {
console.debug("============ webSocketManager connect ", new Date().getTime())
if (this.platformNamespace === 'unknow') {
return Promise.reject(new Error('WebSocket is not supported'));
}
Expand Down Expand Up @@ -111,8 +113,18 @@ class WebSocketManager {
setTimeout(() => onWsClose(), 100);
return;
}
setTimeout(() => this.connect(), this.reconnectInterval);
setTimeout(() => this.connect().catch(() => onWsClose(event)), this.reconnectInterval);

this.reconnectAttempts++;
} else {
const errResp: WsResponse = {
event: CbEvents.OnConnectLimitFailed,
errCode: -2,
errMsg: 'WebSocket is not open. Message not sent.',
data: '',
operationID: CbEvents.OnConnectLimitFailed
};
this.onMessage(errResp)
}
};

Expand Down Expand Up @@ -158,6 +170,14 @@ class WebSocketManager {
}
} else {
console.error('WebSocket is not open. Message not sent.');
const errResp: WsResponse = {
event: message.reqFuncName,
errCode: -1,
errMsg: 'WebSocket is not open. Message not sent.',
data: '',
operationID: message.operationID
};
this.onMessage(errResp)
}
};

Expand Down