获取本机的IP地址,看似简单的获取,实则蕴含非常多的操作

news2024/11/24 2:00:49

这篇文章讲述了PowerJob获取本地IP离奇曲折的经过,以及开放了诸多的可配置参数,打开了我新世界的大窗户。求个关注,求个点赞,求一个评论。

获取地址的操作,本来不应该作为什么重点,但是因为一点小小的意外,导致我对这个环节格外的研究了一下,所以就总结了一下。

先来一段文字,描述一下大致的流程,然后再从源代码中研究一下:

  1. 先是判断内存是否已经存了IP地址,如果存了,则直接返回保存了个IP地址。

  2. 从jvm虚拟机中获取配置绑定的IP地址,如果绑定了,则直接返回绑定的IP地址。

  3. 获取所有的网卡信息,进行遍历。

  4. 忽略一些无效的网卡信息,比如:虚拟机网口,关闭的网口,启动时配置的忽略网口(主要是用过网卡名字和描述名字)。

  5. 通过启动时的配置“powerjob.network.interface.preferred”,获取对应的地址,如果有,则直接返回对应的IP地址。

  6. 对剩余的网卡信息进行遍历,如果遍历到的IP地址合法有效(格式正确并可以ping通),则直接返回该合法有效的ip。

  7. 直接返回第一条格式正确的IP地址。

  8. 获取InetAddress.getLocalHost()得到的IP地址。

经过上述一系列的复杂操作,如果没有配置的话,获得到IP地址可能会无效。

简单的开端

public static String getLocalHost() {
//1.先是判断内存是否已经存了IP地址,如果存了,则直接返回保存了个IP地址。
    if (HOST_ADDRESS != null) {
        return HOST_ADDRESS;
    }

//2.从jvm虚拟机中获取配置绑定的IP地址,如果绑定了,则直接返回绑定的IP地址。
    String addressFromJVM = System.getProperty(PowerJobDKey.BIND_LOCAL_ADDRESS);
    if (StringUtils.isNotEmpty(addressFromJVM)) {
        log.info("[Net] use address from[{}]: {}", PowerJobDKey.BIND_LOCAL_ADDRESS, addressFromJVM);
        return HOST_ADDRESS = addressFromJVM;
    }

//第三步在这个方法里面,但是还需要不断的深入才能找到!
    InetAddress address = getLocalAddress();
    if (address != null) {
        return HOST_ADDRESS = address.getHostAddress();
    }
    return LOCALHOST_VALUE;
}

一切的开端都是从上面的代码开始的。开始很简单,过程却很复杂。

曲折的经过

第1,2步已经出现,这第3步的出现却需要层层的传送~

public static InetAddress getLocalAddress() {
    //这个方法只是一个传送门,将其传送到getLocalAddress0
}

private static InetAddress getLocalAddress0() {
   //这个方法也只是传送门,不过这个方法在之后还会出现的
    InetAddress addressOp = getFirstReachableInetAddress( findNetworkInterface());
    ... ...
    return localAddress;
}

public static NetworkInterface findNetworkInterface() {
    //传送门依旧,该方法之后也会再次出现
    List<NetworkInterface> validNetworkInterfaces = emptyList();
    try {
        validNetworkInterfaces = getValidNetworkInterfaces();
    } catch (Throwable e) {
        log.warn("[Net] findNetworkInterface failed", e);
    }
    ... ...
}

private static List<NetworkInterface> getValidNetworkInterfaces() throws SocketException {
    List<NetworkInterface> validNetworkInterfaces = new LinkedList<>();
    //3.获取所有的网卡信息,进行遍历。
    Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
    //4.忽略一些无效的网卡信息,比如:虚拟机网口,关闭的网口,
    //启动时配置的忽略网口(主要是用过网卡名字和描述名字)。
    while (interfaces.hasMoreElements()) {
        NetworkInterface networkInterface = interfaces.nextElement();
        if (ignoreNetworkInterface(networkInterface)) {
            continue;
        }
        // 根据用户 -D 参数忽略网卡
        if (ignoreInterfaceByConfig(networkInterface.getDisplayName()) || ignoreInterfaceByConfig(networkInterface.getName())) {
            continue;
        }
        validNetworkInterfaces.add(networkInterface);
    }
    return validNetworkInterfaces;
}

