[app自动化工具] autojs教程七: websocket实现云控的核心代码实现,适用 autojs9
应用场景
autojs可以实现批量控制手机端,下发指定任务,后台管理操作等,怎么实现的呢?抱着对技术更高追求和热爱的精神,想要一探背后的技术原理,经过半天的编码,完成了核心的框架的编码,包含客户端和服务端代码,成功跑通并验证运行 (还有一些处理不是很完善,但核心思想和模型已经完成)
实现思路
通过websocket实现客户端和服务器全双工通信,客户端连接后,服务器端可以通过websocket push方式给连接的客户端下发指令,另外管理端一般是web,这里也可以通过起个webserver方式进行管理端操作,也可以将管理端视为客户端的一员,标记为管理端,只有管理端可以操作下发指令。管理控制端基于时间原因就没有写web页面,直接用websocket测试页面下发任务,具体看运行效果图。
简单示例为: app-websocket-client <——-连接/执行—-> websocket-server <——-获取/下发—> web-admin
使用技术栈:
客户端 autojs , javascript, websocket
服务端 nodejs
代码实践:
因为我用的是autojs9,修改于autojs9版本>> [app自动化工具] autojs教程 websocket实现云控的核心代码实现( autojs4),最后有运行完的效果图。
app autojs客户端代码:
//使用与autojs9对应的代码,下面的是相对于autojs4.0修改的部分let ws = web.newWebSocket("ws://124.222.155.145:8080", {eventThread: 'this'});let globalWebsocket = null;console.show();var deviceID = device.getAndroidId(); // 设备唯一标识符function init() {globalWebsocket = ws;ws.on("open", (res) => {console.log("开启连接成功");}).on("text", (text) => {console.log("c<====" + text);process_msg(text);}).on("closed", (code, reason) => {console.log("onclose");}).on("failure", (err, res) => {console.log("onFailure");console.log(err, res);});}//接受处理消息 function process_msg(data){ var msg = JSON.parse(data); //console.log("type:"+msg.type) var runflag = false var runlog var retunflag = true switch (msg.type) { case 'notification': retunflag = false break; case 'pong': retunflag = false break; case 'start': console.log("start") var appname = msg.appname var packagename = msg.appname if(appname){ launchApp(appname) }else{ launchPackage(packagename) } runflag = true break; case 'task1': console.log("执行task1,成功") runflag = true break; case 'task2': console.log("执行task2,失败") runflag = false runlog = "task2 can't xxx" break; case 'remote': var taskcode = msg.taskcode; var taskname = msg.taskname; engines.execScript(taskname, taskcode); runflag = true break; default: runflag = false runlog = "unknow type" break; } if(runflag){ msg.ret="success" }else{ msg.ret="fail" msg.log=runlog } if(retunflag){send_msg(msg)} } function send_msg(data){ if(globalWebsocket==null){ console.log("连接为空..") return } console.log("c===>") var success = globalWebsocket.send(JSON.stringify(data)) if(!success){ console.log("发送失败") } return success } function run(){ try{ if(globalWebsocket==null){ init(); sleep(500) } let success=globalWebsocket.send(JSON.stringify({"type":"ping"})) if(!success){ init(); } sleep(1000) }catch(e){ console.log(e) } } //发送心跳 threads.start(function(){ setInterval(() => { globalWebsocket.send(JSON.stringify({"type":"ping"})) }, 30*1000); }) run() // 脚本退出时取消WebSocket events.on('exit', () => { console.log("退出"); globalWebsocket.close() });
服务端就直接使用nodejs实现websocket-server, 用其他语言也是可以的,处理思路对就行:
//导入ws
var WebSocket = require('ws');
//创建websocket服务,端口port为:****
var WebSocketServer = WebSocket.Server,
wss = new WebSocketServer({ port: 8180 });
//引入uuid模块
var uuid = require('uuid');
//定义一个空数组,存放客户端的信息
var clients = [];
var admin_clients = {};
function wsSend(type, client_uuid, nickname, message, clientcount) {
//遍历客户端
for (var i = 0; i < clients.length; i++) {
//声明客户端
var clientSocket = clients[i].ws;
if (clientSocket.readyState === WebSocket.OPEN) {
//客户端发送处理过的信息
clientSocket.send(JSON.stringify({
"type": type,
"id": client_uuid,
"nickname": nickname,
"message": message,
"clientcount": clientcount,
}));
}
}
}
//消息格式 {"type":"set_admin"}
// {"type":"dispatch_task","cmdtype":"task1"}
function wsSend2(message, target_client_uuid) {
if(target_client_uuid!=null){
if(admin_clients[target_client_uuid]){
//console.log("下发给管理员")
}else{
//console.log("下发给单个客户端");
}
//遍历客户端
for (var i = 0; i < clients.length; i++) {
//声明客户端
var clientSocket = clients[i].ws;
if (clientSocket.readyState === WebSocket.OPEN) {
//指定客户端 发送处理过的信息
if(target_client_uuid==clients[i].id){
clientSocket.send(JSON.stringify(message));
}
}
}
}else{
console.log("下发任务给所有客户端")
//遍历客户端
for (var i = 0; i < clients.length; i++) {
//声明客户端
var clientSocket = clients[i].ws;
if (clientSocket.readyState === WebSocket.OPEN) {
//客户端发送处理过的信息
clientSocket.send(JSON.stringify(message));
}
}
}
}
//声明客户端index默认为1
var clientIndex = 1;
//服务端连接
wss.on('connection', function(ws) {
//客户端client_uuid随机生成
var client_uuid = uuid.v4();
//昵称为游客+客户端index
var nickname = "客户端" + clientIndex;
//client++
clientIndex += 1;
//将新连接的客户端push到clients数组中
clients.push({ "id": client_uuid, "ws": ws, "nickname": nickname });
//控制台打印连接的client_uuid
console.log('client [%s] connected', client_uuid, "当前客户端数量:", clients.length);
//声明连接信息为 昵称+来了
// var connect_message = nickname + " 来了";
var connect_message = " 来了";
//服务器广播信息 ***来了
//wsSend("notification", client_uuid, nickname, connect_message, clients.length);
//当用户发送消息时
ws.on('message', function(message) {
var message = ArrayBufferUTF8ToStr(message)
message = JSON.parse(message)
console.log("s<=== ", message);
// 用户输入"/nick"的话为重命名消息
if (message.type.indexOf('/nick') === 0) {
var nickname_array = message.split(' ');
if (nickname_array.length >= 2) {
var old_nickname = nickname;
nickname = nickname_array[1];
var nickname_message = "用户 " + old_nickname + " 改名为: " + nickname;
wsSend("nick_update", client_uuid, nickname, nickname_message, clients.length);
}
}
if(message.type=="set_admin"){
console.log("设置客户端 ",client_uuid," 为管理员")
admin_clients[client_uuid] = 1
wsSend2(message, client_uuid)
}
if(message.type=="dispatch_task"){
if(admin_clients[client_uuid]){
message.type = message.cmdtype
wsSend2(message)
}else{
console.log("没有权限,不是管理端");
}
}
if(message.type=="ping"){
message.type="pong"
wsSend2(message, client_uuid)
}
//发送消息
else {
//记录log
//wsSend("message", client_uuid, nickname, message, clients.length);
}
});
//关闭socket连接时
var closeSocket = function(customMessage) {
//遍历客户端
for (var i = 0; i < clients.length; i++) {
//如果客户端存在
if (clients[i].id == client_uuid) {
// 声明离开信息
var disconnect_message;
if (customMessage) {
disconnect_message = customMessage;
} else {
disconnect_message = nickname + " 走了";
}
//客户端数组中删掉
clients.splice(i, 1);
//服务广播消息
//wsSend("notification", client_uuid, nickname, disconnect_message, clients.length);
}
}
console.log(client_uuid+",已关闭,剩余客户端:"+clients.length)
}
ws.on('close', function() {
closeSocket();
});
process.on('SIGINT', function() {
console.log("Closing things");
closeSocket('Server has disconnected');
process.exit();
});
});
function ArrayBufferUTF8ToStr(array) {
var out,i,len,c;
var char2,char3;
if (array instanceof ArrayBuffer) {
array = new Uint8Array(array);
}
out = "";
len = array.length;
i = 0;
while(i < len) {
c = array[i++];
switch(c >> 4) {
case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
// 0xxxxxxx
out += String.fromCharCode(c);
break;
case 12: case 13:
// 110x xxxx 10xx xxxx
char2 = array[i++];
out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
break;
case 14:
// 1110 xxxx 10xx xxxx 10xx xxxx
char2 = array[i++];
char3 = array[i++];
out += String.fromCharCode(((c & 0x0F) << 12) |
((char2 & 0x3F) << 6) |
((char3 & 0x3F) << 0));
break;
}
}
return out;
}



待完善的点:
核心功能模块已经实现,搭了个架子。可以继续完善鉴权处理,异常处理,捕获异常情况;日志汇总查看;管理端web可视化操作;任务库开发等。
重要提示
如有解压密码: 看下载页、看下载页、看下载页。
源码工具资源类具有可复制性: 建议具有一定思考和动手能力的用户购买。
请谨慎考虑: 小白用户和缺乏思考动手能力者不建议赞助。
虚拟商品购买须知: 虚拟类商品,一经打赏赞助,不支持退款。请谅解,谢谢合作!
声明: 本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
