[app自动化工具] autojs教程七: websocket实现云控的核心代码实现,适用 autojs4

应用场景

autojs可以实现批量控制手机端,下发指定任务,后台管理操作等,怎么实现的呢?抱着对技术更高追求和热爱的精神,想要一探背后的技术原理,经过半天的编码,完成了核心的框架的编码,包含客户端和服务端代码,成功跑通并验证运行 (还有一些处理不是很完善,但核心思想和模型已经完成)

实现思路

通过websocket实现客户端和服务器全双工通信,客户端连接后,服务器端可以通过websocket push方式给连接的客户端下发指令,另外管理端一般是web,这里也可以通过起个webserver方式进行管理端操作,也可以将管理端视为客户端的一员,标记为管理端,只有管理端可以操作下发指令。管理控制端基于时间原因就没有写web页面,直接用websocket测试页面下发任务,具体看运行效果图。

简单示例为: app-websocket-client <——-连接/执行—-> websocket-server <——-获取/下发—> web-admin

使用技术栈:

客户端 autojs , javascript, websocket
服务端 nodejs

代码实践:

因为我用的是autojs4.0,不能直接使用官方给出的websocket用法,只能用java的OkHttpClient来实现,原理都是一样的,最后有运行完的效果图。

app autojs客户端代码:

//使用与autojs4
var mClient = new OkHttpClient();
//这里是服务端相对于手机端的ip
var request = new Request.Builder().get().url("ws://192.168.0.100:8180/").build();
var globalWebsocket = null;
function init() {
    mClient.newWebSocket(request, new JavaAdapter(WebSocketListener, {
        onOpen: function (webSocket, response) {
            globalWebsocket=webSocket;
            console.log("开启连接成功")
        },
        onMessage: function (webSocket, text) {
            console.log("c<===="+text);
            process_msg(text)
        },
        onClosed: function (webSocket, code,reason) {
           console.log("onclose")
        },   
        onFailure: function (webSocket, throwable,response) {
            console.log("onFailure")
            console.log(webSocket, throwable,response)
        },
    }));
}

//接受处理消息
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模块 npm install ws uuid
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;
}
//node serve.js
服务端运行端口监听
autojs-app客户端运行情况
管理员admin控制端(使用websocket下发指令)

待完善的点:

核心功能模块已经实现,搭了个架子。可以继续完善鉴权处理,异常处理,捕获异常情况;日志汇总查看;管理端web可视化操作;任务库开发等。

获取资源前请仔细阅读一下声明:

重要提示

如有解压密码: 看下载页、看下载页、看下载页。
源码工具资源类具有可复制性: 建议具有一定思考和动手能力的用户购买。
请谨慎考虑: 小白用户和缺乏思考动手能力者不建议赞助。
虚拟商品购买须知: 虚拟类商品,一经打赏赞助,不支持退款。请谅解,谢谢合作!
声明: 本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。