android开发:获取手机IP和UDP广播

初级代码游戏的专栏介绍与文章目录-CSDN博客

UDP广播在通讯双方互相不知道对方IP的情况下很有用。这种情形我们也可以用遍历网段来实现,但是比较粗暴,如果网段比较大,不是最多256台主机的C类网段的话,很难做遍历。

UDP广播是解决这种问题的标准方案。

注意,广播和多播是不同的,广播是同时发送给所有主机,而多播是一个特殊的组,必须明确加入退出。

目录

一、什么是UDP广播

二、获取手机IP

三、UDP广播

[3.1 发送](#3.1 发送)

[3.2 接收](#3.2 接收)

四、作为知识基础的IP地址知识


一、什么是UDP广播

UDP广播分两种:目标地址为255.255.255.255的物理广播,发送给物理网络的所有设备,只要物理联通就能收到,不限网段,但是不能穿过路由器(很显然,如果穿过路由器就会传播给全网络了);目标地址为XXX.XXX.XXX.255(C类)的网段广播,发送给同网段的所有设备,能穿过路由器,同一物理网络的其它网段主机接收不到。

我用手机热点和设备通讯,实测结果如下:

复制代码
            //物理网络广播,对方网段广播,对方收不到但能发送给这里
            //双方都物理网络广播,仍然是对方收不到但能发送给这里
            //网段广播,对方物理网络广播,能收能发
            //双方都网段广播,能收能发

上面的"这里"是手机,开启热点,"对方"是连接到热点的PC。从结果看物理网络广播是不可靠的,还是用网段广播比较好。

二、获取手机IP

从上面分析我们知道,最好使用网段广播,那么就必须先知道目标网段。目标网段可以根据自身的IP地址推算,热点一般都是C类地址,最后一个字节改为255即可。

代码如下:

java 复制代码
    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线程运行,必须创建子线程。

java 复制代码
            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 接收

接收数据需要指定接收端口号,同时提供接收缓冲区。

java 复制代码
                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转换

(这里是结束)

相关推荐
阿巴斯甜9 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker9 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952710 小时前
Andorid Google 登录接入文档
android
黄林晴11 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android