//忽略的网卡内容
private static boolean ignoreNetworkInterface(NetworkInterface networkInterface) throws SocketException {
    return networkInterface == null
            || networkInterface.isLoopback()
            || networkInterface.isVirtual()
            || !networkInterface.isUp();
}
static boolean ignoreInterfaceByConfig(String interfaceName) {
    String regex = System.getProperty(PowerJobDKey.IGNORED_NETWORK_INTERFACE_REGEX);
    if (StringUtils.isBlank(regex)) {
        return false;
    }
    if (interfaceName.matches(regex)) {
        log.info("[Net] ignore network interface: {} by regex({})", interfaceName, regex);
        return true;
    }
    return false;
}

精彩的高潮

 

找到了本地所有的网卡信息,并且忽略掉了很多没有用的网卡信息,接下来就是通过偏好来选择合适的网卡地址来进行通信,一开始我没有发现这一条信息,在官方文档中也没有找到对应的配置,一度以为这个ip地址无法选择,甚至我使用的服务器,第一条网卡信息是docker的,结果就直接给我用的docker的地址,直接给我整混乱了,还好我还能看懂这么一点代码,不得不说,作者这个代码确实厉害,直接打开我新世界的大门,但是你开门开的好,你得跟我说一声啊,你不说我都没法往门里进啊。

public static NetworkInterface findNetworkInterface() {

    List<NetworkInterface> validNetworkInterfaces = emptyList();
    try {
        validNetworkInterfaces = getValidNetworkInterfaces();
    } catch (Throwable e) {
        log.warn("[Net] findNetworkInterface failed", e);
    }

    // Try to find the preferred one
    for (NetworkInterface networkInterface : validNetworkInterfaces) {
        if (isPreferredNetworkInterface(networkInterface)) {
            log.info("[Net] use preferred network interface: {}", networkInterface.getDisplayName());
            return networkInterface;
        }
    }
    ...
    return first(validNetworkInterfaces);
}

public static boolean isPreferredNetworkInterface(NetworkInterface networkInterface) {
//5.通过启动时的配置“powerjob.network.interface.preferred”,获取对应的地址,如果有,则直接返回对应的IP地址。
    String preferredNetworkInterface = System.getProperty(PowerJobDKey.PREFERRED_NETWORK_INTERFACE);
    if (Objects.equals(networkInterface.getDisplayName(), preferredNetworkInterface)) {
        return true;
    }
    // 兼容直接使用网卡名称的情况,比如 Realtek PCIe GBE Family Controller
    return Objects.equals(networkInterface.getName(), preferredNetworkInterface);
}

无奈的结局

我认为通过偏好选择网卡信息就已经非常好了,如果没有偏好设置,默认选择第一条网卡信息这个策略也是不错,之后是选择一条能够访问的地址,最后如果都不行,就破罐子破摔的来获取一个地址,反正必须要返回一个地址,即使这个地址有问题,也得返回了,如果要是一般的网络环境,我觉得这也挺好的,万一有那么一个公司,内网互相通讯还需要代理,这可就恶心了,太恶心了。

public static NetworkInterface findNetworkInterface() {

    List<NetworkInterface> validNetworkInterfaces = emptyList();
    try {
        validNetworkInterfaces = getValidNetworkInterfaces();
    } catch (Throwable e) {
        log.warn("[Net] findNetworkInterface failed", e);
    }

    // Try to find the preferred one
    for (NetworkInterface networkInterface : validNetworkInterfaces) {
        if (isPreferredNetworkInterface(networkInterface)) {
            log.info("[Net] use preferred network interface: {}", networkInterface.getDisplayName());
            return networkInterface;
        }
    }
//6.对剩余的网卡信息进行遍历,如果遍历到的IP地址合法有效(格式正确并可以ping通),则直接返回该合法有效的ip。
    for (NetworkInterface networkInterface : validNetworkInterfaces) {
        InetAddress addressOp = getFirstReachableInetAddress(networkInterface);
        if (addressOp != null) {
            return networkInterface;
        }
    }
//7.直接返回第一条格式正确的IP地址。
    return first(validNetworkInterfaces);
}

private static InetAddress getFirstReachableInetAddress(NetworkInterface networkInterface) {

    if(networkInterface == null ){
        return null;
    }
    Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
    while (addresses.hasMoreElements()) {
        Optional<InetAddress> addressOp = toValidAddress(addresses.nextElement());
        if (addressOp.isPresent()) {
            try {
                if (addressOp.get().isReachable(100)) {
                    return addressOp.get();
                }
            } catch (IOException e) {
                // ignore
            }
        }
    }
    return null;
}

public static <T> T first(Collection<T> values) {
    if (values == null || values.isEmpty()) {
        return null;
    }
    if (values instanceof List) {
        List<T> list = (List<T>) values;
        return list.get(0);
    } else {
        return values.iterator().next();
    }
}

