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);
}
/**
* 对外提供的接口,入参IP出参地址
*
* @param ip
* @return
*/
public static String getLocation(String ip) {
int index = DataProcessManager.binaraySearch(ipLocationPojoArr, ip);
return ipLocationPojoArr[index].getLocation();
}
/**
* 业务类对应的二分法
*
* @param ipLocationPojoArr
* 数组
* @param ip
* ip
* @return 索引
*/
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;
}
/**
* 将对象集合转换为对象数组并排序
*
* @param pojoList
* @return
*/
public static IPAndLocationPojo[] convertListToArrayAndSort(
List pojoList) {
// 转换为数组
IPAndLocationPojo[] ipLocationPojoArr = new IPAndLocationPojo[pojoList
.size()];
pojoList.toArray(ipLocationPojoArr);
// 排序
Arrays.sort(ipLocationPojoArr);
return ipLocationPojoArr;
}
/**
* 把读取到的List 转换为 List
*
* @param ipLibrartPath
* @param encoding
* @return
* @throws IOException
*/
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 + "]";
}
/**
* 起始IP
*/
private String startIP;
// 衍生字段
private long startIPLong;
private long endIPLong;
/**
* 结束IP
*/
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 {
/**
* 读取文件,并返回List
*
* @param txtFilePath
* 字符串格式的文件路径
* @param encoding
* 字符编码
* @return
* @throws IOException
*/
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;
/**
* IP和long类型之间的转换
*
*/
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;
}
/**
* 将long型IP地址转换回点分十进制表示的字符串类型
*
* @param ipLong
* @return java.lang.String
* @throws Exception
*/
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;
/**
* 程序入口
*
* @author 天亮教育-帅气多汁你泽哥
* @Date 2021年11月11日 下午4:41:42
*/
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);
}
}
/**
* 对外提供的接口,入参IP出参地址
*
* @param ip
* @return
*/
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 ;
}
/**
* 把读取到的List 转换为 List
*
* @param ipLibrartPath
* @param encoding
* @return
* @throws IOException
*/
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;
}
}