This commit is contained in:
Looly 2023-04-15 10:26:39 +08:00
parent 0f9000adcb
commit f1addebbc4
9 changed files with 389 additions and 274 deletions

View File

@ -15,7 +15,7 @@ package org.dromara.hutool.core.lang.id;
import org.dromara.hutool.core.exceptions.UtilException;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.lang.Singleton;
import org.dromara.hutool.core.net.NetUtil;
import org.dromara.hutool.core.net.Ipv4Util;
import org.dromara.hutool.core.util.RuntimeUtil;
/**
@ -189,7 +189,7 @@ public class IdUtil {
long id = 1L;
byte[] mac = null;
try{
mac = NetUtil.getLocalHardwareAddress();
mac = Ipv4Util.getLocalHardwareAddress();
}catch (final UtilException ignore){
// ignore
}

View File

@ -12,14 +12,21 @@
package org.dromara.hutool.core.net;
import org.dromara.hutool.core.collection.CollUtil;
import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.lang.Assert;
import org.dromara.hutool.core.lang.Singleton;
import org.dromara.hutool.core.regex.PatternPool;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.core.text.split.SplitUtil;
import org.dromara.hutool.core.util.CharUtil;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.regex.Matcher;
@ -46,6 +53,140 @@ import java.util.regex.Matcher;
*/
public class Ipv4Util implements Ipv4Pool {
private static volatile String localhostName;
/**
* 获取主机名称一次获取会缓存名称<br>
* 注意此方法会触发反向DNS解析导致阻塞阻塞时间取决于网络
*
* @return 主机名称
* @since 5.4.4
*/
public static String getLocalHostName() {
if(null == localhostName){
synchronized (Ipv4Util.class){
if(null == localhostName){
localhostName = NetUtil.getAddressName(getLocalhostDirectly());
}
}
}
return localhostName;
}
/**
* 获得本机MAC地址默认使用获取到的IPv4本地地址对应网卡
*
* @return 本机MAC地址
*/
public static String getLocalMacAddress() {
return MacAddressUtil.getMacAddress(getLocalhost());
}
/**
* 获得本机物理地址
*
* @return 本机物理地址
* @since 5.7.3
*/
public static byte[] getLocalHardwareAddress() {
return MacAddressUtil.getHardwareAddress(getLocalhost());
}
/**
* 获取本机网卡IPv4地址规则如下
*
* <ul>
* <li>必须非回路loopback地址非局域网地址siteLocalIPv4地址</li>
* <li>多网卡则返回第一个满足条件的地址</li>
* <li>如果无满足要求的地址调用 {@link InetAddress#getLocalHost()} 获取地址</li>
* </ul>
*
* <p>
* 此方法不会抛出异常获取失败将返回{@code null}<br>
* <p>
* https://github.com/dromara/hutool/issues/428
*
* @return 本机网卡IP地址获取失败返回{@code null}
*/
public static InetAddress getLocalhost() {
return Singleton.get(Ipv4Util.class.getName(), Ipv4Util::getLocalhostDirectly);
}
/**
* 获取本机网卡IPv4地址不使用缓存规则如下
*
* <ul>
* <li>必须非回路loopback地址非局域网地址siteLocalIPv4地址</li>
* <li>多网卡则返回第一个满足条件的地址</li>
* <li>如果无满足要求的地址调用 {@link InetAddress#getLocalHost()} 获取地址</li>
* </ul>
*
* <p>
* 此方法不会抛出异常获取失败将返回{@code null}<br>
* <p>
* https://github.com/dromara/hutool/issues/428
*
* @return 本机网卡IP地址获取失败返回{@code null}
*/
public static InetAddress getLocalhostDirectly() {
final LinkedHashSet<InetAddress> localAddressList = NetUtil.localAddressList(address -> {
// 非loopback地址指127.*.*.*的地址
return !address.isLoopbackAddress()
// 非地区本地地址
// 10.0.0.0 ~ 10.255.255.255
// 172.16.0.0 ~ 172.31.255.255
// 192.168.0.0 ~ 192.168.255.255
&& !address.isSiteLocalAddress()
// 需为IPV4地址
&& address instanceof Inet4Address;
});
if (CollUtil.isNotEmpty(localAddressList)) {
// 如果存在多网卡返回首个地址
return CollUtil.getFirst(localAddressList);
}
try {
final InetAddress localHost = InetAddress.getLocalHost();
if(localHost instanceof Inet4Address){
return localHost;
}
} catch (final UnknownHostException e) {
// ignore
}
return null;
}
/**
* 构建InetSocketAddress<br>
* 当host中包含端口时隔开使用host中的端口否则使用默认端口<br>
* 给定host为空时使用本地host127.0.0.1
*
* @param host Host
* @param defaultPort 默认端口
* @return InetSocketAddress
*/
public static InetSocketAddress buildInetSocketAddress(String host, final int defaultPort) {
if (StrUtil.isBlank(host)) {
host = LOCAL_IP;
}
final String targetHost;
final int port;
final int index = host.indexOf(":");
if (index != -1) {
// host:port形式
targetHost = host.substring(0, index);
port = Integer.parseInt(host.substring(index + 1));
} else {
targetHost = host;
port = defaultPort;
}
return new InetSocketAddress(targetHost, port);
}
/**
* 根据 ip地址 掩码地址 获得 CIDR格式字符串
*

View File

@ -0,0 +1,73 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.core.net;
import java.math.BigInteger;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.LinkedHashSet;
/**
* IPv6工具类
*
* @author looly
* @since 6.0.0
*/
public class Ipv6Util {
/**
* 将IPv6地址字符串转为大整数
*
* @param ipv6Str 字符串
* @return 大整数, 如发生异常返回 null
*/
public static BigInteger ipv6ToBigInteger(final String ipv6Str) {
try {
final InetAddress address = InetAddress.getByName(ipv6Str);
if (address instanceof Inet6Address) {
return new BigInteger(1, address.getAddress());
}
} catch (final UnknownHostException ignore) {
}
return null;
}
/**
* 将大整数转换成ipv6字符串
*
* @param bigInteger 大整数
* @return IPv6字符串, 如发生异常返回 null
*/
public static String bigIntegerToIPv6(final BigInteger bigInteger) {
try {
return InetAddress.getByAddress(
bigInteger.toByteArray()).toString().substring(1);
} catch (final UnknownHostException ignore) {
return null;
}
}
/**
* 获得本机的IPv6地址列表<br>
* 返回的IP列表有序按照系统设备顺序
*
* @return IP地址列表 {@link LinkedHashSet}
* @since 4.5.17
*/
public static LinkedHashSet<String> localIpv6s() {
final LinkedHashSet<InetAddress> localAddressList = NetUtil.localAddressList(
t -> t instanceof Inet6Address);
return NetUtil.toIpList(localAddressList);
}
}

View File

@ -0,0 +1,104 @@
/*
* Copyright (c) 2023 looly(loolly@aliyun.com)
* Hutool is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
package org.dromara.hutool.core.net;
import org.dromara.hutool.core.exceptions.UtilException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
/**
* MAC地址硬件地址相关工具类
*
* @author looly
* @since 6.0.0
*/
public class MacAddressUtil {
/**
* 获得指定地址信息中的MAC地址使用分隔符-
*
* @param inetAddress {@link InetAddress}
* @return MAC地址-分隔
*/
public static String getMacAddress(final InetAddress inetAddress) {
return getMacAddress(inetAddress, "-");
}
/**
* 获得指定地址信息中的MAC地址
*
* @param inetAddress {@link InetAddress}
* @param separator 分隔符推荐使用-或者:
* @return MAC地址-分隔
*/
public static String getMacAddress(final InetAddress inetAddress, final String separator) {
if (null == inetAddress) {
return null;
}
return toMacAddressStr(getHardwareAddress(inetAddress), separator);
}
/**
* 获得指定地址信息中的硬件地址MAC地址
*
* @param inetAddress {@link InetAddress}
* @return 硬件地址
* @since 5.7.3
*/
public static byte[] getHardwareAddress(final InetAddress inetAddress) {
if (null == inetAddress) {
return null;
}
try {
// 获取地址对应网卡
final NetworkInterface networkInterface =
NetworkInterface.getByInetAddress(inetAddress);
if (null != networkInterface) {
return networkInterface.getHardwareAddress();
}
} catch (final SocketException e) {
throw new UtilException(e);
}
return null;
}
/**
* 将bytes类型的mac地址转换为可读字符串通常地址每个byte位使用16进制表示并用指定分隔符分隔
*
* @param mac MAC地址网卡硬件地址
* @param separator 分隔符
* @return MAC地址字符串
*/
public static String toMacAddressStr(final byte[] mac, final String separator) {
if (null == mac) {
return null;
}
final StringBuilder sb = new StringBuilder(
// 字符串长度 = byte个数*2每个byte转16进制后占2位 + 分隔符总长度
mac.length * 2 + (mac.length - 1) * separator.length());
String s;
for (int i = 0; i < mac.length; i++) {
if (i != 0) {
sb.append(separator);
}
// 字节转换为整数
s = Integer.toHexString(mac[i] & 0xFF);
sb.append(s.length() == 1 ? 0 + s : s);
}
return sb.toString();
}
}

View File

@ -28,31 +28,10 @@ import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import java.io.IOException;
import java.io.OutputStream;
import java.math.BigInteger;
import java.net.Authenticator;
import java.net.DatagramSocket;
import java.net.HttpCookie;
import java.net.IDN;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.URL;
import java.net.UnknownHostException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.*;
import java.util.function.Predicate;
/**
@ -67,8 +46,6 @@ public class NetUtil {
*/
public final static String LOCAL_IP = Ipv4Util.LOCAL_IP;
private static String localhostName;
/**
* 默认最小端口1024
*/
@ -100,39 +77,6 @@ public class NetUtil {
return Ipv4Util.ipv4ToLong(strIP);
}
/**
* 将IPv6地址字符串转为大整数
*
* @param ipv6Str 字符串
* @return 大整数, 如发生异常返回 null
* @since 5.5.7
*/
public static BigInteger ipv6ToBigInteger(final String ipv6Str) {
try {
final InetAddress address = InetAddress.getByName(ipv6Str);
if (address instanceof Inet6Address) {
return new BigInteger(1, address.getAddress());
}
} catch (final UnknownHostException ignore) {
}
return null;
}
/**
* 将大整数转换成ipv6字符串
*
* @param bigInteger 大整数
* @return IPv6字符串, 如发生异常返回 null
* @since 5.5.7
*/
public static String bigIntegerToIPv6(final BigInteger bigInteger) {
try {
return InetAddress.getByAddress(bigInteger.toByteArray()).toString().substring(1);
} catch (final UnknownHostException ignore) {
return null;
}
}
/**
* 检测本地端口可用性<br>
* 来自org.springframework.util.SocketUtils
@ -300,35 +244,6 @@ public class NetUtil {
return hideIpPart(longToIpv4(ip));
}
/**
* 构建InetSocketAddress<br>
* 当host中包含端口时隔开使用host中的端口否则使用默认端口<br>
* 给定host为空时使用本地host127.0.0.1
*
* @param host Host
* @param defaultPort 默认端口
* @return InetSocketAddress
*/
public static InetSocketAddress buildInetSocketAddress(String host, final int defaultPort) {
if (StrUtil.isBlank(host)) {
host = LOCAL_IP;
}
final String destHost;
final int port;
final int index = host.indexOf(":");
if (index != -1) {
// host:port形式
destHost = host.substring(0, index);
port = Integer.parseInt(host.substring(index + 1));
} else {
destHost = host;
port = defaultPort;
}
return new InetSocketAddress(destHost, port);
}
/**
* 通过域名得到IP
*
@ -398,19 +313,6 @@ public class NetUtil {
return toIpList(localAddressList);
}
/**
* 获得本机的IPv6地址列表<br>
* 返回的IP列表有序按照系统设备顺序
*
* @return IP地址列表 {@link LinkedHashSet}
* @since 4.5.17
*/
public static LinkedHashSet<String> localIpv6s() {
final LinkedHashSet<InetAddress> localAddressList = localAddressList(t -> t instanceof Inet6Address);
return toIpList(localAddressList);
}
/**
* 地址列表转换为IP地址列表
*
@ -499,8 +401,8 @@ public class NetUtil {
* @return 本机网卡IP地址获取失败返回{@code null}
* @since 3.0.7
*/
public static String getLocalhostStr() {
final InetAddress localhost = getLocalhost();
public static String getLocalhostStrV4() {
final InetAddress localhost = Ipv4Util.getLocalhost();
if (null != localhost) {
return localhost.getHostAddress();
}
@ -508,157 +410,32 @@ public class NetUtil {
}
/**
* 获取本机网卡IP地址规则如下
* 获取本机网卡IPv4地址规则如下
*
* <ul>
* <li>必须非回路loopback地址非局域网地址siteLocalIPv4地址</li>
* <li>多网卡则返回第一个满足条件的地址</li>
* <li>如果无满足要求的地址调用 {@link InetAddress#getLocalHost()} 获取地址</li>
* </ul>
*
* <pre>
* 1. 查找所有网卡地址必须非回路loopback地址非局域网地址siteLocalIPv4地址
* 2. 如果无满足要求的地址调用 {@link InetAddress#getLocalHost()} 获取地址
* </pre>
* <p>
* 此方法不会抛出异常获取失败将返回{@code null}<br>
* <p>
* <a href="https://github.com/dromara/hutool/issues/428">https://github.com/dromara/hutool/issues/428</a>
* https://github.com/dromara/hutool/issues/428
*
* @return 本机网卡IP地址获取失败返回{@code null}
* @since 3.0.1
*/
public static InetAddress getLocalhost() {
final LinkedHashSet<InetAddress> localAddressList = localAddressList(address -> {
// 非loopback地址指127.*.*.*的地址
return !address.isLoopbackAddress()
// 需为IPV4地址
&& address instanceof Inet4Address;
});
if (CollUtil.isNotEmpty(localAddressList)) {
InetAddress address2 = null;
for (final InetAddress inetAddress : localAddressList) {
if (!inetAddress.isSiteLocalAddress()) {
// 非地区本地地址指10.0.0.0 ~ 10.255.255.255172.16.0.0 ~ 172.31.255.255192.168.0.0 ~ 192.168.255.255
return inetAddress;
} else if (null == address2) {
address2 = inetAddress;
}
}
if (null != address2) {
return address2;
}
}
try {
return InetAddress.getLocalHost();
} catch (final UnknownHostException e) {
// ignore
}
return null;
public static InetAddress getLocalhostV4() {
return Ipv4Util.getLocalhost();
}
/**
* 获得本机MAC地址
* 获得本机MAC地址默认使用获取到的IPv4本地地址对应网卡
*
* @return 本机MAC地址
*/
public static String getLocalMacAddress() {
return getMacAddress(getLocalhost());
}
/**
* 获得指定地址信息中的MAC地址使用分隔符-
*
* @param inetAddress {@link InetAddress}
* @return MAC地址-分隔
*/
public static String getMacAddress(final InetAddress inetAddress) {
return getMacAddress(inetAddress, "-");
}
/**
* 获得指定地址信息中的MAC地址
*
* @param inetAddress {@link InetAddress}
* @param separator 分隔符推荐使用-或者:
* @return MAC地址-分隔
*/
public static String getMacAddress(final InetAddress inetAddress, final String separator) {
if (null == inetAddress) {
return null;
}
final byte[] mac = getHardwareAddress(inetAddress);
if (null != mac) {
final StringBuilder sb = new StringBuilder();
String s;
for (int i = 0; i < mac.length; i++) {
if (i != 0) {
sb.append(separator);
}
// 字节转换为整数
s = Integer.toHexString(mac[i] & 0xFF);
sb.append(s.length() == 1 ? 0 + s : s);
}
return sb.toString();
}
return null;
}
/**
* 获得指定地址信息中的硬件地址
*
* @param inetAddress {@link InetAddress}
* @return 硬件地址
* @since 5.7.3
*/
public static byte[] getHardwareAddress(final InetAddress inetAddress) {
if (null == inetAddress) {
return null;
}
try {
final NetworkInterface networkInterface = NetworkInterface.getByInetAddress(inetAddress);
if (null != networkInterface) {
return networkInterface.getHardwareAddress();
}
} catch (final SocketException e) {
throw new UtilException(e);
}
return null;
}
/**
* 获得本机物理地址
*
* @return 本机物理地址
* @since 5.7.3
*/
public static byte[] getLocalHardwareAddress() {
return getHardwareAddress(getLocalhost());
}
/**
* 获取主机名称一次获取会缓存名称<br>
* 注意此方法会触发反向DNS解析导致阻塞阻塞时间取决于网络
*
* @return 主机名称
* @since 5.4.4
*/
public static String getLocalHostName() {
if (StrUtil.isNotBlank(localhostName)) {
return localhostName;
}
final InetAddress localhost = getLocalhost();
if (null != localhost) {
String name = localhost.getHostName();
if (StrUtil.isEmpty(name)) {
name = localhost.getHostAddress();
}
localhostName = name;
}
return localhostName;
public static String getLocalMacAddressV4() {
return Ipv4Util.getLocalMacAddress();
}
/**
@ -884,6 +661,24 @@ public class NetUtil {
return infos;
}
/**
* 获取地址名称如果无名称返回地址<br>
* 如果提供的地址为{@code null}返回{@code null}
*
* @param address {@link InetAddress}提供{@code null}返回{@code null}
* @return 地址名称或地址
*/
public static String getAddressName(final InetAddress address) {
if (null == address) {
return null;
}
String name = address.getHostName();
if (StrUtil.isEmpty(name)) {
name = address.getHostAddress();
}
return name;
}
// ----------------------------------------------------------------------------------------- Private method start
// ----------------------------------------------------------------------------------------- Private method end

View File

@ -1,10 +1,22 @@
package org.dromara.hutool.core.net;
import org.dromara.hutool.core.regex.PatternPool;
import org.dromara.hutool.core.regex.ReUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.net.InetAddress;
public class Ipv4UtilTest {
@Test
@Disabled
public void getLocalHostNameTest() {
// 注意此方法会触发反向DNS解析导致阻塞阻塞时间取决于网络
Assertions.assertNotNull(Ipv4Util.getLocalHostName());
}
@Test
public void formatIpBlockTest() {
for (int i = Ipv4Util.IPV4_MASK_BIT_VALID_MIN; i < Ipv4Util.IPV4_MASK_BIT_MAX; i++) {
@ -199,4 +211,22 @@ public class Ipv4UtilTest {
Assertions.assertEquals(Ipv4Util.getMaskByMaskBit(i), Ipv4Util.getMaskByIpRange(beginIpStr, endIpStr));
}
}
@Test
@Disabled
public void getLocalhostTest() {
final InetAddress localhost = Ipv4Util.getLocalhost();
Assertions.assertNotNull(localhost);
}
@Test
@Disabled
public void getLocalMacAddressTest() {
final String macAddress = Ipv4Util.getLocalMacAddress();
Assertions.assertNotNull(macAddress);
// 验证MAC地址正确
final boolean match = ReUtil.isMatch(PatternPool.MAC_ADDRESS, macAddress);
Assertions.assertTrue(match);
}
}

View File

@ -1,14 +1,11 @@
package org.dromara.hutool.core.net;
import org.dromara.hutool.core.lang.Console;
import org.dromara.hutool.core.regex.PatternPool;
import org.dromara.hutool.core.regex.ReUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.net.HttpCookie;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.List;
@ -23,28 +20,10 @@ public class NetUtilTest {
@Test
@Disabled
public void getLocalhostStrTest() {
final String localhost = NetUtil.getLocalhostStr();
final String localhost = NetUtil.getLocalhostStrV4();
Assertions.assertNotNull(localhost);
}
@Test
@Disabled
public void getLocalhostTest() {
final InetAddress localhost = NetUtil.getLocalhost();
Assertions.assertNotNull(localhost);
}
@Test
@Disabled
public void getLocalMacAddressTest() {
final String macAddress = NetUtil.getLocalMacAddress();
Assertions.assertNotNull(macAddress);
// 验证MAC地址正确
final boolean match = ReUtil.isMatch(PatternPool.MAC_ADDRESS, macAddress);
Assertions.assertTrue(match);
}
@Test
public void longToIpTest() {
final String ipv4 = NetUtil.longToIpv4(2130706433L);
@ -77,16 +56,9 @@ public class NetUtilTest {
Assertions.assertEquals("cookiedomain.com", httpCookie.getDomain());
}
@Test
@Disabled
public void getLocalHostNameTest() {
// 注意此方法会触发反向DNS解析导致阻塞阻塞时间取决于网络
Assertions.assertNotNull(NetUtil.getLocalHostName());
}
@Test
public void getLocalHostTest() {
Assertions.assertNotNull(NetUtil.getLocalhost());
Assertions.assertNotNull(NetUtil.getLocalhostV4());
}
@Test

View File

@ -59,7 +59,7 @@ public class BCMacEngine implements MacEngine {
*
* @return {@link Mac}
*/
public Mac getMac() {
public Mac ipgetMac() {
return mac;
}

View File

@ -27,7 +27,7 @@ public class HostInfo implements Serializable {
private final String HOST_ADDRESS;
public HostInfo() {
final InetAddress localhost = NetUtil.getLocalhost();
final InetAddress localhost = NetUtil.getLocalhostV4();
if(null != localhost){
HOST_NAME = localhost.getHostName();
HOST_ADDRESS = localhost.getHostAddress();