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
关于Android Socket发送广播包的那些坑就总结到这里了,有任何疑问或者建议欢迎留言或者来信lujun.hust@gmail.com交流,或者关注我的新浪微博 @卢_俊 获取最新的文章和资讯。
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
关于Android Socket发送广播包的那些坑就总结到这里了,有任何疑问或者建议欢迎留言或者来信lujun.hust@gmail.com交流,或者关注我的新浪微博 @卢_俊 获取最新的文章和资讯。