B2B网络软件

标题: WSS协议示例 AIWROK 版websocket [打印本页]

作者: YYPOST群发软件    时间: 昨天 07:05
标题: WSS协议示例 AIWROK 版websocket
WSS协议示例 AIWROK 版websocket


WSS协议示例 AIWROK 版websocket B2B网络软件

WSS协议示例 AIWROK 版websocket B2B网络软件

WSS协议示例 AIWROK 版websocket B2B网络软件

  1. /**
  2. * WSS 协议示例 - AIWROK 版
  3. *
  4. * 使用 AIWROK 原生 websocket 对象实现 ws:// 和 wss://(TLS 加密)连接
  5. * 支持:双向通信、心跳保活、断线重连、JSON 消息收发
  6. *
  7. * AIWROK 原生 WebSocket API:
  8. *   new websocket()           - 创建实例
  9. *   ws.setSocketID(String)    - 设置唯一ID
  10. *   ws.event(onOpen, onMsg, onErr, onClose) - 挂载四个回调
  11. *   ws.connet(String)         - 连接服务器(注意是双n)
  12. *   ws.send(String)           - 发送消息
  13. *   ws.close()                - 关闭连接
  14. *
  15. * ws:// 和 wss:// 的区别:
  16. *   wss:// 走 TLS 加密通道,数据传输安全
  17. *   AIWROK 的 websocket 对象会自动处理 TLS 握手
  18. *
  19. * 依赖:AIWROK 原生 websocket 对象,零依赖
  20. *
  21. * 使用方法:
  22. *   1. 修改 CFG.serverUrl 为你的 wss:// 地址
  23. *   2. AIWROK 中直接运行本脚本
  24. *   3. 日志窗口查看连接状态和收发消息
  25. */

  26. // ========== 配置 ==========
  27. var CFG = {
  28.     // WSS 服务器地址(TLS 加密)
  29.     // wss:// 走加密通道,ws:// 走明文通道
  30.     serverUrl: "wss://echo.websocket.org/ws",

  31.     // 心跳间隔(秒),定时发 PING 保持连接
  32.     heartbeatIntervalSec: 15,

  33.     // 重连配置
  34.     reconnectIntervalMs: 3000,      // 重连基础间隔(毫秒)
  35.     maxReconnectAttempts: 999,      // 最大重连次数(999=无限)
  36.     reconnectMultiplier: 1.5,        // 指数退避倍数
  37.     maxReconnectDelayMs: 30000,     // 最大重连延迟(毫秒)

  38.     // 调试日志
  39.     debug: true,

  40.     // 脚本运行时长(毫秒),0=无限
  41.     runDuration: 0
  42. };

  43. // ========== 全局状态 ==========
  44. var G = {
  45.     ws: null,                      // websocket 实例
  46.     connected: false,              // 是否已连接
  47.     reconnectCount: 0,             // 已重连次数
  48.     currentDelay: CFG.reconnectIntervalMs,  // 当前重连延迟
  49.     running: true,                 // 运行标志
  50.     startTime: 0,                  // 启动时间
  51.     msgSentCount: 0,               // 已发送消息数
  52.     msgRecvCount: 0,               // 已接收消息数
  53.     lastPongAt: 0,                 // 上次收到PONG时间
  54.     heartbeatTimer: null           // 心跳定时器
  55. };

  56. // ========== 时间工具 ==========
  57. function nowMs() {
  58.     return (new Date()).getTime();
  59. }
  60. function nowSec() {
  61.     return Math.floor(nowMs() / 1000);
  62. }

  63. // ========== 设备信息 ==========
  64. function getDeviceInfo() {
  65.     var info = { imei: "", brand: "", model: "", screen: "" };
  66.     try { info.imei = device.getIMEI() || ""; } catch (e) {}
  67.     try { info.brand = device.getBrand() || ""; } catch (e) {}
  68.     try { info.model = device.getModel() || ""; } catch (e) {}
  69.     try { info.screen = screen.getScreenWidth() + "x" + screen.getScreenHeight(); } catch (e) {}
  70.     return info;
  71. }

  72. // ========== 消息发送 ==========
  73. function sendMsg(data) {
  74.     if (!G.connected || !G.ws) {
  75.         printl("[WARN] 未连接,无法发送");
  76.         return false;
  77.     }
  78.     try {
  79.         var raw;
  80.         if (typeof data === "string") {
  81.             raw = data;
  82.         } else {
  83.             raw = JSON.stringify(data);
  84.         }
  85.         G.ws.send(raw);
  86.         G.msgSentCount++;
  87.         printl("[SEND] " + raw.slice(0, 200));
  88.         return true;
  89.     } catch (e) {
  90.         printl("[ERROR] " + "发送异常: " + e.message);
  91.         return false;
  92.     }
  93. }

  94. // ========== 处理收到的消息 ==========
  95. function handleMessage(raw) {
  96.     G.msgRecvCount++;
  97.     printl("[RECV] " + raw.slice(0, 200));

  98.     var obj = null;
  99.     try {
  100.         obj = JSON.parse(raw);
  101.     } catch (e) {
  102.         printl("[DEBUG] " + "非 JSON 消息: " + raw.slice(0, 100));
  103.         return;
  104.     }

  105.     // 根据消息类型处理业务逻辑
  106.     if (obj.type === "WELCOME") {
  107.         printl("[INFO] " + "服务器欢迎: " + (obj.msg || ""));
  108.     } else if (obj.type === "PING" || obj.type === "ping") {
  109.         // 服务端心跳,回 PONG
  110.         G.lastPongAt = nowMs();
  111.         sendMsg({ type: "PONG", ts: nowSec() });
  112.     } else if (obj.type === "PONG" || obj.type === "pong") {
  113.         // 收到 PONG,更新心跳时间
  114.         G.lastPongAt = nowMs();
  115.         printl("[DEBUG] 收到 PONG");
  116.     } else if (obj.type === "CMD_SCREENSHOT") {
  117.         handleScreenshotCmd(obj);
  118.     } else if (obj.type === "CMD_CLICK") {
  119.         handleClickCmd(obj);
  120.     } else if (obj.type === "CMD_STATUS") {
  121.         handleStatusCmd(obj);
  122.     } else {
  123.         printl("[DEBUG] " + "未处理的消息类型: " + obj.type);
  124.     }
  125. }

  126. // ========== 业务指令:截图 ==========
  127. function handleScreenshotCmd(obj) {
  128.     try {
  129.         var bitmap = screen.screenShotFull();
  130.         if (!bitmap) {
  131.             sendMsg({ type: "CMD_RESP", cmd: "CMD_SCREENSHOT", ok: false, msg: "截图返回空" });
  132.             return;
  133.         }
  134.         var base64 = "" + bitmap.toBase64();
  135.         try { bitmap.recycle(); } catch (e) {}
  136.         sendMsg({
  137.             type: "CMD_RESP",
  138.             cmd: "CMD_SCREENSHOT",
  139.             ok: true,
  140.             cmdId: obj.cmdId,
  141.             data: { base64: base64, length: base64.length }
  142.         });
  143.     } catch (e) {
  144.         sendMsg({ type: "CMD_RESP", cmd: "CMD_SCREENSHOT", ok: false, msg: e.message });
  145.     }
  146. }

  147. // ========== 业务指令:点击 ==========
  148. function handleClickCmd(obj) {
  149.     try {
  150.         var x = parseInt(obj.data.x, 10);
  151.         var y = parseInt(obj.data.y, 10);
  152.         if (isNaN(x) || isNaN(y)) {
  153.             sendMsg({ type: "CMD_RESP", cmd: "CMD_CLICK", ok: false, msg: "坐标无效" });
  154.             return;
  155.         }
  156.         if (typeof action !== "undefined") {
  157.             action.click(x, y);
  158.         } else if (typeof hid !== "undefined") {
  159.             hid.click(x, y);
  160.         }
  161.         sendMsg({ type: "CMD_RESP", cmd: "CMD_CLICK", ok: true, cmdId: obj.cmdId });
  162.     } catch (e) {
  163.         sendMsg({ type: "CMD_RESP", cmd: "CMD_CLICK", ok: false, msg: e.message });
  164.     }
  165. }

  166. // ========== 业务指令:状态查询 ==========
  167. function handleStatusCmd(obj) {
  168.     var info = getDeviceInfo();
  169.     info.type = "CMD_RESP";
  170.     info.cmd = "CMD_STATUS";
  171.     info.ok = true;
  172.     info.cmdId = obj.cmdId;
  173.     info.runtimeSec = nowSec() - G.startTime;
  174.     info.msgSentCount = G.msgSentCount;
  175.     info.msgRecvCount = G.msgRecvCount;
  176.     info.reconnectCount = G.reconnectCount;
  177.     sendMsg(info);
  178. }

  179. // ========== 心跳定时器 ==========
  180. function startHeartbeat() {
  181.     stopHeartbeat();
  182.     var hbMs = CFG.heartbeatIntervalSec * 1000;
  183.     try {
  184.         G.heartbeatTimer = setTimeout(function tick() {
  185.             try {
  186.                 if (!G.running || !G.connected) return;
  187.                 // 检查心跳超时
  188.                 if (G.lastPongAt > 0 && (nowMs() - G.lastPongAt) > hbMs + 10000) {
  189.                     printl("[WARN] 心跳超时,主动断开重连");
  190.                     try { G.ws.close(); } catch (e) {}
  191.                     return;
  192.                 }
  193.                 sendMsg({ type: "PING", ts: nowSec() });
  194.                 G.heartbeatTimer = setTimeout(tick, hbMs);
  195.             } catch (e) {
  196.                 printl("[ERROR] " + "心跳异常(已兜住): " + e.message);
  197.                 G.heartbeatTimer = setTimeout(tick, hbMs);
  198.             }
  199.         }, hbMs);
  200.     } catch (e) {
  201.         // setTimeout 不存在时用 runTime.setTimeout 兜底
  202.         try {
  203.             G.heartbeatTimer = runTime.setTimeout(function tick() {
  204.                 try {
  205.                     if (!G.running || !G.connected) return;
  206.                     if (G.lastPongAt > 0 && (nowMs() - G.lastPongAt) > hbMs + 10000) {
  207.                         printl("[WARN] 心跳超时,主动断开重连");
  208.                         try { G.ws.close(); } catch (e2) {}
  209.                         return;
  210.                     }
  211.                     sendMsg({ type: "PING", ts: nowSec() });
  212.                     G.heartbeatTimer = runTime.setTimeout(tick, hbMs);
  213.                 } catch (e3) {
  214.                     G.heartbeatTimer = runTime.setTimeout(tick, hbMs);
  215.                 }
  216.             }, hbMs);
  217.         } catch (e4) {}
  218.     }
  219. }

  220. function stopHeartbeat() {
  221.     if (G.heartbeatTimer) {
  222.         try { clearTimeout(G.heartbeatTimer); } catch (e) {}
  223.         try { runTime.stopTimeout(G.heartbeatTimer); } catch (e) {}
  224.         G.heartbeatTimer = null;
  225.     }
  226. }

  227. // ========== WebSocket 回调函数 ==========

  228. // 连接成功
  229. function wsOnConnected() {
  230.     G.connected = true;
  231.     G.reconnectCount = 0;
  232.     G.currentDelay = CFG.reconnectIntervalMs;
  233.     G.lastPongAt = nowMs();
  234.     printl("[INFO] 连接成功 (onConnected) — TLS 握手完成");

  235.     // 启动心跳
  236.     startHeartbeat();

  237.     // 连接成功后发送注册消息
  238.     var info = getDeviceInfo();
  239.     sendMsg({
  240.         type: "REGISTER",
  241.         device: info,
  242.         ts: nowSec()
  243.     });
  244. }

  245. // 收到文本消息
  246. function wsOnTextMessage(msg) {
  247.     var raw;
  248.     try { raw = String(msg); } catch (e) { raw = "" + msg; }
  249.     handleMessage(raw);
  250. }

  251. // 连接错误
  252. function wsOnConnectError(err) {
  253.     G.connected = false;
  254.     var errMsg = "unknown";
  255.     try { errMsg = String(err); } catch (e) {}
  256.     if (!errMsg || errMsg === "null" || errMsg === "undefined") {
  257.         errMsg = "连接错误(无详细信息)";
  258.     }
  259.     printl("[ERROR] " + "连接错误 (onConnectError): " + errMsg);
  260.     stopHeartbeat();
  261.     scheduleReconnect();
  262. }

  263. // 连接断开
  264. function wsOnDisconnected() {
  265.     G.connected = false;
  266.     printl("[WARN] 连接断开 (onDisconnected)");
  267.     stopHeartbeat();
  268.     scheduleReconnect();
  269. }

  270. // ========== 建立连接 ==========
  271. function connect() {
  272.     // 先清理旧连接
  273.     if (G.ws) {
  274.         try { G.ws.close(); } catch (e) {}
  275.         G.ws = null;
  276.     }
  277.     G.connected = false;

  278.     printl("[INFO] 正在连接: " + CFG.serverUrl + "  (重连 " + G.reconnectCount + "/" + CFG.maxReconnectAttempts + ")");

  279.     // 创建 AIWROK 原生 websocket 实例
  280.     var ws = null;
  281.     try {
  282.         ws = new websocket();
  283.     } catch (e) {
  284.         printl("[ERROR] " + "new websocket() 失败: " + e.message);
  285.         scheduleReconnect();
  286.         return;
  287.     }

  288.     G.ws = ws;

  289.     // 设置 SocketID(唯一标识)
  290.     try {
  291.         ws.setSocketID("aiwrok_" + nowMs());
  292.     } catch (e) {
  293.         printl("[DEBUG] " + "setSocketID 失败(非致命): " + e.message);
  294.     }

  295.     // 挂载回调事件
  296.     var eventOk = false;
  297.     try {
  298.         ws.event(wsOnConnected, wsOnTextMessage, wsOnConnectError, wsOnDisconnected);
  299.         eventOk = true;
  300.         printl("[DEBUG] ws.event() 挂事件成功");
  301.     } catch (e1) {
  302.         printl("[WARN] ws.event() 失败: " + e1.message + " — 尝试属性赋值");
  303.     }

  304.     // 如果 event() 不行,用属性赋值兜底
  305.     if (!eventOk) {
  306.         try {
  307.             ws.onConnected = wsOnConnected;
  308.             ws.onTextMessage = wsOnTextMessage;
  309.             ws.onConnectError = wsOnConnectError;
  310.             ws.onDisconnected = wsOnDisconnected;
  311.             printl("[DEBUG] 属性赋值挂事件成功");
  312.         } catch (e2) {
  313.             printl("[ERROR] " + "两种挂事件方式都失败: " + e2.message);
  314.             scheduleReconnect();
  315.             return;
  316.         }
  317.     }

  318.     // 发起连接(注意: connet 双n)
  319.     try {
  320.         ws.connet(CFG.serverUrl);
  321.         printl("[DEBUG] connet() 已调用");
  322.     } catch (e3) {
  323.         printl("[WARN] connet() 异常: " + e3.message + " — 尝试 connect()");
  324.         try {
  325.             ws.connect(CFG.serverUrl);
  326.         } catch (e4) {
  327.             printl("[ERROR] " + "connect() 也失败: " + e4.message);
  328.             scheduleReconnect();
  329.         }
  330.     }
  331. }

  332. // ========== 重连调度 ==========
  333. function scheduleReconnect() {
  334.     if (!G.running) return;
  335.     if (G.reconnectCount >= CFG.maxReconnectAttempts) {
  336.         printl("[ERROR] 已达最大重连次数 " + CFG.maxReconnectAttempts + ",停止");
  337.         return;
  338.     }

  339.     G.reconnectCount++;

  340.     // 指数退避 + 随机抖动
  341.     G.currentDelay = Math.min(
  342.         G.currentDelay * CFG.reconnectMultiplier,
  343.         CFG.maxReconnectDelayMs
  344.     );
  345.     var jitter = Math.floor(G.currentDelay * (0.8 + Math.random() * 0.4));

  346.     printl("[INFO] 重连 " + G.reconnectCount + "/" + CFG.maxReconnectAttempts +
  347.         " — " + (jitter / 1000).toFixed(1) + "s 后重试");

  348.     try {
  349.         setTimeout(function () {
  350.             if (G.running) {
  351.                 try { connect(); } catch (e) {
  352.                     printl("[ERROR] " + "重连回调异常(已兜住): " + e.message);
  353.                     scheduleReconnect();
  354.                 }
  355.             }
  356.         }, jitter);
  357.     } catch (e) {
  358.         try {
  359.             runTime.setTimeout(function () {
  360.                 if (G.running) {
  361.                     try { connect(); } catch (e2) {
  362.                         printl("[ERROR] " + "重连回调异常(已兜住): " + e2.message);
  363.                         scheduleReconnect();
  364.                     }
  365.                 }
  366.             }, jitter);
  367.         } catch (e3) {}
  368.     }
  369. }

  370. // ========== 关闭清理 ==========
  371. function cleanup() {
  372.     G.running = false;
  373.     stopHeartbeat();
  374.     if (G.ws) {
  375.         try { G.ws.close(); } catch (e) {}
  376.         G.ws = null;
  377.     }
  378.     G.connected = false;
  379.     printl("[INFO] 资源已清理");
  380. }

  381. // ========== 主程序 ==========
  382. function main() {
  383.     try {
  384.         G.startTime = nowSec();

  385.         printl("[INFO] ==========================================");
  386.         printl("[INFO]   WSS WebSocket 客户端 (AIWROK原生) 启动");
  387.         printl("[INFO] " + "  服务器: " + CFG.serverUrl);
  388.         printl("[INFO]   心跳间隔: " + CFG.heartbeatIntervalSec + "s");
  389.         printl("[INFO]   最大重连: " + CFG.maxReconnectAttempts + " 次");
  390.         printl("[INFO] " + "  设备信息: " + JSON.stringify(getDeviceInfo()));
  391.         printl("[INFO] ==========================================");

  392.         // 发起首次连接
  393.         connect();

  394.         // 主线程保活(链式 setTimeout,非阻塞)
  395.         var tick = 0;
  396.         (function keepAlive() {
  397.             try {
  398.                 if (!G.running) {
  399.                     printl("[INFO] 主线程保活退出");
  400.                     return;
  401.                 }
  402.                 tick++;
  403.                 // 每 20 秒打印一次状态
  404.                 if (tick % 20 === 0) {
  405.                     printl("[INFO] " + "[保活] 已运行=" + (nowSec() - G.startTime) +
  406.                         "s 连接=" + G.connected +
  407.                         " 发送=" + G.msgSentCount +
  408.                         " 接收=" + G.msgRecvCount +
  409.                         " 重连=" + G.reconnectCount);
  410.                 }
  411.             } catch (e) {
  412.                 printl("[ERROR] " + "保活异常(已兜住): " + e.message);
  413.             }
  414.             try { setTimeout(keepAlive, 1000); }
  415.             catch (e) {
  416.                 try { runTime.setTimeout(keepAlive, 1000); } catch (e2) {}
  417.             }
  418.         })();

  419.         // 如果设置了运行时长,到时自动退出
  420.         if (CFG.runDuration > 0) {
  421.             sleep.millisecond(CFG.runDuration);
  422.             cleanup();
  423.         }
  424.         // 否则无限运行(靠保活循环维持)

  425.     } catch (e) {
  426.         printl("[FATAL] " + "主程序异常: " + e.message + "\n" + (e.stack || ""));
  427.         try { cleanup(); } catch (e2) {}
  428.     }
  429. }

  430. // ========== 启动 ==========
  431. main();
复制代码







欢迎光临 B2B网络软件 (http://bbs.niubt.cn/) Powered by Discuz! X3.2