private static InetAddress getLocalAddress0() {
    // @since 2.7.6, choose the {@link NetworkInterface} first
    try {
        InetAddress addressOp = getFirstReachableInetAddress( findNetworkInterface());
        if (addressOp != null) {
            return addressOp;
        }
    } catch (Throwable e) {
        log.warn("[Net] getLocalAddress0 failed.", e);
    }

    InetAddress localAddress = null;
    try {
//8.获取InetAddress.getLocalHost()得到的IP地址。
        localAddress = InetAddress.getLocalHost();
        Optional<InetAddress> addressOp = toValidAddress(localAddress);
        if (addressOp.isPresent()) {
            return addressOp.get();
        }
    } catch (Throwable e) {
        log.warn("[Net] getLocalAddress0 failed.", e);
    }


    return localAddress;
}

总结

 

其实大部分人是不需要了解这部分代码的,基本都不会有啥问题,因为大部分人使用的都是正常人使用的网络,只有我们公司这1万来人用的是不正常人使用的网络,但是万一遇到这方面的问题,了解一下还是好的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

再创荣誉 | Softing工业荣获CAIMRS 2023 数字化创新奖

在刚刚结束的中国工控-第二十一届“自动化及数字化”年度评选&#xff08;CAIMRS 2023&#xff09;中&#xff0c;Softing凭借edgeAggregator产品荣获“数字化创新奖”&#xff01; 经层层筛选&#xff0c;Softing edgeAggregator边缘聚合服务器从中脱颖而出&#xff0c;摘得C…

隐马尔科夫模型基础

一、定义是一种生成模型&#xff0c;是隐藏的马尔科夫链随机生成不可观测的状态序列&#xff0c;再由各个状态生成观测序列的过程二、符号含义其中&#xff1a;Q是所有可能的状态集合V是所有的可能的观测集合N是可能的状态数&#xff0c;M是可能的观测数其中&#xff1a;I是长度…

想搞钱,先培养商业思维!

昨天谈借助 ChatGPT 挣点房贷钱的时候&#xff0c;看评论区大家留言的时候&#xff0c;发现很多人不知道这个东西可以赚钱&#xff0c;或者说知道这个东西且也做了功课&#xff0c;无奈太忙最后也没搞到钱。可以看到&#xff0c;大家的问题&#xff0c;归根于自己有没有商业思维…

小白系列Vite-Vue3-TypeScript:010-封装svg

上一篇我们介绍了ViteVue3TypeScript项目中mockjs的安装和配置i。本篇我们来介绍封装SVG图标组件。svg特征Preloading所有图标都是在项目运行时生成的&#xff0c;只需要操作一次dom即可。高性能内置缓存&#xff0c;仅在文件被修改时才会重新生成。安装插件vite-plugin-svg-ic…

QHash源码解读

QT版本 v5.12.10 元素 // 重点说明QHashData的函数&#xff0c;QHashData是QHash的基础 struct QHashData {struct Node {Node *next;uint h;};Node *fakeNext; // 永为nullNode **buckets; // Node *数组QtPrivate::RefCount ref;int size; // node个数int nodeSize; /…

koa2结合MySQL实现简单的考试系统

项目需求:1. 数据库采用mysql实现, 后台服务Koa2框架, 通过postman调试所有接口2. 接口功能&#xff1a;(1)实现对科目表的增删改查(2)实现对试题表的增删改(3)实现对试题表的查询操作&#xff0c;要求&#xff1a;<1>显示科目名称和类型名称 <2> 可以按照科目名称…

第一章 - 对数据库和SQL的简单了解

第一章 - 对数据库和SQL的简单了解1 了解数据库&#xff1a;2 什么是数据库&#xff1a;3 什么是SQL&#xff1a;4 SQL的优点&#xff1a;5 数据库的一些常用术语&#xff1a;6 什么是MySQL&#xff1a;1 了解数据库&#xff1a; 其实你一直都在使用数据库&#xff0c;只是你并…

【观察】从消费级SSD AM6A1,看忆联的优势与胜势

毫无疑问&#xff0c;目前SSD&#xff08;固态硬盘&#xff09;已取代HDD&#xff08;机械硬盘&#xff09;成为电脑中常见的存储设备&#xff0c;特别是在技术创新的持续推动下&#xff0c;如今SSD的速度和效率都在不断地提高&#xff0c;从SATA2 3GB发展到SATA3 6GB&#xff…

