开发者社区> 技术小阿哥> 正文

Android Socket 发送广播包的那些坑

简介:
+关注继续查看

Socket广播包经常被用于局域网内的两台设备之间互相发现和消息传递,在Android应用开发过程中,也经常会遇到这样的需求,例如:两台Android设备之间、Android与手环等智能硬件之间、Android与Windows电脑之间等等。


本文主要介绍在Android中使用Socket开发广播包程序时需要注意的编程事项,以及解决方法。


首先给出一段Android发送广播包的示例代码:


1
2
3
4
5
6
7
8
DatagramSocket socket = new DatagramSocket(8000);
socket.setBroadcast(true);
InetAddress addr = InetAddress.getByName("255.255.255.255");
byte[] buffer = "Hello World".getBytes();
DatagramPacket packet = new DatagramPacket(buffer,buffer.length);
packet.setAddress(addr);
packet.setPort(8086);
socket.send(packet);


下面分析其中需要注意的地方:


1. 不要在主线程中发送广播包


当然,这个做Android开发的人应该都知道,不能在UI线程中执行任何网络访问相关的操作,由于广播包的发送也属于网络操作,因此必须放到单独的线程中执行。


2. 广播地址不建议使用“255.255.255.255”


上述代码中,广播包的目标地址设置为了“255.255.255.255”,其实,这并不是一种推荐的做法。


“255.255.255.255” 是一种受限的广播地址,常用于在计算机不知道自己IP地址的时候发送,比如设备启动时向DHCP服务器索要地址等等,一般情况下,路由器不会转发目标为受限广播地址的广播包。


而且,有些路由器/Wi-Fi热点不支持该广播地址(例如:用Android手机做Wi-Fi热点的时候),因此在程序中会出现“ENETUNREACH (Network is unreachable)”的异常,因此,为了保证程序成功发送广播包,建议使用直接广播地址,例如:当前IP地址是 192.168.1.100,子网掩码是 255.255.255.0 的情况下,广播地址为:192.168.1.255,(具体的推算方法这里就不展开了,可以参考计算机网络相关书籍)。


那么,如何得到本网段的直接广播地址呢,下面是stackoverflow上面有位大牛分享的代码:


1
2
3
4
5
6
7
8
9
10
11
12
public static InetAddress getBroadcastAddress(Context context) throws UnknownHostException {
    WifiManager wifi = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);
    DhcpInfo dhcp = wifi.getDhcpInfo();
    if(dhcp==null) {
        return InetAddress.getByName("255.255.255.255");
    }
    int broadcast = (dhcp.ipAddress & dhcp.netmask) | ~dhcp.netmask;
    byte[] quads = new byte[4];
    for (int k = 0; k < 4; k++)
        quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);
    return InetAddress.getByAddress(quads);
}


直接使用该函数即可得到正确的“广播地址”,通过setAddress函数设置到DatagramPacket对象中即可。


3. Android设置为Wi-Fi热点时的广播地址


这是个比较大的坑,当Android设备被设置为Wi-Fi热点的时候,上面的函数得到的地址是"0.0.0.0",因此,我们需要探究当Android设备被设置为Wi-Fi热点的时候,它的IP地址究竟是多少?


有人研究了Android底层源码发现,当Android设备被设置为Wi-Fi热点的时候,其IP地址是hardcode写死在源码中的,地址是:“192.168.43.1”,对应的广播地址是:"192.168.43.255"


为此,我们需要写个函数来判断一下当前Android手机是否处于Wi-Fi热点模式下,如果是,则应该使用上面给出的这个广播地址,这里给出代码示例:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
protected static Boolean isWifiApEnabled(Context context) {
    try {
        WifiManager manager = (WifiManager)context.getSystemService(Context.WIFI_SERVICE);  
        Method method = manager.getClass().getMethod("isWifiApEnabled");
        return (Boolean)method.invoke(manager);
    }
    catch (NoSuchMethodException e) {
        e.printStackTrace();
    }
    catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)  {
        e.printStackTrace();
}
    return false;
}


Android SDK并没有开放判断是否处于热点模式的API,因此,我们需要通过反射的方式来得到,另外,注意添加权限: 


1
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />


4. 小结


本文涉及到的代码被封装到了一个Broadcaster.java的文件中,可以在博文最后的附件中下载,也可以从下面的地址下载:


https://github.com/Jhuster/Android/blob/master/Socket/Broadcaster.java



本文转自 Jhuster 51CTO博客,原文链接:http://blog.51cto.com/ticktick/1707858,如需转载请自行联系原作者

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
Android Socket通讯 分离服务端和客户端、发送表情消息
Android Socket通讯 分离服务端和客户端、发送表情消息
52 0
Android WebView选择图片、发送图片
Android WebView选择图片、发送图片
329 0
Android发送验证码倒计时,时间倒计时
Android发送验证码倒计时,时间倒计时
58 0
Android源码分析--广播的注册、发送和接收
Android源码分析--广播的注册、发送和接收
123 0
【Android 异步操作】手写 Handler ( Handler 发送与处理消息 | Handler 初始化 | 完整 Handler 代码 )
【Android 异步操作】手写 Handler ( Handler 发送与处理消息 | Handler 初始化 | 完整 Handler 代码 )
59 0
【Android RTMP】RTMPDump 推流过程 ( 独立线程推流 | 创建推流器 | 初始化操作 | 设置推流地址 | 启用写出 | 连接 RTMP 服务器 | 发送 RTMP 数据包 )
【Android RTMP】RTMPDump 推流过程 ( 独立线程推流 | 创建推流器 | 初始化操作 | 设置推流地址 | 启用写出 | 连接 RTMP 服务器 | 发送 RTMP 数据包 )
184 0
cordova-plugin-local-notifications发送Android本地消息
原文:cordova-plugin-local-notifications发送Android本地消息 1.GitHub源代码地址: https://github.
1547 0
android 空调遥控器——简单发送内容
android 空调遥控器——简单发送内容 接着上一篇我们接着研究android红外开发,本篇我们以网上最开源的格力空调编码开始研究,好了废话不多说,开搞!! 1、首先我们先来了解一下格力空调的编码格式 这个网上都有 格力空调遥控器(YB0F2)红外码组成如下,按解码顺序排列 起始码(S)+35.
1716 0
Android非UI主线程中,若干普通Java线程使用Handler发送接收消息
Android非UI主线程中,若干普通Java线程使用Handler发送接收消息 线程1和线程2均为普通Java线程,在Android中创建,然后在这两个普通Java线程中使用Handler发送和接收消息。
1110 0
文章
问答
文章排行榜
最热
最新
相关电子书
更多
Android组件化实现
立即下载
蚂蚁聚宝Android秒级编译—— Freeline
立即下载
Android插件化:从入门到放弃
立即下载