[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



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