打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
App Inventor插件开发(六)WiFi局域网socket通信

已上传GitHub,包括源码及aix,以及测试用aia,顺便贴个apk上去。
国内可以访问Gitee,来自开学后虚脱的我。。。

0.前言

Wifi下通信应该是大家都希望的吧,网上TaifunWiFi似乎很高级的样子,但是原谅我英语渣,看不懂。。。
mac和ip我懂,可是ssid是神马。。。看来我tcp/ip还没啃到家。。。
TaifunWiFi盯了半天似乎好像大概可能应该差不多是怎么去连接WiFi而不是通信的。
好像很多人的需求是和arduino有关,谁能来介绍一下,不懂
Android下很多人都讲了怎么用socket通信,可是移植到app inventor上的很少。
那就让我来开心的皮一下,介绍一下怎么在局域网内通信,不止WiFi哦。

1.解决步骤

1.1原理

我就不扯tcp/ip了,估计也没人有心思听。
这里只是讲一些基础知识,因为这个插件目前只能传字符串,如果要更多功能就需要自行补充了。
两台计算机间进行通讯需要以下三个条件:IP地址、协议、端口号。
IP地址肯定听说过,用人话说就像一个港口。
端口号用于区分一台主机的多个不同应用程序,范围为0-65535,我取8000,0-1023为为系统保留。用人话讲就像港口有很多船位,可以同时停很多条船。
Socket由IP地址+端口号组成。在Java中是使用TCP协议实现的网络通信。
ServerSocket是服务端。
因为网络连接是一个非常耗时的操作,比读写硬盘还耗时,需要单独一个线程,甚至不止一个。

1.2测试时bug记录

Client客户端状态良好,可发,收等会再说
Sever服务端莫名智障,打电话也不接,发短信也不回,最后发现是message是直接new的,要从myHandler.obtainMessage()获取才有用,坑死我了。。。
又被这个message坑了一回,每次sendMessage都要重新获取message一回,或者相邻几行不用,我也没搞懂,反正每次sendMessage都获取一遍问题就解决了,不然闪退,连个报错都没有,连接adb才得到错误信息,坑啊
Sever服务端测试成功!
等我重写一下,达到能够使用的程度。
又是一个坑,OutputStream的flush()是个空方法,只能再建一个BufferedOutputStream。
readline结尾要加\n!不是OutputStream的问题,这是装饰模式,空方法就是用于覆盖。
测试完成,鉴定可食用。

1.3服务端源码讲解

全文见SocketUtil.java
收到消息的回调,顺便加了个回车

@SimpleEvent public void GetMessage(String s){ EventDispatcher.dispatchEvent(this, 'GetMessage', '\n'+s);}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

handler用于从子进程返回UI线程,并调用回调。
如果你需要区分不同的消息的话,设置what,在handler里if-else或switch都可以。