四、常用样式讲解二

文章目录一、常用样式讲解二1.1 元素隐藏1.2 二级菜单1.3 相对定位和绝对定位1.4 定位的特殊情况1.5 表格1.6 表格的css属性1.7 表格中新增的标签一、常用样式讲解二 1.1 元素隐藏 如何让一个元素隐藏 1、不定义颜色 占用空间 2、display: none 不占用空间 3、visibility: hi…

在Linux和Windows上安装Nacos-2.1.1

记录&#xff1a;377场景&#xff1a;在CentOS 7.9操作系统安装Nacos-2.1.1。在Windows操作系统上安装Nacos-2.1.1。Nacos&#xff1a;Nacos: Dynamic Naming and Configuration Service。Nacos提供动态配置服务、服务发现及管理、动态DNS服务功能。版本&#xff1a;JDK 1.8 Na…

dva + antd 报错

学习 dva 》 按照dva指南学习、安装 dva-cli、引入antd的报错问题解决 1、在执行命令 npm install antd babel-plugin-import --save时报错 报错类似“A complete log of this run can be fund in : … " 解决&#xff1a;换成cnpm 或者 yarn 进行安装 举例在安装history的…

Java常见问题总结三

一、ArrayList 和 LinkedList的区别 1. 底层数据结构不同。ArrayList底层是基于数组实现的&#xff0c;LinkedList底层是基于链表文现的 2. 由于底层数缺结构不同&#xff0c;他们所适电的场景也不同&#xff0c;Araylist史适合随机查战&#xff0c;LinkedList史适合期余和添…

自动化测试工程师的发展前景怎么样?

根据各大网络招聘平台的数据显示&#xff0c;越来越多的企业在招聘测试工程师的时候&#xff0c;都开始重视自动化测试这一重要技能。早在四年前&#xff0c;自动化测试的人才需求和薪资待遇就开始一路上涨。如果你问&#xff1a;自动化测试工程师的发展前景怎么样&#xff1f;…

基于redis实现分布式锁

前言 我们的系统都是分布式部署的&#xff0c;日常开发中&#xff0c;秒杀下单、抢购商品等等业务场景&#xff0c;为了防⽌库存超卖&#xff0c;都需要用到分布式锁。 分布式锁其实就是&#xff0c;控制分布式系统不同进程共同访问共享资源的一种锁的实现。如果不同的系统或…

ubuntu重启、关机命令

// // // //之前用linux系统&#xff0c; 一键解决也是可以的&#xff0c;反正我每次用命令&#xff08;泪目…&#xff09;&#xff0c;中间崩了好几次&#xff0c;换回win&#xff0c;此篇也做记录 // // // 重启命令 以下所有命令在root根目录下输入&#xff08;普通用户&…

SQL Server 数据批量导出处理

在实际项目环境中&#xff0c;有时会遇到需要将大量数据&#xff08;这里所指百万级别以上的数据量&#xff09;从一台服务器迁移到另外一台数据库服务器的情况。SQL Server有很多方式可以进行数据迁移&#xff1a;备份还原、导入/导出数据、生成脚本&#xff08;包含数据&…

解决console.log打印不出深度链表问题

解决console.log打印不出深度链表问题相信大家在写算法题的时候会遇到这样一个问题&#xff0c;写了一个链表的数据结构&#xff0c;在append几个数据之后&#xff0c;想console.log打印以下看看append正不正确&#xff0c;但是&#xff0c;consolo.log出来成这个样子&#xff…

【算法】差分

作者&#xff1a;指针不指南吗 专栏&#xff1a;算法篇 &#x1f43e;合理规划时间与精力&#x1f43e; 1.什么是差分&#xff1f; 与前缀和是反函数 原数组a a1 , a2 , a3 , a4 , a5 , a6 , a7 构造数组b a1b1; a2b1b2; a3b1b2b3; … aib1b2b3…bi; 构造一个b数组使得&#…

【Linux】信号量

&#x1f387;Linux&#xff1a; 博客主页&#xff1a;一起去看日落吗分享博主的在Linux中学习到的知识和遇到的问题博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 看似不起波澜的日复一日&#xff0c;一定会在某一天让你看见坚持…

Java基础:面向对象进阶

1.static 1.static概念 工具类 2.static内存图 静态变量是随着类的加载而加载的,优于对象出现 3.static的注意事项 1.静态方法中,只能访问静态 : 因为非静态方法一般会传入调用方法的对象的地址this(一般是虚拟机自动调用,不需要手动传入, 如student.study()).但是由于静态方…