WSS协议示例 AIWROK 版websocket
WSS协议示例 AIWROK 版websocket/**
* WSS 协议示例 - AIWROK 版
*
* 使用 AIWROK 原生 websocket 对象实现 ws:// 和 wss://(TLS 加密)连接
* 支持:双向通信、心跳保活、断线重连、JSON 消息收发
*
* AIWROK 原生 WebSocket API:
* new websocket() - 创建实例
* ws.setSocketID(String) - 设置唯一ID
* ws.event(onOpen, onMsg, onErr, onClose) - 挂载四个回调
* ws.connet(String) - 连接服务器(注意是双n)
* ws.send(String) - 发送消息
* ws.close() - 关闭连接
*
* ws:// 和 wss:// 的区别:
* wss:// 走 TLS 加密通道,数据传输安全
* AIWROK 的 websocket 对象会自动处理 TLS 握手
*
* 依赖:AIWROK 原生 websocket 对象,零依赖
*
* 使用方法:
* 1. 修改 CFG.serverUrl 为你的 wss:// 地址
* 2. AIWROK 中直接运行本脚本
* 3. 日志窗口查看连接状态和收发消息
*/
// ========== 配置 ==========
var CFG = {
// WSS 服务器地址(TLS 加密)
// wss:// 走加密通道,ws:// 走明文通道
serverUrl: "wss://echo.websocket.org/ws",
// 心跳间隔(秒),定时发 PING 保持连接
heartbeatIntervalSec: 15,
// 重连配置
reconnectIntervalMs: 3000, // 重连基础间隔(毫秒)
maxReconnectAttempts: 999, // 最大重连次数(999=无限)
reconnectMultiplier: 1.5, // 指数退避倍数
maxReconnectDelayMs: 30000, // 最大重连延迟(毫秒)
// 调试日志
debug: true,
// 脚本运行时长(毫秒),0=无限
runDuration: 0
};
// ========== 全局状态 ==========
var G = {
ws: null, // websocket 实例
connected: false, // 是否已连接
reconnectCount: 0, // 已重连次数
currentDelay: CFG.reconnectIntervalMs,// 当前重连延迟
running: true, // 运行标志
startTime: 0, // 启动时间
msgSentCount: 0, // 已发送消息数
msgRecvCount: 0, // 已接收消息数
lastPongAt: 0, // 上次收到PONG时间
heartbeatTimer: null // 心跳定时器
};
// ========== 时间工具 ==========
function nowMs() {
return (new Date()).getTime();
}
function nowSec() {
return Math.floor(nowMs() / 1000);
}
// ========== 设备信息 ==========
function getDeviceInfo() {
var info = { imei: "", brand: "", model: "", screen: "" };
try { info.imei = device.getIMEI() || ""; } catch (e) {}
try { info.brand = device.getBrand() || ""; } catch (e) {}
try { info.model = device.getModel() || ""; } catch (e) {}
try { info.screen = screen.getScreenWidth() + "x" + screen.getScreenHeight(); } catch (e) {}
return info;
}
// ========== 消息发送 ==========
function sendMsg(data) {
if (!G.connected || !G.ws) {
printl(" 未连接,无法发送");
return false;
}
try {
var raw;
if (typeof data === "string") {
raw = data;
} else {
raw = JSON.stringify(data);
}
G.ws.send(raw);
G.msgSentCount++;
printl(" " + raw.slice(0, 200));
return true;
} catch (e) {
printl(" " + "发送异常: " + e.message);
return false;
}
}
// ========== 处理收到的消息 ==========
function handleMessage(raw) {
G.msgRecvCount++;
printl(" " + raw.slice(0, 200));
var obj = null;
try {
obj = JSON.parse(raw);
} catch (e) {
printl(" " + "非 JSON 消息: " + raw.slice(0, 100));
return;
}
// 根据消息类型处理业务逻辑
if (obj.type === "WELCOME") {
printl(" " + "服务器欢迎: " + (obj.msg || ""));
} else if (obj.type === "PING" || obj.type === "ping") {
// 服务端心跳,回 PONG
G.lastPongAt = nowMs();
sendMsg({ type: "PONG", ts: nowSec() });
} else if (obj.type === "PONG" || obj.type === "pong") {
// 收到 PONG,更新心跳时间
G.lastPongAt = nowMs();
printl(" 收到 PONG");
} else if (obj.type === "CMD_SCREENSHOT") {
handleScreenshotCmd(obj);
} else if (obj.type === "CMD_CLICK") {
handleClickCmd(obj);
} else if (obj.type === "CMD_STATUS") {
handleStatusCmd(obj);
} else {
printl(" " + "未处理的消息类型: " + obj.type);
}
}
// ========== 业务指令:截图 ==========
function handleScreenshotCmd(obj) {
try {
var bitmap = screen.screenShotFull();
if (!bitmap) {
sendMsg({ type: "CMD_RESP", cmd: "CMD_SCREENSHOT", ok: false, msg: "截图返回空" });
return;
}
var base64 = "" + bitmap.toBase64();
try { bitmap.recycle(); } catch (e) {}
sendMsg({
type: "CMD_RESP",
cmd: "CMD_SCREENSHOT",
ok: true,
cmdId: obj.cmdId,
data: { base64: base64, length: base64.length }
});
} catch (e) {
sendMsg({ type: "CMD_RESP", cmd: "CMD_SCREENSHOT", ok: false, msg: e.message });
}
}
// ========== 业务指令:点击 ==========
function handleClickCmd(obj) {
try {
var x = parseInt(obj.data.x, 10);
var y = parseInt(obj.data.y, 10);
if (isNaN(x) || isNaN(y)) {
sendMsg({ type: "CMD_RESP", cmd: "CMD_CLICK", ok: false, msg: "坐标无效" });
return;
}
if (typeof action !== "undefined") {
action.click(x, y);
} else if (typeof hid !== "undefined") {
hid.click(x, y);
}
sendMsg({ type: "CMD_RESP", cmd: "CMD_CLICK", ok: true, cmdId: obj.cmdId });
} catch (e) {
sendMsg({ type: "CMD_RESP", cmd: "CMD_CLICK", ok: false, msg: e.message });
}
}
// ========== 业务指令:状态查询 ==========
function handleStatusCmd(obj) {
var info = getDeviceInfo();
info.type = "CMD_RESP";
info.cmd = "CMD_STATUS";
info.ok = true;
info.cmdId = obj.cmdId;
info.runtimeSec = nowSec() - G.startTime;
info.msgSentCount = G.msgSentCount;
info.msgRecvCount = G.msgRecvCount;
info.reconnectCount = G.reconnectCount;
sendMsg(info);
}
// ========== 心跳定时器 ==========
function startHeartbeat() {
stopHeartbeat();
var hbMs = CFG.heartbeatIntervalSec * 1000;
try {
G.heartbeatTimer = setTimeout(function tick() {
try {
if (!G.running || !G.connected) return;
// 检查心跳超时
if (G.lastPongAt > 0 && (nowMs() - G.lastPongAt) > hbMs + 10000) {
printl(" 心跳超时,主动断开重连");
try { G.ws.close(); } catch (e) {}
return;
}
sendMsg({ type: "PING", ts: nowSec() });
G.heartbeatTimer = setTimeout(tick, hbMs);
} catch (e) {
printl(" " + "心跳异常(已兜住): " + e.message);
G.heartbeatTimer = setTimeout(tick, hbMs);
}
}, hbMs);
} catch (e) {
// setTimeout 不存在时用 runTime.setTimeout 兜底
try {
G.heartbeatTimer = runTime.setTimeout(function tick() {
try {
if (!G.running || !G.connected) return;
if (G.lastPongAt > 0 && (nowMs() - G.lastPongAt) > hbMs + 10000) {
printl(" 心跳超时,主动断开重连");
try { G.ws.close(); } catch (e2) {}
return;
}
sendMsg({ type: "PING", ts: nowSec() });
G.heartbeatTimer = runTime.setTimeout(tick, hbMs);
} catch (e3) {
G.heartbeatTimer = runTime.setTimeout(tick, hbMs);
}
}, hbMs);
} catch (e4) {}
}
}
function stopHeartbeat() {
if (G.heartbeatTimer) {
try { clearTimeout(G.heartbeatTimer); } catch (e) {}
try { runTime.stopTimeout(G.heartbeatTimer); } catch (e) {}
G.heartbeatTimer = null;
}
}
// ========== WebSocket 回调函数 ==========
// 连接成功
function wsOnConnected() {
G.connected = true;
G.reconnectCount = 0;
G.currentDelay = CFG.reconnectIntervalMs;
G.lastPongAt = nowMs();
printl(" 连接成功 (onConnected) — TLS 握手完成");
// 启动心跳
startHeartbeat();
// 连接成功后发送注册消息
var info = getDeviceInfo();
sendMsg({
type: "REGISTER",
device: info,
ts: nowSec()
});
}
// 收到文本消息
function wsOnTextMessage(msg) {
var raw;
try { raw = String(msg); } catch (e) { raw = "" + msg; }
handleMessage(raw);
}
// 连接错误
function wsOnConnectError(err) {
G.connected = false;
var errMsg = "unknown";
try { errMsg = String(err); } catch (e) {}
if (!errMsg || errMsg === "null" || errMsg === "undefined") {
errMsg = "连接错误(无详细信息)";
}
printl(" " + "连接错误 (onConnectError): " + errMsg);
stopHeartbeat();
scheduleReconnect();
}
// 连接断开
function wsOnDisconnected() {
G.connected = false;
printl(" 连接断开 (onDisconnected)");
stopHeartbeat();
scheduleReconnect();
}
// ========== 建立连接 ==========
function connect() {
// 先清理旧连接
if (G.ws) {
try { G.ws.close(); } catch (e) {}
G.ws = null;
}
G.connected = false;
printl(" 正在连接: " + CFG.serverUrl + "(重连 " + G.reconnectCount + "/" + CFG.maxReconnectAttempts + ")");
// 创建 AIWROK 原生 websocket 实例
var ws = null;
try {
ws = new websocket();
} catch (e) {
printl(" " + "new websocket() 失败: " + e.message);
scheduleReconnect();
return;
}
G.ws = ws;
// 设置 SocketID(唯一标识)
try {
ws.setSocketID("aiwrok_" + nowMs());
} catch (e) {
printl(" " + "setSocketID 失败(非致命): " + e.message);
}
// 挂载回调事件
var eventOk = false;
try {
ws.event(wsOnConnected, wsOnTextMessage, wsOnConnectError, wsOnDisconnected);
eventOk = true;
printl(" ws.event() 挂事件成功");
} catch (e1) {
printl(" ws.event() 失败: " + e1.message + " — 尝试属性赋值");
}
// 如果 event() 不行,用属性赋值兜底
if (!eventOk) {
try {
ws.onConnected = wsOnConnected;
ws.onTextMessage = wsOnTextMessage;
ws.onConnectError = wsOnConnectError;
ws.onDisconnected = wsOnDisconnected;
printl(" 属性赋值挂事件成功");
} catch (e2) {
printl(" " + "两种挂事件方式都失败: " + e2.message);
scheduleReconnect();
return;
}
}
// 发起连接(注意: connet 双n)
try {
ws.connet(CFG.serverUrl);
printl(" connet() 已调用");
} catch (e3) {
printl(" connet() 异常: " + e3.message + " — 尝试 connect()");
try {
ws.connect(CFG.serverUrl);
} catch (e4) {
printl(" " + "connect() 也失败: " + e4.message);
scheduleReconnect();
}
}
}
// ========== 重连调度 ==========
function scheduleReconnect() {
if (!G.running) return;
if (G.reconnectCount >= CFG.maxReconnectAttempts) {
printl(" 已达最大重连次数 " + CFG.maxReconnectAttempts + ",停止");
return;
}
G.reconnectCount++;
// 指数退避 + 随机抖动
G.currentDelay = Math.min(
G.currentDelay * CFG.reconnectMultiplier,
CFG.maxReconnectDelayMs
);
var jitter = Math.floor(G.currentDelay * (0.8 + Math.random() * 0.4));
printl(" 重连 " + G.reconnectCount + "/" + CFG.maxReconnectAttempts +
" — " + (jitter / 1000).toFixed(1) + "s 后重试");
try {
setTimeout(function () {
if (G.running) {
try { connect(); } catch (e) {
printl(" " + "重连回调异常(已兜住): " + e.message);
scheduleReconnect();
}
}
}, jitter);
} catch (e) {
try {
runTime.setTimeout(function () {
if (G.running) {
try { connect(); } catch (e2) {
printl(" " + "重连回调异常(已兜住): " + e2.message);
scheduleReconnect();
}
}
}, jitter);
} catch (e3) {}
}
}
// ========== 关闭清理 ==========
function cleanup() {
G.running = false;
stopHeartbeat();
if (G.ws) {
try { G.ws.close(); } catch (e) {}
G.ws = null;
}
G.connected = false;
printl(" 资源已清理");
}
// ========== 主程序 ==========
function main() {
try {
G.startTime = nowSec();
printl(" ==========================================");
printl(" WSS WebSocket 客户端 (AIWROK原生) 启动");
printl(" " + "服务器: " + CFG.serverUrl);
printl(" 心跳间隔: " + CFG.heartbeatIntervalSec + "s");
printl(" 最大重连: " + CFG.maxReconnectAttempts + " 次");
printl(" " + "设备信息: " + JSON.stringify(getDeviceInfo()));
printl(" ==========================================");
// 发起首次连接
connect();
// 主线程保活(链式 setTimeout,非阻塞)
var tick = 0;
(function keepAlive() {
try {
if (!G.running) {
printl(" 主线程保活退出");
return;
}
tick++;
// 每 20 秒打印一次状态
if (tick % 20 === 0) {
printl(" " + "[保活] 已运行=" + (nowSec() - G.startTime) +
"s 连接=" + G.connected +
" 发送=" + G.msgSentCount +
" 接收=" + G.msgRecvCount +
" 重连=" + G.reconnectCount);
}
} catch (e) {
printl(" " + "保活异常(已兜住): " + e.message);
}
try { setTimeout(keepAlive, 1000); }
catch (e) {
try { runTime.setTimeout(keepAlive, 1000); } catch (e2) {}
}
})();
// 如果设置了运行时长,到时自动退出
if (CFG.runDuration > 0) {
sleep.millisecond(CFG.runDuration);
cleanup();
}
// 否则无限运行(靠保活循环维持)
} catch (e) {
printl(" " + "主程序异常: " + e.message + "\n" + (e.stack || ""));
try { cleanup(); } catch (e2) {}
}
}
// ========== 启动 ==========
main();
页:
[1]