2021.11.11 晴
一、介绍背景和需求
根据IP得到位置
IP 分析 归属地信息 , 查找在毫秒内完成
IP地址库,公网都是开放的
IANA : 国际组织,负责公网IP维护分发
技术栈
Eclipse ,JavaSE中面向对象、IO流、二分法算法、base64编码、工具类封装目标
二、相关问题
通过开发IP地址归属地查询平台,我们需要对JavaSE综合技术有所提升,增强实战能力。学习完该项目我们应该具备如下能力:
面向对象程序设计
工具类封装与使用写法
文件IO流
字符串处理
二分法查找
IP地址的不同形式的使用强化项目思维
不管好方法还是差方法,先尝试把功能实现
再考虑对代码进行优化,最优为止
三、主要思路解析提供的地址库字符串,为结构化数据格式
基于结构化数据构建成某个数据结构,加速给定IP地址的查找速度
封装成响应的工具类API,开放其响应的方法,即给定IP地址可以再ms内计算得到其位置信息
工具类只有一个入参一个出参
入参是IP
出参是地址
四、研发项目分类应用开发类项目
解决某些特定功能而开发的应用软件,比如QQ,微信,Eclipse等
Web开发类 项目
以B/S架构为主,也就是网页形式访问的在线系统,如各类官网等
中小型应用类开发项目的流程需求概述 : 需求描述,说清楚你要干什么,为什么做这个
在互联网公司中,根据IP地址获取归属地信息是非常广泛的,开发的这个项目就能解决这个问题
需求分析 : 需要根据需求概述,用技术角度来看一下这个项目是否可行
可行性一定是可以做的
需求分析的梳理
三方面 : 输入,输出,已具备的物料(前置条件)
输入 : 给定任意一个IP地址
输出 : 返回IP对应的归属地
前置条件 :
IP地址库
JavaSE
面向对象
IO
常用类
二分法
正则表达式校验
开发步骤拆分,可以理解为解耦,把一个拆分为多个
读取IP地址库
解析IP地址每行数据,进行结构化
找到无结构化数据的规则,进行结构化处理
简单来说,就是根据需求,生成实体类
Entity/model : 实体类,一般该类和数据库表是一一对应的
DTO : 先不管
Pojo : 无特殊意义,纯粹的业务对象
把对象加入List中
转换为数组,方便二分法操作
为什么不直接转换为数组?
数组长度问题,不清楚有多少行,有多少空行等
ArrayList转数组,不会消耗很多时间,因为底层就是数组
解决二分法查找的技术问题
完成比较逻辑
对外提供访问接口
测试
细节开发与风险控制
BUG修复,调优,标准化
正式上线
项目总结,项目复盘·
package com.controller;
import java.util.Scanner;
import com.manager.DataProcessManager;
public class SystemController {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("请输入IP : ");
String ip = scanner.nextLine();
long startTime = System.currentTimeMillis();
String location = DataProcessManager.getLocation(ip);
long endTime = System.currentTimeMillis();
System.out.println(location + " : " + (endTime - startTime));
}
}
}
package com.manager;
import com.pojo.IPAndLocationPojo;
import com.util.FileOperatorUtil;
import com.util.IPUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class DataProcessManager {
private static IPAndLocationPojo[] ipLocationPojoArr = null;
static {
// 地址库文件
String ipLibrartPath = "ip_location_relation.txt";
// 字符编码
String encoding = "utf-8";
// 获取数据
List ipAndLocationPojos = null;
try {
ipAndLocationPojos = DataProcessManager.getPojoList(ipLibrartPath,
encoding);
} catch (IOException e) {
e.printStackTrace();
}
// 转换为数组
ipLocationPojoArr = DataProcessManager
.convertListToArrayAndSort(ipAndLocationPojos);
}
public static String getLocation(String ip) {
int index = DataProcessManager.binaraySearch(ipLocationPojoArr, ip);
return ipLocationPojoArr[index].getLocation();
}
public static int binaraySearch(IPAndLocationPojo[] ipLocationPojoArr,
String ip) {
// 转换为long类型
long ipLong = IPUtil.ipToLong(ip);
int startIndex = 0;
int endIndex = ipLocationPojoArr.length - 1;
int m = (endIndex + startIndex) / 2;
while (startIndex <= endIndex) {
// 大于等于起始 小于等于结束 说明找到了
// 小于起始 在前半截
// 大于结束 在后半截
if (ipLong > ipLocationPojoArr[m].getEndIPLong()) {
startIndex = m + 1;
} else if (ipLong < ipLocationPojoArr[m].getStartIPLong()) {
endIndex = m - 1;
} else {
return m;
}
m = (startIndex + endIndex) / 2;
}
return -1;
}
public static IPAndLocationPojo[] convertListToArrayAndSort(
List pojoList) {
// 转换为数组
IPAndLocationPojo[] ipLocationPojoArr = new IPAndLocationPojo[pojoList
.size()];
pojoList.toArray(ipLocationPojoArr);
// 排序
Arrays.sort(ipLocationPojoArr);
return ipLocationPojoArr;
}
public static List getPojoList(String ipLibrartPath,
String encoding) throws IOException {
List lineList = FileOperatorUtil.getLineList(ipLibrartPath,
encoding);
List ipAndLocationPojos = new ArrayList();
// 遍历 获取每一行
for (String string : lineList) {
if (string == null || string.trim().equals("")) {
continue;
}
// 以t分割,得到三个列
String[] columnArray = string.split("t");
// 创建结构化对象
IPAndLocationPojo ipAndLocationPojo = new IPAndLocationPojo(
columnArray[0], columnArray[1], columnArray[2]);
// 保存到集合中
ipAndLocationPojos.add(ipAndLocationPojo);
}
return ipAndLocationPojos;
}
}
package com.pojo; import com.util.IPUtil; public class IPAndLocationPojo implements Comparable{ @Override public String toString() { return "IPAndLocationPojo [startIP=" + startIP + ", endIP=" + endIP + ", location=" + location + "]"; } private String startIP; // 衍生字段 private long startIPLong; private long endIPLong; private String endIP; public long getStartIPLong() { return startIPLong; } public void setStartIPLong(long startIPLong) { this.startIPLong = startIPLong; } public long getEndIPLong() { return endIPLong; } public void setEndIPLong(long endIPLong) { this.endIPLong = endIPLong; } private String location; public String getStartIP() { return startIP; } public void setStartIP(String startIP) { this.startIP = startIP; } public String getEndIP() { return endIP; } public void setEndIP(String endIP) { this.endIP = endIP; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public IPAndLocationPojo(String startIP, String endIP, String location) { super(); this.startIP = startIP; this.endIP = endIP; this.location = location; this.startIPLong = IPUtil.ipToLong(startIP); this.endIPLong = IPUtil.ipToLong(endIP); } public IPAndLocationPojo() { super(); } @Override public int compareTo(IPAndLocationPojo o) { // 因为IP段没有交集,所以使用起始和结束 比较 都是可以的 long result = startIPLong - o.startIPLong; if (result > 0) { return 1; } else if (result < 0) { return -1; } else { return 0; } } }
package com.util;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;
public class FileOperatorUtil {
public static List getLineList(String txtFilePath, String encoding)
throws IOException {
List lineLine = new ArrayList();
// 字节流
FileInputStream fis = new FileInputStream(txtFilePath);
// 转换为字符流
Reader reader = new InputStreamReader(fis, encoding);
// 封装缓冲流
BufferedReader br = new BufferedReader(reader);
String temp = null;
while ((temp = br.readLine()) != null) {
lineLine.add(temp);
}
// 关闭资源
br.close();
return lineLine;
}
}
package com.util;
public class IPUtil {
public static void main(String[] args) {
long x = ipToLong("120.211.144.131");
System.out.println(x);
String s = longToIp(x);
System.out.println(s);
}
public static Long ipToLong(String ipStr) {
long ipLong = 0;
if (ipStr != null && ipStr.length() > 0) {
// 将ip(点分十进制的形式 a.b.c.d) 地址按.分割
String[] ipSplit = ipStr.split("\.");
try {
if (ipSplit.length != 4) {
throw new Exception("IP Format Error");
}
for (int i = 0; i < ipSplit.length; i++) {
int temp = Integer.parseInt(ipSplit[i]);
ipLong += temp * (1L << (ipSplit.length - i - 1) * 8);
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
}
return ipLong;
}
public static String longToIp(long ipLong) {
StringBuffer ipStr = new StringBuffer();
try {
if (ipLong < 0) {
throw new Exception("Can not to IP...");
}
// 最高8位,直接右移24位
ipStr.append((ipLong >>> 24));
ipStr.append(".");
// 将高8位设置0,然后右移16位
ipStr.append(((ipLong & 0x00FFFFFF) >>> 16));// 获得高8位,6个f对应的是24个1,最高8位设置空为0,之后右移16位将前面多余的16位去掉,以下类推即可
ipStr.append(".");
// 将高16位设置0,然后右移8位
ipStr.append((ipLong & 0x0000FFFF) >>> 8); // 前16位
// 设置0,移除低8位,16个1,高16位设置为0
ipStr.append(".");
// 将高24位设置0
ipStr.append(ipLong & 0x000000FF); // 前24位 设置0,留低8位,8个1,高24位设置为0
} catch (Exception e) {
e.printStackTrace();
}
return ipStr.toString();
}
}
进阶修改:(将数据存储进数据库,直接用sql语句从数据库中查找)
package practice03.controller;
import java.util.Scanner;
import practice03.manager.DataProcessManager;
public class SystemController {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("请输入IP : ");
String ip = scanner.nextLine();
long startTime = System.currentTimeMillis();
String location=DataProcessManager.getLocation(ip);
long endTime = System.currentTimeMillis();
System.out.println("地址为:"+location);
System.out.println("运行时间: " + (endTime - startTime));
}
}
}
package practice03.manager;
import practice.util.DBUtil;
import practice03.pojo.IPAndLocationPojo;
import practice03.util.FileOperatorUtil;
import practice03.util.IPUtil;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class DataProcessManager {
private static IPAndLocationPojo[] ipLocationPojoArr = null;
static {
// 地址库文件
String ipLibrartPath = "ip_location_relation.txt";
// 字符编码
String encoding = "utf-8";
// 获取数据
List ipAndLocationPojos = null;
try {
ipAndLocationPojos = DataProcessManager.getPojoList(ipLibrartPath,
encoding);
} catch (IOException e) {
e.printStackTrace();
}
Connection conn = null;
PreparedStatement stmt = null;
String sql="insert into ip(startIp,endIp,location) values (?,?,?)";
try {
conn = DBUtil.getConnection();
stmt=conn.prepareStatement(sql);
for (IPAndLocationPojo ipAndLocationPojo : ipAndLocationPojos) {
stmt.setLong(1, ipAndLocationPojo.getStartIPLong());
stmt.setLong(2, ipAndLocationPojo.getEndIPLong());
stmt.setString(3, ipAndLocationPojo.getLocation());
stmt.executeUpdate();
}
} catch (Exception e) {
e.printStackTrace();
}finally{
DBUtil.close(stmt);
DBUtil.close(conn);
}
}
public static String getLocation(String ip) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
String canshu=null;
long ip1=IPUtil.ipToLong(ip);
String sql="select location from ip where startIp<"+ip1 +" and endIp >"+ip1+";";
try {
conn = DBUtil.getConnection();
stmt = conn.createStatement();
rs=stmt.executeQuery(sql);
while(rs.next()){
canshu=rs.getString("location");
}
} catch (Exception e) {
e.printStackTrace();
}finally{
DBUtil.close(rs);
DBUtil.close(stmt);
DBUtil.close(conn);
}
return canshu ;
}
public static List getPojoList(String ipLibrartPath,
String encoding) throws IOException {
List lineList = FileOperatorUtil.getLineList(ipLibrartPath,
encoding);
List ipAndLocationPojos = new ArrayList();
// 遍历 获取每一行
for (String string : lineList) {
if (string == null || string.trim().equals("")) {
continue;
}
// 以t分割,得到三个列
String[] columnArray = string.split("t");
// 创建结构化对象
IPAndLocationPojo ipAndLocationPojo = new IPAndLocationPojo(
columnArray[0], columnArray[1], columnArray[2]);
// 保存到集合中
ipAndLocationPojos.add(ipAndLocationPojo);
}
return ipAndLocationPojos;
}
}



