Files
TrulyMEM-TrueHumanMEM/trulymem-core/src/main/ets/components/RconClient.ets

209 lines
6.5 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { socket } from '@kit.NetworkKit';
import { BusinessError } from '@kit.BasicServicesKit';
import {RConResult,parseRCONBuffer,buildPacket,GetAuth} from '../components/tools';
type RConCallback = (result: RConResult) => void;
export class RConClient
{
public isActive: boolean; //是否可用标志位,随tcp状态更改
private tcp: socket.TCPSocket; //socket连接
private ipAddress: socket.NetAddress; //ip地址
private passWord: string; //密码
private isConnecting: boolean = false; // 标记连接是否正在进行中,防止并发连接
private closeHandler: (() => void) | null = null; // 保存 close 事件处理器引用以便清理
private authResolve: (() => void) | null = null; // 认证完成信号
private nextPacketId: number = 1; // 递增的包序号,避免重复
constructor(ip:string,LPort:number,PassWord:string) {
this.isActive = false;
this.tcp = socket.constructTCPSocketInstance();
this.ipAddress = {
address: ip,
port: LPort
}
this.passWord = PassWord;//初始化tcp连接相关配置
}
//连接服务器
async setConnect(timeOut:number,callBack:RConCallback)//异步获取服务器连接
{
if (this.isConnecting) {
// Already connecting, return an error via callback
callBack(RConResult.error("ALREADY_CONNECTING", "A connection attempt is already in progress"));
return;
}
this.isConnecting = true;
let finished = false; // To ensure we set isConnecting to false only once
const setFinished = () => {
if (!finished) {
finished = true;
this.isConnecting = false;
}
};
// 清理旧的事件监听器
this.cleanupListeners();
let connectOptions: socket.TCPConnectOptions = {
address: this.ipAddress,
timeout: timeOut
};
await this.tcp.connect(connectOptions).then(() => {
console.info('Connection established');
let result:RConResult = RConResult.result(undefined,"tcp_ok")
// 注册 close 事件并保存引用
this.closeHandler = () => {
this.isActive = false;
};
this.tcp.on('close', this.closeHandler);
//发送认证信息
GetAuth(this.tcp,this.passWord,3,(result:RConResult)=>{
setFinished();
if(!result.success) {
callBack(result);
// 通知等待的 sendAfterAuth 认证失败
this.notifyAuthComplete();
}
else
{
this.isActive = true;
callBack(result);
// 通知等待的 sendAfterAuth 认证成功
this.notifyAuthComplete();
}
})
}).catch((err: BusinessError) => {
setFinished();
let result = RConResult.error(JSON.stringify({ code: err.code, message: err.message }), "tcp_fail");
callBack(result);
this.notifyAuthComplete();
});
}
/**
* 通知认证流程完成(供 sendAfterAuth 等待机制使用)
*/
private notifyAuthComplete(): void {
if (this.authResolve) {
this.authResolve();
this.authResolve = null;
}
}
/**
* 获取下一个递增包序号(跳过 -1,因为 -1 在 RCON 协议中表示认证失败)
*/
private getNextPacketId(): number {
const id = this.nextPacketId;
this.nextPacketId++;
if (this.nextPacketId === -1 || this.nextPacketId > 2147483646) {
this.nextPacketId = 1;
}
return id;
}
/**
* 清理所有事件监听器
*/
private cleanupListeners(): void {
this.tcp.off("close");
this.tcp.off("message");
this.closeHandler = null;
}
async sendMessage(message:string,type:number,callBack:RConCallback)
{//发送一条指令
if(!this.isActive)//错误重连机制
{
await this.setConnect(1000,(result:RConResult)=>{
if (!result.success) {
// Connection failed, call the outer callback with the error
callBack(result);
return;
}
// 重连后需要重新认证,不能直接发送命令
// 等待认证完成后再发送消息
this.sendAfterAuth(message, type, callBack);
})
}
else {
this.sendWithResponse(message, type, callBack);
}
}
/**
* 发送消息并等待响应(正常路径)
*/
private sendWithResponse(message: string, type: number, callBack: RConCallback): void {
const packetId = this.getNextPacketId();
const messageHandler = (info: socket.SocketMessageInfo) => {
const buffer: ArrayBuffer = info.message;
const result = parseRCONBuffer(buffer);
callBack(result);
this.tcp.off("message", messageHandler);
};
this.tcp.on("message", messageHandler);
let sendOption: socket.TCPSendOptions = { data: buildPacket(packetId, type, message) };
this.tcp.send(sendOption).catch((err: BusinessError) => {
let result = RConResult.error(JSON.stringify({ code: err.code, message: err.message }), "send_auth_fail");
callBack(result);
this.tcp.off("message", messageHandler);
this.setDisconnect();
});
}
/**
* 重连后等待认证完成再发送消息
* 使用 Promise 等待机制替代轮询
*/
private sendAfterAuth(message: string, type: number, callBack: RConCallback): void {
// 创建认证等待 Promise(最多 5 秒)
const authPromise = new Promise<boolean>((resolve) => {
this.authResolve = () => resolve(this.isActive);
// 超时兜底
setTimeout(() => {
if (this.authResolve) {
this.authResolve = null;
resolve(false);
}
}, 5000);
});
authPromise.then((authSuccess: boolean) => {
if (authSuccess) {
this.sendWithResponse(message, type, callBack);
} else {
let result = RConResult.error("AUTH_TIMEOUT", "Authentication timed out after reconnect");
callBack(result);
}
});
}
async resetServer(ip:string,password:string,port:number,timeout:number,callback:RConCallback)
{//重新设置服务器配置
this.ipAddress = {
address:ip,
port:port
}
this.passWord = password;
this.setDisconnect();//断开放弃使用的tcp连接
await this.setConnect(timeout,callback);
}
setDisconnect()
{
// 清理所有事件监听器
this.cleanupListeners();
// 断链
try {
this.tcp.close();
} catch {
// Socket already closed
}
this.isActive = false;
}
}