JavaSE-项目小结-IP归属地查询(本地IP地址库)

一、项目介绍

        1. 背景

           IP地址是网络通信中的重要标识,通过分析IP地址的归属地信息,可以帮助我们了解访问来源、用户行为和网络安全等关键信息。例如应用于网站访问日志分析:通过分析访问日志中的IP地址,了解网站访问者的地理位置分布和访问行为,优化网站内容和用户体验。

        2. 需求

        IP分析,返回归属地信息,要求在毫秒内完成。

        3. 涉及技术栈

        Eclipse的使用,JavaSE中面向对象,IO流,二分法查找,集合。

        4. 目的

        通过IP归属地查询项目,巩固javaSE部分所学知识,增强实战能力。

        需具备以下能力:1.面向对象程序设计。

                                     2.工具类的封装与使用。

                                     3. 文件IO流操作。

                                     4. 字符串处理。

                                     5. 二分法查找。

二、主要思路

        1. 读取提供的地址库文件,解析地址库字符串,转换为结构化数据

        2. 封装对应的数据结构,进行查询。

        3. 封装对应的相关工具类。

        4. 对外提供接口,只需要入参和出参(入参:IP地址,出参:归属地)。

三、代码开发

        创建utils包,存放工具类

        创建pojo包,存放实体类

        创建manager包,存储管理类,业务代码

        创建controller包,存储程序入口类

        1.  文件读取,封装为工具类

             编写文件读取工具类,用于读取本地IP地址库文件中的数据。当前类放于Utils包下。