public Handler handler = new Handler(){    @Override public void handleMessage(Message msg) {		/*switch(msg.what){			case 1:...break;		}*/		GetMessage(msg.obj.toString());	}};/*Message message_1 = handler.obtainMessage();message_1.what= 11;message_1.obj = '';handler.sendMessage(message_1);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

获得ip以及port的代码

String ip;int port;private ServerSocket serverSocket = null;public void getLocalIpAddress(ServerSocket serverSocket){ try { for (Enumeration<NetworkInterface> en=NetworkInterface.getNetworkInterfaces();en.hasMoreElements();){ NetworkInterface intf = en.nextElement(); for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses();enumIpAddr.hasMoreElements();){ InetAddress inetAddress = enumIpAddr.nextElement(); String mIP = inetAddress.getHostAddress().substring(0, 3); if(mIP.equals('192')){ ip = inetAddress.getHostAddress(); //获取本地IP port = serverSocket.getLocalPort(); } } } } catch (SocketException e) {}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

内部类,用于读取数据直到换行符,就是新的设备连接时的消息处理类

class ServerThread extends Thread{	    Socket socket;        Message message_2;	    public ServerThread(Socket socket){	        this.socket = socket;	    }	    @Override	    public void run() {            try {                BufferedReader br = null;                br = new BufferedReader(new InputStreamReader(socket.getInputStream()));                while(true){                    String msg = null;                    msg = br.readLine();                    if(msg != null){                        message_2 = handler.obtainMessage();                        message_2.obj = socket.getInetAddress().getHostAddress()+':'+msg;//把ip和冒号拼到消息上                        handler.sendMessage(message_2);                    }                }	        } catch (IOException e) {                message_2 = handler.obtainMessage();                message_2.obj = '他好像不见了';                handler.sendMessage(message_2);                try{socket.close();}catch(Exception e1){}	        }        }	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

启动服务的方法

@SimpleFunction public void receiveData(){ Thread thread = new Thread(){ @Override public void run() { //.. } }; thread.start();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

端口8000,初始化ip和port,发送一个message

try {	serverSocket = new ServerSocket(8000);} catch (IOException e) {	e.printStackTrace();}getLocalIpAddress(serverSocket);Message message_1 = handler.obtainMessage();message_1.obj = 'IP:' + ip + ' PORT: ' + port;handler.sendMessage(message_1);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

如果有新的设备连接,新建一个线程用于接收

while (true){ Socket socket = null; try { socket = serverSocket.accept();//如果没有设备连接会阻塞在这一句 //如果想控制连接数就把while改成for,然后计数即可 Message message_2 = handler.obtainMessage(); message_2.obj = '有兄弟连上了!'+socket.getInetAddress().getHostAddress(); handler.sendMessage(message_2); catch (IOException e) {} new ServerThread(socket).start();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

1.4客户端源码讲解

全文见SocketClient.java
handle等服务端出现的就不再叙述
这仨函数也没必要介绍了,一看就明白,重点在于那个线程

Socket socket = null;MyThread mt;final int CONNECT = 100001;final int SENDMESSAGE = 100002;final int CLOSE = 100003;@SimpleFunction(description = 'start')//关闭链接public void closeConnect(){	if(socket != null){		mt = new MyThread(CLOSE);		mt.start();	}else{		GetMessage('连接未创建!');	}}//发送消息@SimpleFunction(description = 'start')public void sendMessage(String s){	if(socket != null){		mt = new MyThread(SENDMESSAGE);		mt.setText(s);		mt.start();	}else{		GetMessage('连接未创建!');	}}//连接@SimpleFunction(description = 'start')public void connect(String ip){	if(socket == null){		mt = new MyThread(CONNECT);		mt.setIP(ip);		mt.start();	}else{		GetMessage('连接已创建!');	}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

额因为懒得写三个类所以合起来了

class MyThread extends Thread { public String txt1; public String IP; Message msg; public int flag; public MyThread(int flag) {this.flag = flag; } public void setText(String s){txt1 = s;} public void setIP(String ip){IP = ip;} @Override public void run() { switch(flag){ case CONNECT: //连接... break; case SENDMESSAGE //发送... break; case CLOSE: //关闭... break; } } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

连接

case CONNECT:	try {		socket = new Socket();		msg = myHandler.obtainMessage();		msg.obj = '开始连接';		myHandler.sendMessage(msg);		socket.connect(new InetSocketAddress(IP, 8000), 1000);//端口8000,超时1000ms		ou = socket.getOutputStream();//获取输出流		msg = myHandler.obtainMessage();		msg.obj = '连接成功';		myHandler.sendMessage(msg);	} catch (SocketTimeoutException aa) {		msg = myHandler.obtainMessage();		msg.obj = '连接超时';		myHandler.sendMessage(msg);		socket = null;	} catch (IOException e) {		msg = myHandler.obtainMessage();		msg.obj = '未知错误';		myHandler.sendMessage(msg);		socket = null;	}break;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

发信

case SENDMESSAGE: try { ou.write(txt1.getBytes('utf-8'));//用utf8输出字符串,一定要化成字节流 ou.write('\n'.getBytes('utf-8'));//因为是读取一行,一行结束了要加换行 ou.flush();//清空缓冲区 msg = myHandler.obtainMessage(); msg.obj = '发送完毕'; myHandler.sendMessage(msg); }catch (IOException e) { msg = myHandler.obtainMessage(); msg.obj = '未知错误'; myHandler.sendMessage(msg); }break;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

关闭

case CLOSE:	try {		ou.close();		socket.close();		socket = null;		msg = myHandler.obtainMessage();		msg.obj = '关闭';		myHandler.sendMessage(msg);	}catch (IOException e) {		msg = myHandler.obtainMessage();		msg.obj = '未知错误';		myHandler.sendMessage(msg);	}break;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2.先来几张测试图

难得我终于发测试图了。。。
不过一个问题,就是要测试的话需要两个手机,我翻箱倒柜找到了一个特别卡的旧手机,所以截图只能在那个比较新的手机上完成
所以我用那个新手机分别做了一次client和server,分别截图
还有,server只能打开一次,第二次会闪退,要彻底关掉才能打开第二次
发现一个很神奇的事,电脑热点共享后竟然可以直连,但只能单向
server服务端长这样


client客户端长这样

客户端的ip处填写server第二行的ip
客户端代码

服务端代码

代码看得出,并不多,使用起来应该比较方便

aia和aix及apk见GitHub

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Android Application Thread CPU GC Operatiing and OOM Question 0603
android消息机制 之Handler
Android Handler详解
android学习笔记之Handler使用
Android中handler的用法总结
Android Message和obtainMessage的区别
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服