牙叔教程 简单易懂
发送文件效果演示手机发送文件给模拟器
模拟器发送文件给手机
思路
要实现互传, 那么两端必须通信, 通信采用什么方式呢?
经测试,
用autojs的nodejs在真机上开起服务, 模拟器是可以访问到的;
用autojs的nodejs在模拟器上开起服务, 真机访问不到;
因此, 我们就不能在模拟器用nodejs开启服务了, 因为真机访问不到;
所以, 我们只在手机开启服务, 模拟器只发送请求;
手机端开启socket服务,
模拟器也用socket与手机通信;
有一个问题要注意, 模拟器上, autojs不能运行命令
npm i --no-bin-links koa
会报错: initialize houdini failed
因此, 我们写完代码后, 要打包以后使用;
打包以后安装到模拟上, 可以正常运行
以上是文件互传的通信设计思路, 下面来看看代码怎么写:
nodejs的socket例子服务端
const net = require("net");
const port = 8000;
const hostname = "127.0.0.1";
// const hostname = null;
// 定义两个变量, 一个用来计数,一个用来保存客户端
let clients = {};
let clientName = 0;
// 创建服务器
const server = new net.createServer();
server.on("connection", (client) => {
client.name = clientName; // 给每一个client起个名
clients[client.name] = client; // 将client保存在clients
client.on("data", function (msg) {
//接收client发来的信息
console.log(`客户端${client.name}发来一个信息:${msg}`);
});
client.on("error", function (e) {
//监听客户端异常
console.log("client error" e);
client.end();
});
client.on("close", function () {
delete clients[client.name];
console.log(`客户端${client.name}下线了`);
});
});
server.listen(port, hostname, function () {
console.log(`服务器运行在:http://${hostname}:${port}`);
});
客户端
const net = require("net");
const socket = new net.Socket();
const port = 8000;
const hostname = "127.0.0.1";
socket.setEncoding = "UTF-8";
socket.connect(port, hostname, function () {
socket.write("hello 大家好~~");
});
socket.on("data", function (msg) {
console.log(msg);
});
socket.on("error", function (error) {
console.log("error" error);
});
socket.on("close", function () {
console.log("服务器端下线了");
});
然后在终端运行
node socket-server.js
再开一个终端, 运行
node socket-client.js
控制台显示内容
基本的socket建立起来了, 下面要考虑实际情况
连接的实际情况假如有一个手机, 电脑上再开一个模拟器, 两者使用同一个无线网络;
现在要实现文件互传, 用户先打开哪个? 后打开哪个?
我们是不知道的, 并且, 还要判断是不是模拟器,
怎么判断是不是模拟器
importClass(android.net.Uri);
importClass(android.os.BUIld);
function isSimulator() {
let url = "tel:" "123456";
let Intent = new Intent();
intent.setData(Uri.parse(url));
intent.setAction(Intent.ACTION_DIAL);
// 是否可以处理跳转到拨号的 Intent
let canCallPhone = intent.resolveActivity(context.getPackageManager()) != null;
return (
Build.FINGERPRINT.startsWith("generic") ||
java.lang.String(Build.FINGERPRINT.toLowerCase()).contains("vbox") ||
java.lang.String(Build.FINGERPRINT.toLowerCase()).contains("test-keys") ||
java.lang.String(Build.MODEL).contains("google_sdk") ||
java.lang.String(Build.MODEL).contains("Emulator") ||
java.lang.String(Build.MODEL).contains("MuMu") ||
java.lang.String(Build.MODEL).contains("virtual") ||
java.lang.String(Build.SERIAL).equalsIgnoreCase("android") ||
java.lang.String(Build.MANUFACTURER).contains("Genymotion") ||
(Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")) ||
"google_sdk".equals(Build.PRODUCT) ||
context.getSystemService(context.TELEPHONY_SERVICE).getNetworkOperatorName().toLowerCase().equals("android") ||
!canCallPhone
);
}
let simulator = isSimulator();
if (simulator) isSimulator = "当前是模拟器环境";
else isSimulator = "当前是真机环境";
log(isSimulator);
假设先点开手机
正常启动socket服务端, UI上应该显示一个等待别人连接的画面, 谁连接了就显示出来
我们先画一个头像
然后画圆圈, 从小到大
把圆慢慢变成透明
让圆环动起来
再绘制一个客户端头像
UI基本就这样, 后面有需要再添加其他的按钮或者列表之类的
模拟器怎么发现手机端模拟器一定是看客户端, 因为我用的雷电模拟器局域网IP是 172 开头,
手机是192开头, 不在同一个网段;
模拟器不知道手机的前三位, 因此, 我们前三位就固定下来是 192.168.5.
或者用一个输入框, 让用户可编辑, 作为教程来说, 简单一些, 就固定即即可;
那么模拟器怎么才能知道手机最后一位呢?
我们可以遍历最后一位 2~254
子网掩码默认为255.255.255.0掩码,意思就是前面三个位置已经固定,IP地址最多有255个。如果子网掩码是255.255.0.0 那IP地址就是255X255=65025.一般255个已经足够了。
C类地址
C类地址的表示范围为:192.0.0.0~223.255.255.255,默认网络掩码为:255.255.255.0;C类地址分配给小型网络,如一般的局域网和校园网,它可连接的主机数量是最少的,采用把所属的用户分为若干的网段进行管理。C类网络用前三组数字表示网络的地址,最后一组数字作为网络上的主机地址。
一般同一个局域网,前三位都是一样的, 我这里前三位是 192.168.5
那么我们要发现同一局域网可用的ip时, 我们可以发送请求遍历2~254,
可以用ping, 也可以用http, 我测试ping的时候, ping不通
用java代码或者autojs自带的命令行都ping不通 , 网上说可能是台式机禁用了ICMP回显功能之类的吧
pingCmd = "ping -c 1 -w 0.5 "; //其中 -c 1为发送的次数,-w 表示发送后等待响应的时间
locAddress = "192.168.5.";
// current_ip = locAddress "21";
current_ip = "192.168.5.20";
p = pingCmd current_ip;
run = java.lang.Runtime.getRuntime(); //获取当前运行环境,来执行ping,相当于windows的cmd
log(p);
proc = run.exec(p);
result = proc.waitFor();
if (result == 0) {
log("连接成功" current_ip);
} else {
log("连接失败" current_ip);
}
那么我就不用ping了, 用http, 也不用auotjs的http, 用nodejs会快一点, 毕竟是异步的;
ping不需要端口, http需要端口, 端口我们也用固定的, 随便选个 5001-65535
就选 53182 , 随便打了几个数字
手机服务端监听我发现写教程, 不能考虑那么多场景, 不然要花好多时间才能写完
用的还是上面那个服务端代码
大家要注意, 我用的雷电模拟器和手机不在同一个网段,
手机ping不了模拟器, 所以只能手机当服务端, 模拟器只能当客户端
const server = new net.createServer();
server.on("connection", (client) => {
...
});
server.listen(port, hostname, function () {
console.log(`服务器运行在:http://${hostname}:${port}`);
});
"nodejs";
var net = require("net");
var Socket = net.Socket;
var ip = "192.168.5";
var port = 53182;
var scan = function (host, cb) {
var socket = new Socket();
var status = null;
socket.setTimeout(1500);
socket.on("connect", function () {
socket.end();
cb && cb(null, host);
});
socket.on("timeout", function () {
socket.destroy();
cb && cb(new Error("timeout"), host);
});
socket.on("error", function (err) {
cb && cb(err, host);
});
socket.on("close", function (err) {});
socket.connect(port, host);
};
for (var i = 2; i < 254; i ) {
scan(ip "." i, function (err, host) {
if (err) {
console.log("Not found", host);
return;
}
console.log("Found: ", host);
});
}
遍历的日志
09-18 12:40:39.890 Thread-34/D: Found: 192.168.5.30
09-18 12:40:41.246 Thread-34/D: Not found 192.168.5.19
09-18 12:40:41.249 Thread-34/D: Not found 192.168.5.20
09-18 12:40:41.251 Thread-34/D: Not found 192.168.5.21
...
09-18 12:40:41.269 Thread-34/D: Not found 192.168.5.28
09-18 12:40:41.270 Thread-34/D: Not found 192.168.5.29
09-18 12:40:41.278 Thread-34/D: process exit: 0
09-18 12:40:41.279 Script-7 Main [/storage/emulated/0/脚本/findipclient.js]/V:
我的手机ip是192.168.5.30, 模拟器是可以发现我的手机ip的, 同时nodejs是异步的, 速度很快, 最多2秒, 255个端口就遍历完了
篇幅太长, 下一节再说传输文件
环境设备: 小米11proAndroid版本: 12雷电模拟器:9.0.17Android版本: 9Autojs版本: 9.2.13
名人名言思路是最重要的, 其他的百度, bing, stackoverflow, github, 安卓文档, autojs文档, 最后才是群里问问 --- 牙叔教程
声明部分内容来自网络 本教程仅用于学习, 禁止用于其他用途
,