公网IP地址获取:移动网络IP、Wifi IP

前言

因为项目中经常会遇到要上传一系列设备信息的功能,为了方便使用,所以就拆分成以下系列文章来单独介绍如何获取各类设备信息

1. 移动流量IP地址获取

通过NetworkInterface的getNetworkInterfaces()方法和getInetAddresses()方法可以得到该节点的所有IP地址

1.1 步骤

  • 通过NetworkInterface.getNetworkInterfaces()获取该机器上关于NetworkInterface的枚举
  • 遍历该枚举,通过getInetAddress()获取该节点的所有IP地址
  • 从IP地址列表中获取是IPV4且不是回路地址的正确IP地址

1.2 具体实现

private fun getMobileIP(): String? {
    
    
    try {
    
    
        //返回本机的所有接口,枚举类型;
        //至少包含一个元素,代表回路isLoopbackAddress;
        //只支持该机器内实体间能进行通信的接口
        //getNetworkInterfaces()+getInetAddresses()可以获取该节点的所有IP地址
        val networkInterfaceEnumeration = NetworkInterface.getNetworkInterfaces()
        //枚举进行遍历
        while (networkInterfaceEnumeration.hasMoreElements()) {
    
    
            val networkInterface = networkInterfaceEnumeration.nextElement()

            val inetAddressEnumeration = networkInterface.inetAddresses
            //枚举进行遍历
            while (inetAddressEnumeration.hasMoreElements()) {
    
    
                val inetAddress = inetAddressEnumeration.nextElement()
                //当不是回路地址且是IPV4时
                if (!inetAddress.isLoopbackAddress && inetAddress is Inet4Address) {
    
    
                    return inetAddress.getHostAddress()
                }
            }
        }
    } catch (e: SocketException) {
    
    
        return null
        e.printStackTrace()
    }
    return null
}

::1		IPv6形式,对应IPV4的127.0.0.1
127.0.0.1
fe80::c4eb:7d14:a746:a087%wlan0
192.168.20.20

1.3 注意

该方法仅适用于只开启了移动流量未开启WIFI的时候使用,因为我们在遍历并判断是IPV4的地址时,获取到了第一个就直接返回了,而且该方法获取到的也是内网A类地址

while (inetAddressEnumeration.hasMoreElements()) {
    
    
    val inetAddress = inetAddressEnumeration.nextElement()
    //当不是回路地址且是IPV4时
    if (!inetAddress.isLoopbackAddress && inetAddress is Inet4Address) {
    
    
        return inetAddress.getHostAddress()
    }
}

当同时连接WIFI和移动流量时,将其打印,可以得到以下地址

10.98.193.41
192.168.137.21

可以看到,其实是有两个IP的,一个是A类的内网地址,是移动流量的,另一个则是Wifi的C类内网地址。

如果我们想要移动流量WIFI同时连接时使用,那么就将上面的判断改一下,去除掉C类地址后,得到的就是移动运营商的内网IP。

2. Wifi下IP地址获取

2.1 内网IP地址获取

2.1.1 方法一

通过小结1中的方法获取

2.1.2 方法二

通过WifiManager来获取Wifi信息

2.1.2.1 所需权限

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

2.1.2.2 步骤

  • 通过WifiManager的getConnectionInfo()方法来获取WifiInfo信息
  • 通过WifiInfo的getIpAddress()方法获取IP地址
  • 将Int类型的IPv4转为String类型

2.1.2.3 具体实现

private fun getWifiIP(): Int {
    
    
    val wifiManager = applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager
    val wifiInfo = wifiManager.connectionInfo
    return wifiInfo.ipAddress
}
private fun intIP2StringIP(ip: Int): String? {
    
    
    return (ip and 0xFF).toString() + "." +
    (ip shr 8  and 0xFF) + "." +
    (ip shr 16 and 0xFF) + "." +
    (ip shr 24 and 0xFF)
}
-------------------------------------IP转Int数值------------------------------------------
192(10)=1100 0000(2)
168(10)=1010 1000(2)
1(10)  =0000 0001(2)
100(10)=0110 0100(2)

连在一起就是
11000000101010000000000101100100
对应的int数字是-1062731420

192左移2411000000 00000000 00000000 00000000
168左移1600000000 10101000 00000000 00000000
1  左移800000000 00000000 00000001 00000000
100左移000000000 00000000 00000000 01100100
-----------------------------------------Int转IP------------------------------------------
1100 0000 1010 1000 0000 0001 0110 0100	(-1062731420)

