android开发:获取手机IP和UDP广播
UDP广播在通讯双方互相不知道对方IP的情况下很有用。这种情形我们也可以用遍历网段来实现,但是比较粗暴,如果网段比较大,不是最多256台主机的C类网段的话,很难做遍历。
UDP广播是解决这种问题的标准方案。
注意,广播和多播是不同的,广播是同时发送给所有主机,而多播是一个特殊的组,必须明确加入退出。
目录
一、什么是UDP广播
二、获取手机IP
三、UDP广播
3.1 发送
3.2 接收
四、作为知识基础的IP地址知识
一、什么是UDP广播
UDP广播分两种:目标地址为255.255.255.255的物理广播,发送给物理网络的所有设备,只要物理联通就能收到,不限网段,但是不能穿过路由器(很显然,如果穿过路由器就会传播给全网络了);目标地址为XXX.XXX.XXX.255(C类)的网段广播,发送给同网段的所有设备,能穿过路由器,同一物理网络的其它网段主机接收不到。
我用手机热点和设备通讯,实测结果如下:
//物理网络广播,对方网段广播,对方收不到但能发送给这里
//双方都物理网络广播,仍然是对方收不到但能发送给这里
//网段广播,对方物理网络广播,能收能发
//双方都网段广播,能收能发
上面的“这里”是手机,开启热点,“对方”是连接到热点的PC。从结果看物理网络广播是不可靠的,还是用网段广播比较好。
二、获取手机IP
从上面分析我们知道,最好使用网段广播,那么就必须先知道目标网段。目标网段可以根据自身的IP地址推算,热点一般都是C类地址,最后一个字节改为255即可。
代码如下:
public String getLocalIPAddress() {
Enumeration<NetworkInterface> enumeration = null;
try {
enumeration = NetworkInterface.getNetworkInterfaces();
} catch (Exception e) {
error_msg.append(e.toString());
}
if (enumeration != null) {
StringBuilder sb = new StringBuilder();
// 遍历所用的网络接口
while (enumeration.hasMoreElements()) {
NetworkInterface nif = enumeration.nextElement();// 得到每一个网络接口绑定的地址
Enumeration<InetAddress> inetAddresses = nif.getInetAddresses();
// 遍历每一个接口绑定的所有ip
if (inetAddresses != null)
while (inetAddresses.hasMoreElements()) {
InetAddress ip = inetAddresses.nextElement();
if (!ip.isLoopbackAddress() && isIPv4Address(ip.getHostAddress())) {
sb.append(ip.getHostAddress());
}
}
}
return sb.toString();
}
return "";
}
/**
* Ipv4 address check.
*/
private static final Pattern IPV4_PATTERN = Pattern.compile("^(" +
"([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}" +
"([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$");
/**
* Check if valid IPV4 address.
*
* @param input the address string to check for validity.
* @return True if the input parameter is a valid IPv4 address.
*/
public static boolean isIPv4Address(String input) {
return IPV4_PATTERN.matcher(input).matches();
}
这段代码是网上搜来的,我稍微改了一下,直接返回一个字符串,不过拼接的时候没有加分隔符,正式使用最好是返回一个列表。
手机可能会返回两个IP地址,一个是移动网络的地址,一个是热点的地址,移动网络的地址通常是10网段,热点地址通常是192.168网段,但是——所谓的热点固定是“192.168.43.*”网段的说法已经过时了。
将192开头的地址的最后一段改成255就是热点的网段地址,对这个网段广播即可。
三、UDP广播
3.1 发送
注意,网络发送接收因为可能会阻塞,不能在UI线程运行,必须创建子线程。
String all_ip = getLocalIPAddress();
InetAddress ip = InetAddress.getByName("192.168.???.255");
Log(ip.toString());
{
Log("发送数据 ");
DatagramSocket sender = new DatagramSocket();
DatagramPacket dpSend = new DatagramPacket(all_ip.getBytes(), all_ip.getBytes().length, ip, 6000);
sender.send(dpSend);
Log("数据发送成功");
sender.close();
}
设置IP要根据前面获取到的实际IP网段(代码中的问号处),我的手机是179,不是传说的43。第一行就是根据前面的获取IP的方法获得IP列表,不过我只是测试,所以没有进行拆解。
发送UDP无所谓自身的IP端口,只需要目标IP和端口即可。
3.2 接收
接收数据需要指定接收端口号,同时提供接收缓冲区。
Log("接收数据");
DatagramSocket receiver = new DatagramSocket(6000);
byte[] buffer = new byte[10];
DatagramPacket dpReceive = new DatagramPacket(buffer, buffer.length);
receiver.receive(dpReceive);
String datamsg = new String(buffer);
Log("收到数据" + datamsg);
Log("关闭接收");
receiver.close();
这里只接收了一次,只提供了很小的缓冲区,实际使用要使用循环来多次接收并提供足够大的缓冲区。
在连接热点的PC上使用的是UDP测试软件,使用同样的地址和端口,与手机互相发送接收成功。
四、作为知识基础的IP地址知识
发现很多人对TCP/IP网络和IP地址的相关知识不是很扎实,配网络的时候不知道子网掩码、网关是什么意思,下面列出几个要点:
- IP地址分两部分:网段号和主机号,A、B、C类地址分别指1字节网段号、2字节网段号和3字节网段号,同网段可以理解为“应该”连接在同一个物理网络上,可以直达,不需要经过“网关”
- 子网掩码就是从IP地址上“掩掉”网段号,只保留主机号的一个值,怎么个“掩法”不清楚,反正看上去就是网段号的位全为1,所以对于三个字节网段号的子网掩码就是“255.255.255.0”,255的二进制就是所有位均为1
- “网关”是本网段和其它网段之间的桥梁,如果要访问的目标地址不是同一个网段,则会被发给网关,所以,两台设备设置为同一个网段,网线直连或通过交换机、HUB连接,是不需要设置网关的。而且,如果是通过网线连接在无线路由器上,无线路由器的设置是无关紧要的(不用理睬路由器的DHCP服务分配的网段,使用静态地址),物理网口之间提供了物理连接,并不需要路由功能介入
- DNS是个软件服务,用来实现域名到IP地址的查询。网络配置时填写的是DNS服务器的地址,如果有就填,没有就不填,填网络接入商提供的可以,填自建的可以,填任何一个能访问到的也可以
- ping不通不一定是网络不通,可能只是没有开启ping服务(不对ping请求做出应答),但是硬件设备一般是允许ping的(服务器、个人电脑喜欢禁止ping),所以一般就是IP地址设置问题
- 常见的10\176.16~31\192.168开头的网段是局域网保留地址,不允许出现在公网,如果你能上网一定是网络接入商做了NAT转换
(这里是结束)