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,如需转载请自行联系原作者

相关文章
|
11月前
|
Android开发 Kotlin
Android 获取当前的类名,包名,路径等
在做项目时,无论为了功能还是调试,很多时候都需要获取到当前类的类名,包名,路径等等。 在这里总结一下,以便总结和以后需要的时候更快的解决问题。
|
25天前
|
Java Android开发
Android Studio的使用导入第三方Jar包
Android Studio的使用导入第三方Jar包
11 1
|
4月前
|
缓存 安全 Java
安卓现代化开发系列——从生命周期到Lifecycle【扩展包1已更新】-1
安卓现代化开发系列——从生命周期到Lifecycle【扩展包1已更新】
64 0
|
5月前
|
Shell 数据库 开发工具
(超详细)android中SqLite数据库的使用(一文包懂包会)
(超详细)android中SqLite数据库的使用(一文包懂包会)
117 0
|
9月前
|
移动开发 编解码 缓存
Android包体积过大,真的会影响绩效
Apk瘦身,做为一个Android开发者,这是多多少少都会接触到的,同样功能的App,200M和150M,给用户的第一直觉是不一样的,如果不是刚需,体积越大,用户的排斥也就越大,所以啊,铁子们,你以为瘦身,是简简单单的把体积变小,殊不知,直接影响着用户的真实体验,在开发中,是很有必要进行实施的,毕竟影响着网络数据流量和下载的等待时间。
100 0
|
10月前
|
资源调度 JavaScript Android开发
webapp打包为Android的apk包的一种方法
webapp打包为Android的apk包的一种方法
|
10月前
|
Java 开发工具 Android开发
Android dx工具(jar包转成dex格式二进制jar包工具)
Android dx工具(jar包转成dex格式二进制jar包工具)
611 0
|
10月前
|
缓存 网络协议 算法
【Python基础篇021】黏包现象丨udp的socket服务
【Python基础篇021】黏包现象丨udp的socket服务
10929 0
|
11月前
|
Java Android开发 Kotlin
Android 获取栈顶应用包名
需求:因为需要做到应用锁功能,所以需要对前台展示的App进行判断锁屏,所以当应用锁的后台服务挂起时,需要对栈顶的应用包名进行判断。 这里做一个总结
|
11月前
|
Java Android开发
【Android】jar包和AAR包
以前在使用 Eclipse 开发 Android 时,如果想代码打包,只有 jar 包一个方法,但是 jar包 只能把 Java 文件代码打包进去,如果要使用一个有布局和资源的库的话,除了将 jar 放入 libs 外,还要引入相关的资源和配置文件,很麻烦。
152 0