右移24(-64)									 and 1111 1111  后为:
1111 1111 1111 1111 1111 1111 1100 0000			192
右移16(-16216)
1111 1111 1111 1111 1100 0000 1010 1000			168
右移8(-4151295)
1111 1111 1100 0000 1010 1000 0000 0001			1
右移0(-1062731420)
1100 0000 1010 1000 0000 0001 0110 0100			100


位运算,只能是Int和Long类型,
shl	左移位<<
shr 右移位>>(用符号位的值来填充)		1是负数0是正数

2.2 外网IP地址获取

2.2.1 思路

在本地,通过WifiManager获取到是内网IP,无法获取到外网IP,需要借助服务器,所以我们通过网络请求来获取

这里我们通过Get请求访问http://pv.sohu.com/cityjson搜狐的地址,即可得到以下内容

var returnCitySN = {
    
    
    "cip": "202.97.159.66",
    "cid": "140100",
    "cname": "山西省太原市"
};

对该字符串进行解析即可得到本机的外网IP

val start: Int = msg.indexOf("{")
val end: Int = msg.indexOf("}")
val ip = JSONObject(msg.substring(start, end + 1)).get("cip") as String

在具体项目中,可以让后端出个接口来返回我们的外网IP,毕竟别人的接口什么时候不通了也说不准。

2.2.2 具体实现

interface IPService {
    
    
    @GET
    fun getIP(@Url url: String): Call<ResponseBody>
}
val retrofit= Retrofit.Builder()
.baseUrl("http://a")
.build()
val api =retrofit.create(IPService::class.java)
api.getIP("http://pv.sohu.com/cityjson").enqueue(object :Callback<ResponseBody>{
    
    
    override fun onResponse(call: Call<ResponseBody>, response: Response<ResponseBody>) {
    
    
        response.body()?.string()?.let {
    
    
            val start: Int = it.indexOf("{")
            val end: Int = it.indexOf("}")
            val ip = JSONObject(it.substring(start, end + 1)).get("cip") as String
            Log.i(TAG, "网络请求下的IP为:$ip")
        }
    }
    override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
    
    
        Toast.makeText(this@IPActivity,"获取IP失败:${
      
      t.message}",Toast.LENGTH_SHORT).show()
    }
})

3. 判断网络类型

判断是移动网络还是Wifi,这里我们用到了ConnectivityManager类,该类可以监测网络的连接

3.1所需权限

ACCESS_NETWORK_STATE

3.2 方法

getActiveNetwork()只能Android6以上才能调用,所以Android 6以下,我们使用getActiveNetworkInfo()方法来获取。

/**
* 判断是移动网络还是Wifi
* 0:未知网络
* 1:移动网络
* 2:Wifi
* 3:没有网络
*/
private fun judgeNetType():Int{
    
    
    val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M){
    
    
        //安卓6以下
        val mNetworkInfo = connectivityManager.activeNetworkInfo
        return if (mNetworkInfo != null) {
    
    
            when (mNetworkInfo.type) {
    
    
                ConnectivityManager.TYPE_MOBILE -> {
    
    
                    1
                }
                ConnectivityManager.TYPE_WIFI -> {
    
    
                    2
                }
                else -> {
    
    
                    0
                }
            }
        }else{
    
    
            3
        }
    }else{
    
    
        val network = connectivityManager.activeNetwork
        if (network != null) {
    
    
            val nc = connectivityManager.getNetworkCapabilities(network)
            return if (nc != null){
    
    
                when {
    
    
                    nc.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> {
    
    
                        1
                    }
                    nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> {
    
    
                        2
                    }
                    else -> {
    
    
                        0
                    }
                }
            }else{
    
    
                3
            }
        }else{
    
    
            return 3
        }
    }
}

4. 总结

获取IP的方式无非就是以下3种

  • NetworkInterface.getNetworkInterfaces()+getInetAddress()可以得到该节点的所有IP地址,筛选是IPv4的即我们所要的IP,得到的都是内网IP

  • 通过WifiManager的getConnectionInfo()+getIpAddress()即可得到WIFI下的IP地址,同样,得到的是内网IP

  • 通过访问外部服务器,由服务器通过接口告诉我们自己的外网IP地址

所以我们在项目中使用时,如果想要获取外网IP,只能通过访问外部服务器;如果想要获取WIFI的内网IP,第一二种方式均行;想要获取移动网络的的内网IP,用第一种方式,并筛选是10段的内网IP(不同地方的运营商可能会有区别)。

项目地址

如果本文对你有帮助,请别忘记点赞start,如果有不恰当的地方也请提出来,下篇文章见。

5. 参考文章

Java实现ip地址和int数字的相互转换

猜你喜欢

转载自blog.csdn.net/Myfittinglife/article/details/121636941
IP