YYPOST群发软件 发表于 昨天 07:05

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]
查看完整版本: WSS协议示例 AIWROK 版websocket