package com.zh.utils;import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.List;public class FileReadUtil {// 默认编码方式private static String defaultencoding = "UTF-8";public static List<String> FileRead(String filePath, String encoding) throws IOException{// 通过类加载器获取流,防止打包后无法获取IP文件InputStream is =  FileReadUtil.class.getClassLoader().getResourceAsStream(filePath);// 字节流// FileInputStream fis = new FileInputStream(filePath);// 转字符流Reader read = new InputStreamReader(is,encoding);// 缓冲流BufferedReader br = new BufferedReader(read);String temp = null;// 集合存储数据List<String> list = new ArrayList<String>();// 逐行读取while((temp = br.readLine()) != null) {list.add(temp);}br.close();return list;}// 提供无参的方法,以默认编码方式为参数,调用有参的方法public static List<String> FileRead(String filePath) throws IOException{return FileRead(filePath,defaultencoding);}}

        2.  抽象Pojo类,用于数据结构化

        根据地址库中的IP地址及其归属地的存储格式(1.0.0.0    1.0.0.255    澳大利亚 亚太互联网络信息中心),创建实体类。实体类属性包括IP起始地址,IP结束地址,归属地,IP起始地址的long类型数值,IP结束地址的long类型数值。IP地址转换为long类型,用于后续对IP地址进行排序。

        实体类实现Comparable接口,用于自定义比较规则,覆写comparaTo方法,根据起始IP地址进行比较。

        实体类实现Serializable接口,可对当前对象进行序列化操作。

package com.zh.pojo;import java.io.Serializable;import com.zh.utils.IPUtil;public class IPAndLocationPojo implements Comparable<IPAndLocationPojo>, Serializable {private static final long serialVersionUID = 1L;/*** 起始IP*/private String startIP;/*** 结束IP*/private String endIP;/*** 归属地*/private String location;/*** 起始地址转long类型*/private long startIPLong;/*** 结束地址转long类型*/private long endIPLong;public IPAndLocationPojo() {super();}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 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 long getStartIPLong() {return startIPLong;}public long getEndIPLong() {return endIPLong;}@Overridepublic String toString() {return "IPAndLocationPojo [startIP=" + startIP + ", endIP=" + endIP + ", location=" + location+ ", startIPLong=" + startIPLong + ", endIPLong=" + endIPLong + "]";}@Overridepublic int compareTo(IPAndLocationPojo o) {long result = this.startIPLong - o.getStartIPLong();if(result > 0) {return 1; // 正序}else if(result < 0) {return -1; // 倒序}else {return 0;}}}

        3. 结构化数据

        调用文件读取工具类,返回一个集合,集合中的每一条数据,即为文件中的一行。

        遍历集合,取出每一条数据,根据制表符(\t)分割,存储到数组中,此时数组中下标为0的位置存储的为IP起始地址的字符串,下标为1的位置存储的是IP结束地址的字符串,下标为2的位置存储的为归属地信息。

        将数组中的数据封装为对象,后添加到ArrayList集合中,此时ArrayList中每一个元素即为一个IP归属地对象。

/*** 结构化数据* @param filePath* @param encoding* @return* @throws IOException*/public static List<IPAndLocationPojo> getPojoList(String filePath, String encoding) throws IOException{// 获取数据List<String> list = FileReadUtil.FileRead(filePath, encoding);// 封装结构化后的数据List<IPAndLocationPojo> pojoList = new ArrayList<IPAndLocationPojo>();// 逐条遍历取出数据for(String string : list) {// 跳过空行if(string == null || string.trim().equals("")) {continue;}// 分割字符串String[] strs = string.split("\t");// 跳过不合规的数组if(strs.length != 3) {continue;}String startIP = strs[0];String endIP = strs[1];String location = strs[2];IPAndLocationPojo ipPojo = new IPAndLocationPojo(startIP, endIP, location);pojoList.add(ipPojo);}return pojoList;		}

        4. 集合转数组并排序

        由于实体类中存储的起始IP和结束IP为字符串类型,默认排序规则不符合要求,所以需要提供工具类,将实体类中起始IP地址和结束IP地址的字符串转换为long类型,为实体类中添加long类型的起始IP和结束IP,并实现Comparable接口,覆写comparaTo方法来自定义比较规则。

        工具类:

package com.zh.utils;import java.util.Scanner;/*** Ip地址转换*/
public class IPUtil {/*** ip地址字符串转long类型* * @param ipString* @return*/public static long ipToLong(String ipString) {String[] str = ipString.split("\\.");return (Long.parseLong(str[0]) << 24) + (Long.parseLong(str[1]) << 16) + (Long.parseLong(str[2]) << 8)+ Long.parseLong(str[3]);}/*** long 类型数转Ip地址* * @param ipLong* @return*/public static String longToIP(long ipLong) {StringBuffer sb = new StringBuffer("");sb.append(String.valueOf(ipLong >> 24)).append(".").append(String.valueOf((ipLong & 0x00ffffff) >> 16)).append(".").append(String.valueOf((ipLong & 0x0000ffff) >> 8)).append(".").append(String.valueOf(ipLong & 0x000000ff));return sb.toString();}
}
/*** 将对象集合转换为数组并排序* @param ipPojoList* @return*/public static IPAndLocationPojo[] IpPojoArray(List<IPAndLocationPojo> ipPojoList) {// 集合转数组IPAndLocationPojo[] ipPojoArray = new IPAndLocationPojo[ipPojoList.size()];ipPojoList.toArray(ipPojoArray);// 数组排序Arrays.sort(ipPojoArray);return ipPojoArray;}

        5. 二分法查找

        根据用户输入的IP地址,采用二分法在已经排序好的数组中,查找IP的归属地信息。

由于IP地址库中存储的IP为一段地址范围,所以每次判断中间索引的起始地址小于用户输入地址后,需要在判断中间索引的结束地址是否大于用户输入IP,如果满足条件说明用户输入的IP地址位于当前中间索引起始地址所在的IP地址段,直接返回中间索引的IP归属地即为用户的IP归属地,

/*** 根据用户输入的地址查寻归属地* @param userIp* @param arr* @return*/public static String searchIP(String userIp, IPAndLocationPojo[] arr) {// 将用户输入的IP转换为long型long userIP = IPUtil.ipToLong(userIp);int height = arr.length - 1;int low = 0;// 二分查找while(low <= height) {int mid =(height + low) / 2;// 当中间索引的起始IP大于用户输入IP时,结束索引等于中间索引-1if(arr[mid].getStartIPLong() > userIP) {height = mid - 1;// 当中间索引的结束IP地址大于等于用户输入IP时,返回当前归属地信息}else if(arr[mid].getEndIPLong() >= userIP){	return arr[mid].getLocation();}else {low = mid + 1;}}return null;	}

        6. 封装接口类

        封装接口,对外只提供获取归属地的方法,入参:IP地址, 出参:归属地

        静态代码块中为数组中数据的初始化工作,保证每次执行只执行一次,不需要重复结构化数据,提高效率。文中代码对排序好的数组执行了序列化操作,也可以不进行序列化。

        序列化工具类:

package com.zh.utils;import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;public class FileSerializationUtil {// 序列化对象public static void SerializationObject(String path, Object arr) {try (FileOutputStream fos = new FileOutputStream(path);BufferedOutputStream bos = new BufferedOutputStream(fos);ObjectOutputStream oos = new ObjectOutputStream(bos);) {oos.writeObject(arr);oos.flush();} catch (Exception e) {e.printStackTrace();}}// 对象反序列化public static Object DeserializationObject(String path) {Object obj = null;try (FileInputStream fis = new FileInputStream(path);BufferedInputStream bis = new BufferedInputStream(fis);ObjectInputStream ois = new ObjectInputStream(bis);){obj = ois.readObject();} catch (Exception e) {e.printStackTrace();}return obj;}}

        IP地址校验工具类:用于校验用户输入IP地址是否符合规范。

package com.zh.utils;import java.util.regex.Matcher;
import java.util.regex.Pattern;// IP地址校验
public class IPCheckUtil {// 正则表达式IP地址验证规则private static final String IP_PATTERN = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$";// 创建匹配模式private static final Pattern pattern = Pattern.compile(IP_PATTERN);// 匹配IP地址public static boolean checkIP(String ipString) {		Matcher matcher = pattern.matcher(ipString);return matcher.matches();}
}

        管理类中代码对外提供接口的方法

private static final String IP_ILLEGAL = "IP地址不合法";private static final String SERIALIZED_FILE_PATH = "./serialized_ip_data.ser";private static IPAndLocationPojo[] arr = null;// 静态语句块只加载一次static {File serializedPath = new File(SERIALIZED_FILE_PATH);if(serializedPath.exists()) {arr = (IPAndLocationPojo[])FileSerializationUtil.DeserializationObject(SERIALIZED_FILE_PATH);}else {// 本地IP地址库String filePath = "ip_location_relation.txt";// 编码方式String encoding = "UTF-8";List<IPAndLocationPojo> list = null;try {// 数据结构化list = DataProcessManager.getPojoList(filePath, encoding);// 转换为数组并排序arr = DataProcessManager.IpPojoArray(list);FileSerializationUtil.SerializationObject(SERIALIZED_FILE_PATH, arr);} catch (IOException e) {e.printStackTrace();}}}/*** 获取IP归属地* @param ipString* @return* @throws IOException*/public static String ipLocationSer(String ipString){// 校验IP地址是否合法if (IPCheckUtil.checkIP(ipString)) {// IP地址合法,查询归属地String ipuser = DataProcessManager.searchIP(ipString, arr);return ipuser;} else {return IP_ILLEGAL;}}

        7. 入口类

        充当程序入口,执行main方法。

package com.zh.controller;import java.io.IOException;
import java.util.List;
import java.util.Scanner;import com.zh.manage.DataProcessManager;
import com.zh.pojo.IPAndLocationPojo;
import com.zh.utils.IPCheckUtil;//入口类
public class SystemController {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);System.out.println("请输入要查询的IP地址:");String ip = null;while((ip = scanner.next())!= null) {// long startTime = System.currentTimeMillis();String location = DataProcessManager.ipLocationSer(ip);System.out.println(location);// System.out.println("耗时: " + (System.currentTimeMillis() - startTime));System.out.println("请输入要查询的IP地址:");}}	
}

             管理类完整代码:

package com.zh.manage;import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;import com.zh.pojo.IPAndLocationPojo;
import com.zh.utils.FileReadUtil;
import com.zh.utils.FileSerializationUtil;
import com.zh.utils.IPCheckUtil;
import com.zh.utils.IPUtil;/*** 管理类*/
public class DataProcessManager {private static final String IP_ILLEGAL = "IP地址不合法";private static final String SERIALIZED_FILE_PATH = "./serialized_ip_data.ser";private static IPAndLocationPojo[] arr = null;// 静态语句块只加载一次static {File serializedPath = new File(SERIALIZED_FILE_PATH);if(serializedPath.exists()) {arr = (IPAndLocationPojo[])FileSerializationUtil.DeserializationObject(SERIALIZED_FILE_PATH);}else {// 本地IP地址库String filePath = "ip_location_relation.txt";// 编码方式String encoding = "UTF-8";List<IPAndLocationPojo> list = null;try {// 数据结构化list = DataProcessManager.getPojoList(filePath, encoding);// 转换为数组并排序arr = DataProcessManager.IpPojoArray(list);FileSerializationUtil.SerializationObject(SERIALIZED_FILE_PATH, arr);} catch (IOException e) {e.printStackTrace();}}}/*** 获取IP归属地* @param ipString* @return* @throws IOException*/public static String ipLocationSer(String ipString){// 校验IP地址是否合法if (IPCheckUtil.checkIP(ipString)) {// IP地址合法,查询归属地String ipuser = DataProcessManager.searchIP(ipString, arr);return ipuser;} else {return IP_ILLEGAL;}}/*** 结构化数据* @param filePath* @param encoding* @return* @throws IOException*/private static List<IPAndLocationPojo> getPojoList(String filePath, String encoding) throws IOException{// 获取数据List<String> list = FileReadUtil.FileRead(filePath, encoding);// 封装结构化后的数据List<IPAndLocationPojo> pojoList = new ArrayList<IPAndLocationPojo>();// 逐条遍历取出数据for(String string : list) {// 跳过空行if(string == null || string.trim().equals("")) {continue;}// 分割字符串String[] strs = string.split("\t");// 跳过不合规的数组if(strs.length != 3) {continue;}String startIP = strs[0];String endIP = strs[1];String location = strs[2];IPAndLocationPojo ipPojo = new IPAndLocationPojo(startIP, endIP, location);pojoList.add(ipPojo);}return pojoList;		}/*** 将对象集合转换为数组并排序* @param ipPojoList* @return*/private static IPAndLocationPojo[] IpPojoArray(List<IPAndLocationPojo> ipPojoList) {// 集合转数组IPAndLocationPojo[] ipPojoArray = new IPAndLocationPojo[ipPojoList.size()];ipPojoList.toArray(ipPojoArray);// 数组排序Arrays.sort(ipPojoArray);return ipPojoArray;}/*** 根据用户输入的地址查寻归属地* @param userIp* @param arr* @return*/private static String searchIP(String userIp, IPAndLocationPojo[] arr) {// 将用户输入的IP转换为long型long userIP = IPUtil.ipToLong(userIp);int height = arr.length - 1;int low = 0;// 二分查找while(low <= height) {int mid =(height + low) / 2;// 当中间索引的起始IP大于用户输入IP时,结束索引等于中间索引-1if(arr[mid].getStartIPLong() > userIP) {height = mid - 1;// 当中间索引的结束IP地址大于等于用户输入IP时,返回当前归属地信息}else if(arr[mid].getEndIPLong() >= userIP){	return arr[mid].getLocation();}else {low = mid + 1;}}return null;	}}

          运行结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/444814.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux进程通信基础

前要&#xff1a;本节内容主要是管道和共享内存块&#xff0c;而且我们需要明白我们所讲的进程通信本质是为了在内存中传递数据&#xff08;看到同一份资源&#xff09;。 而实际上&#xff0c;我们也可以通过磁盘传递数据&#xff0c;但是为什么不这么做呢&#xff1f;很简单&…

【Linux】Daemon守护进程详解

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;Linux系列专栏&#xff1a;Linux基础 &#x1f525; 给大家…

DRV8313和L298N都是电机驱动,一个是驱动三相FOC无刷直流电机的,一个是驱动有刷电机,使stm32控制无刷电机简单入门知识

DRV8313和L298N都是电机驱动器&#xff0c;但它们之间存在一些关键的区别&#xff1a; DRV83131&#xff1a; 由德州仪器&#xff08;TI&#xff09;制造。 具有集成的场效应晶体管&#xff08;FET&#xff09;。 最大电压为65V。 峰值电流为3A。 适用于三相电机驱动。 L298N…

性能测试常用术语

之前在性能测试过程中&#xff0c;对于某些其中的术语一知半解&#xff0c;导致踩了很多坑。这篇博客&#xff0c;就常见的一些性能测试术语进行一次浅析。。。 负载 对被测系统不断施加压力&#xff0c;直到性能指标超过预期或某项资源使用达到饱和&#xff0c;以验证系统的处…

基于FPGA的OFDM基带发射机的设计与实现

文章目录 前言一、OFDM描述二、本系统的实现参照 1.IEEE 802.11a协议主要参数2.不同调制方式与速率 3. IFFT映射关系4. IEEE 802.11a物理层规范5. PPDU帧格式三、设计与实现 1.扰码2.卷积编码与删余3.数据交织4.符号调制5.导频插入6.IFFT变换 7.循环前缀&加窗8.训练序列生成…

kubekey网页版安装k8s集群操作流程

kubekey可以一键拉起k8s集群并完成kubesphere的部署&#xff0c;以后kubekey简称kk。kk 3.2版本以前都是在宿主机上完成对应的创建集群、添加节点、升级等操作的&#xff0c;3.2版本后开始往页面操作的方向演进&#xff0c;kk 3.2版本现在还是alpha&#xff0c;所以不推荐在生产…

java面向对象基础(面试)

一、面向对象基础 1. 面向对象和面向过程的区别 面向过程把解决问题的过程拆成一个个方法&#xff0c;通过一个个方法的执行解决问题。面向对象会先抽象出对象&#xff0c;然后用对象执行方法的方式解决问题。 2.创建一个对象用什么运算符?对象实体与对象引用有何不同? n…

[C++]继承(续)

一、基类和派生类对象赋值转换 在public继承时&#xff0c;父类和子类是一个“is - a”的关系。 子类对象赋值给父类对象/父类指针/父类引用&#xff0c;我们认为是天然的&#xff0c;中间不产生临时对象&#xff0c;也叫作父子类赋值兼容规则&#xff08;切割/切片&#xff…

如何本地搭建Emby影音管理服务并结合内网穿透实现远程访问本地影音库

文章目录 1.前言2. Emby网站搭建2.1. Emby下载和安装2.2 Emby网页测试 3. 本地网页发布3.1 注册并安装cpolar内网穿透3.2 Cpolar云端设置3.3 Cpolar内网穿透本地设置 4.公网访问测试5.结语 1.前言 在现代五花八门的网络应用场景中&#xff0c;观看视频绝对是主力应用场景之一&…

江科大stm32学习笔记9——OLED调试工具

一、OLED OLED&#xff1a;有机发光二极管&#xff0c;供电3~5.5V&#xff0c;0.96寸OLED通信协议&#xff1a;I2C/SPI&#xff0c;分辨率&#xff1a;128*64 二、调试方式 串口调试&#xff1a;通过串口通信&#xff0c;将调试信息发送到电脑端&#xff0c;电脑使用串口助手…

JS逆向实战28——补环境过RS6

前言 本文章中所有内容仅供学习交流&#xff0c;抓包内容、敏感网址、数据接口均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff0c;若有侵权&#xff0c;请联系我立即删除&#xff01;此外出于某种原因。本章大…

探究HMAC算法:消息认证与数据完整性的完美结合

Hash-based Message Authentication Code&#xff08;基于哈希的消息认证码&#xff0c;简称HMAC&#xff09;算法作为一种广泛应用的消息认证码&#xff08;MAC&#xff09;算法&#xff0c;在现代信息安全领域起着至关重要的作用。本文将从算法原理、优缺点、实际应用等方面&…