什么是IP地址?
IP地址
IP地址是IP协议中还有一个非常重要的内容,那就是给因特网上的每台计算机和其它设备都规定了一个唯一的地址,叫做“IP地址”。由于有这种唯一的地址,才保证了用户在连网的计算机上操作时,能够高效而且方便地从千千万万台计算机中选出自己所需的对象来。
IP地址就像是我们的家庭住址一样,如果你要写信给一个人,你就要知道他(她)的地址,这样邮递员才能把信送到。计算机发送信息就好比是邮递员,它必须知道唯一的“家庭地址”才能不至于把信送错人家。只不过我们的地址是用文字来表示的,计算机的地址用二进制数字表示。
如何查看我们的IP地址呢
首先我们需要了解 两个概念:
公网IP:
公网也称外网,互联网,公网IP地址是分配给连接到 Internet 的设备的地址。要连接到 Internet,设备必须具有唯一的IP地址,以允许其他节点和设备找到它并建立连接。公共IP地址可以分配给Web服务器、电子邮件服务器,甚至是个人的个人计算机。
私网IP:
私网也称局域网,内网,私网IP地址是您的网络路由器分配给您的设备的地址。同一网络中的每个设备都分配有一个唯一的私有 IP 地址(有时称为私有网络地址)——这是同一内部网络中的设备相互通信的方式。
简单来说:我们平常家里连接使用的WIFI,校园连接的WIFI使用的都是内网IP,我们进行网页访问的时候,是要将私网ip转换为公网ip的,而路由器的主要工作就是这个,那么我们使用的运营商网络4G、5G又属于什么呢,查询我们手机上的运营商网络IP,一般会有两个IP一个是Ipv4另一个是Ipv6,以100开头,这个是Ipv4,输入到ip查询会显示到 运营商及NAT保留IP地址,另外一个Ipv6我们可以通过它查询到我们的体位置信息,也就是说现在我们使用的移动网络事实上还是属于内网的,运营商网络是使用的类似与路由器的网络设备将我们的网络形成了局域网,通过NAT技术实现网络通信,为什么要使用这种方式,主要原因是Ipv4地址已经枯竭,通俗点理解就是我们使用的运营商网络不会为我们单独分配公网ip,我们使用的是私网ip,多个用户共用一个公网ip进行网络访问。
如何进行私网ip地址查看:
如果我们使用的是WIFI那么很简单,只需要点击WIFI详情就可以看到,这里我们查询到的是私网ip,手机上也是相同的原理,我们连接WIFI时的ip地址也是私网IP。但是通过私网ip我们是无法查询到具体的位置的,需要获取公网ip。
获取ip地址
方法一:
使用第三方API,通过第三方接口我们可以很容易获取我们的外网ip,好处是网上接口多,容易找到,缺点是API接口存在失效的可能性,这里我推荐1个API接口:
能获取省份信息,ip地址,网络运营商
https://gamematrix.qq.com/sdk/v2/get_gateway
下面演示我们的第一种方式:
这种获取ip的原理很简单,直接从接口中获取JSON数据转换成字符串即可,因为这个接口返回的就是ip地址我们直接返回就行,不需要对Json数据进行额外的取key操作,这了用了阿里巴巴的fastjson 要导入依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.60</version>
</dependency>
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.2.3</version>
<classifier>jdk15</classifier><!-- 指定jdk版本 -->
</dependency>
import net.sf.json.JSONObject;
import org.lionsoul.ip2region.xdb.Searcher;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;
import java.util.concurrent.TimeUnit;
// 获取本机ip
public class IpUtil {
//获取Json数据
public static String getIp(String url) {
StringBuilder json0 = new StringBuilder();
try {
URL urlObject = new URL(url);
//从地址中获取数据
URLConnection uc = urlObject.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(uc.getInputStream(),"utf-8"));
String inputLine = null;
while ( (inputLine = in.readLine()) != null) {
//按行读取
json0.append(inputLine);
}
in.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return json0.toString();
}
测试:
@Test
public void testIPUtils(){
String ip2 = IpUtil.getIp("https://gamematrix.qq.com/sdk/v2/get_gateway");
System.out.println("方式二"+ip2);
}
结果:上百度查看我们获取的ip是否正确
成功获取了ip地址。
方法二:
通过http请求解析ip地址:
通过这种方式我们可以获取ip地址,需要注意的是,如果是本地的请求获取的是内网ip
public static String getIpAddr(HttpServletRequest request) {
String ipAddress = null;
try {
ipAddress = request.getHeader("x-forwarded-for");
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getHeader("WL-Proxy-Client-IP");
}
if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
ipAddress = request.getRemoteAddr();
if (ipAddress.equals("127.0.0.1")) {
// 根据网卡取本机配置的IP
try {
ipAddress = InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
// 通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
if (ipAddress != null) {
if (ipAddress.contains(",")) {
return ipAddress.split(",")[0];
} else {
return ipAddress;
}
} else {
return "";
}
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
测试:我们将springboot项目部署到公网上,访问测试代码可获得ip
@RequestMapping(value = "/test", method = RequestMethod.GET)
public String test(HttpServletRequest request){
//获取IP地址
String ipAddress = IpUtil.getIpAddr(request);
System.out.println("ip-------------"+ipAddress);
return "front/index";
}
在Xshell查看:
以上两种方式都可以获取本机ip,推荐使用第二种,网上的api可能会失效。
获取ip属地:
我们通过上文的https://gamematrix.qq.com/sdk/v2/get_gateway就已经可以获取ip地址了,不过显示到的是省份,下面我们介绍两种获取ip属地的方式,国内详细到市,国外显示国家。
测试以ip:112.67.251.91为例
百度查询:
方式一:IP地址库ip2region
推荐使用GitHub上免费的开源地址库,ip2region,简单好用,支持:java、C#、php、c、python、nodejs、php扩展(php5和php7)、golang、rust、lua、lua_c, nginx
GitHub地址:https://github.com/lionsoul2014/ip2region
如何使用:
官方文档:https://github.com/lionsoul2014/ip2region/tree/master/binding/java
第一步:找到数据库文件,ip2region.xdb,文件在data里
将文件放到我们的resource中
第二步,导入依赖在pom.xml:
<dependency>
<groupId>org.lionsoul</groupId>
<artifactId>ip2region</artifactId>
<version>2.6.5</version>
</dependency>
第三步编写IpUtil类:
//方式一:使用ip2region获取Ip属地
public static String ipAddress(String ip) {
String dbPath = "";
//读取文件
File file = new File("src/main/resources/ip2region.xdb");
dbPath = file.getPath();
String region = "";
Searcher searcher = null;
try {
searcher = Searcher.newWithFileOnly(dbPath);
} catch (IOException e) {
System.out.printf("failed to create searcher with `%s`: %s\n", dbPath, e);
return "文件未找到";
}
// 2、查询
try {
long sTime = System.nanoTime();
//查询ip属地
region = searcher.search(ip);
long cost = TimeUnit.NANOSECONDS.toMicros((long) (System.nanoTime() - sTime));
System.out.printf("{region: %s, ioCount: %d, 耗时: %d μs}\n", region, searcher.getIOCount(), cost);
} catch (Exception e) {
System.out.printf("failed to search(%s): %s\n", ip, e);
}
// 3、关闭资源
try {
searcher.close();
} catch (IOException e) {
e.printStackTrace();
}
return region;
}
测试:
@Test
public void testIPUtils1(){
String ipAddress = "112.67.251.91";
System.out.println(IpUtil.ipAddress(ipAddress));
}
结果:
这种基于ip属地库的查询方式效率高于第三方接口,不需要给第三放接口发送请求,节省时间。
方式二:使用第三方API
原理和获取ip地址的原理相同,这里我们推荐两个ip属地查询接口:
//使用百度ip地址查询API
String ipAddressAPI1 = "http://opendata.baidu.com/api.php?query="+ip+"&co=&resource_id=6006&oe=utf8";
//使用太平洋ip地址查询API
String ipAddressAPI2 = "http://whois.pconline.com.cn/ipJson.jsp?ip=+"+ip+"&json=true";
获取地址方法:
//方式二:使用IP归属地查询API
public static String getIpAddress2(String ip){
//使用百度ip地址查询API
String ipAddressAPI1 = "http://opendata.baidu.com/api.php?query="+ip+"&co=&resource_id=6006&oe=utf8";
//使用太平洋ip地址查询API
String ipAddressAPI2 = "http://whois.pconline.com.cn/ipJson.jsp?ip=+"+ip+"&json=true";
StringBuilder json = new StringBuilder();
try {
URL urlObject = new URL(ipAddressAPI1);
//2.从地址中获取数据
URLConnection uc = urlObject.openConnection();
BufferedReader in = new BufferedReader(new InputStreamReader(uc.getInputStream(),"utf-8"));
String inputLine = null;
while ( (inputLine = in.readLine()) != null) {
//按行读取
json.append(inputLine);
}
in.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return json.toString();
}
测试:
@Test
public void testIPUtils2(){
String ipAddress = IpUtil.getIpAddress2("112.67.251.91");
System.out.println("原始数据"+ipAddress);//原始数据
JSONObject jsonObject1 = JSON.parseObject(ipAddress);
System.out.println(jsonObject1);
//Json数据含有数组,先获取数组
JSONArray data = jsonObject1.getJSONArray("data");
//获取数组data第一个数据
JSONObject JsonData=(JSONObject) data.get(0);
Object location = JsonData.get("location");
System.out.println(location);
}
测试结果:
第三放查询ip属地API也是一种不错的方式,需要注意的问题是接口可能会失效,接口对查询次数可能存在限制。