最近在做一个项目,手机app需要发现家庭Wi-Fi下面连接的物联网设备,并获取设备的一些相关信息,思考了几种方案,最终决定使用Udp广播的形式,理由呢,就是Udp使用起来简单,大部分功能Google已经替我们封装好了,直接使用就可以。
很多人说Udp是不可靠的,因为它是一种无连接协议。但是考虑到使用的场景:家庭Wi-Fi,网络环境不会太复杂;每次发送的数据很小等等,我觉得Udp能够满足需求,好了,废话不说,直接上代码。
首先是手机app端,app主动发送Udp广播,并监听指定端口来接收设备单播回来的数据,这里我使用两个线程,一个负责发广播,一个负责接收数据,考虑到可能多个地方会使用到,我决定封装成为一个工具类,关键代码如下:
/*** 用来发送Udp广播* @param sendData:需要广播出去的数据* */
public void send(final UdpScanSendData sendData) {new Thread(new Runnable() {@Overridepublic void run() {DatagramSocket hostSocket = null;try {hostSocket = new DatagramSocket();Gson gson = new Gson();//设置30秒超时hostSocket.setSoTimeout(30000);//转换为json字符串String req = Json(sendData);Log.e("UDP req", req);//转换为byte数组byte[] data = Bytes();//设置广播地址InetAddress ipBroad = ByName("255.255.255.255");DatagramPacket packet = new DatagramPacket(data, data.length, ipBroad, 2088);packet.setData(data);//发送数据hostSocket.send(packet);} catch (Exception e) {e.printStackTrace();} finally {if (hostSocket != null) {hostSocket.close();}}}}).start();}
其中UdpScanSendData 包含了手机端的一些信息,比如手机的Ip地址,监听的端口(这里为2088),以及会话标识,代码如下:
public class UdpScanSendData {public String IP;public String port;public String msgType;public UdpScanSendData(String IP) {this.IP = IP;this.port = 2088;this.msgType = "SCAN_DEV_REQ";}
}
这样,我们就通过广播,把手机的信息广播出去,当设备收到广播,解析出手机的Ip,端口号,就可以把设备信息,通过单播的形式发送到手机,因此手机还需要监听指定的端口,来接收数据,代码如下:
/*** 监听Udp回信* @param handler 使用handler把接收到的消息传递出去* @param localIp 手机Ip地址* */public void receive(final String localIp, final Handler handler) {new Thread(new Runnable() {@Overridepublic void run() {if (handler != null) {handler.sendEmptyMessage(HttpConstance.SCANING);}byte[] data = new byte[1024 * 4];datagramSocket = null;DatagramPacket dp;//这里存放接收到的对象Set<UdpScanReceiveData> set = new HashSet<>();try {datagramSocket = new DatagramSocket(null);datagramSocket.setReuseAddress(true);//绑定指定端口datagramSocket.bind(new InetSocketAddress(2017));datagramSocket.setSoTimeout(30000);dp = new DatagramPacket(data, data.length);while (!datagramSocket.isClosed()) {//接收消息ive(dp);if (dp != null) {//接收到数据包的ip地址String devIp = dp.getAddress().getHostAddress();//过滤本机的Ip地址,由于是发的全网广播,手机也可能会收到if (!localIp.equals(devIp)) {//还原出消息字符串String rsp = new Data(), dp.getOffset(), dp.getLength());Log.e("接收到 ==", rsp);if (!TextUtils.isEmpty(rsp)) {//转换为指定的消息对象UdpScanReceiveData rspData = new Gson().fromJson(rsp, UdpScanReceiveData.class);if (rspData != null) {//相关的逻辑处理set.add(rspData);if (handler != null) {handler.obtainMessage(HttpConstance.SCAN_SUCCESS, set).sendToTarget();}}}}}}} catch (Exception e) {e.printStackTrace();} finally {if (handler != null) {if (set.size() > 0) {handler.obtainMessage(HttpConstance.SCAN_COMPLETE, set).sendToTarget();} else {handler.obtainMessage(HttpConstance.SCAN_FAILD).sendToTarget();}}if (datagramSocket != null) {datagramSocket.close();datagramSocket = null;}}}}).start();}
我把这个工具类,定义为单例,把datagramSocket定义为全局变量,这样方便我们在外面关闭upd监听,从上面的代码可以看出,要关闭Udp监听,要么等30秒超时,要么调用datagramSocket.close()这个方法,为此,我们添加一个关闭Udp的方法:
public void close() {try {if (datagramSocket != null) {datagramSocket.close();}} catch (Exception e) {e.printStackTrace();} finally {datagramSocket = null;}}
这样,手机端的工具类我们就封装好了。到这里,我的工作算是完成了,剩下的就交给终端厂商完成,在手机app上面,点击扫描按钮,就调用工具类的send方法发送一个Udp广播,并监听端口30秒,当设备接收到该广播,就是向手机的ip地址,端口发送一个udp单播,把终端数据发送给手机app。
因为udp的不可靠性,我在activity里面定义了一个子线程,每隔5秒调用一次发送广播的方法,实验表明,发送3次,基本上不会出现扫描不到的情况。
客户端的流程跟手机app刚好相反,开启一个线程监听约定好的端口,这里是2088,当收到广播时候,如果会话标识正确,就把自己的信息广播出去,附上客户端的测试demo,真实设备不是安卓的,该demo仅用于测试
.git
本文发布于:2024-01-29 15:38:09,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170651389416301.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |