This commit is contained in:
2025-11-25 14:27:10 +08:00
parent d3931a9ddd
commit 7d8aeedcf2
23 changed files with 414 additions and 166 deletions

View File

@ -0,0 +1,120 @@
package com.yj.earth.common.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.*;
import java.util.concurrent.*;
/**
* 网络工具类
*/
public class NetUtils {
private static final Logger logger = LoggerFactory.getLogger(NetUtils.class);
// Ping 超时时间(毫秒)
private static final int PING_TIMEOUT = 500;
// TCP Ping 端口
private static final int TCP_PING_PORT = 80;
// TCP Ping 超时时间(毫秒)
private static final int TCP_TIMEOUT = 1500;
private static final ExecutorService PING_EXECUTOR = Executors.newFixedThreadPool(
Math.min(Runtime.getRuntime().availableProcessors() * 2, 50),
new ThreadFactory() {
private int count = 0;
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setName("ping-executor-" + (count++));
thread.setDaemon(true);
return thread;
}
}
);
/**
* 使用 ICMP Echo Request (标准 Ping) 检测主机是否可达
* @param ipAddress IP地址或主机名
* @return true if reachable, false otherwise
*/
public static boolean isReachableByIcmp(String ipAddress) {
if (ipAddress == null || ipAddress.trim().isEmpty()) {
return false;
}
try {
InetAddress address = InetAddress.getByName(ipAddress);
// isReachable 会尝试 ICMP Echo 和 TCP Echo (端口 7)
return address.isReachable(PING_TIMEOUT);
} catch (UnknownHostException e) {
logger.warn("未知的主机: {}", ipAddress);
return false;
} catch (IOException e) {
// 发生 IO 异常、视为不可达
return false;
}
}
/**
* 使用 TCP 连接检测主机端口是否开放(模拟 Ping
* 当 ICMP 被防火墙禁止时、此方法更有效
* @param ipAddress IP地址
* @param port 端口号
* @return true if port is open, false otherwise
*/
public static boolean isReachableByTcp(String ipAddress, int port) {
if (ipAddress == null || ipAddress.trim().isEmpty() || port < 1 || port > 65535) {
return false;
}
Socket socket = null;
try {
socket = new Socket();
socket.connect(new java.net.InetSocketAddress(ipAddress, port), TCP_TIMEOUT);
return true;
} catch (IOException e) {
// 连接失败、端口未开放或主机不可达
return false;
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
// ignore
}
}
}
}
/**
* 异步并发检测多个IP的可达性
* @param ips 需要检测的IP列表
* @return 一个 Map、key 是 IP、value 是 Boolean (是否在线)
* @throws ExecutionException 执行异常
* @throws InterruptedException 线程中断异常
*/
public static Map<String, Boolean> checkReachabilityAsync(Collection<String> ips) throws ExecutionException, InterruptedException {
if (ips == null || ips.isEmpty()) {
return Collections.emptyMap();
}
List<Callable<Map.Entry<String, Boolean>>> tasks = new ArrayList<>();
for (String ip : ips) {
tasks.add(() -> {
boolean isOnline = isReachableByTcp(ip, TCP_PING_PORT) || isReachableByIcmp(ip);
return new AbstractMap.SimpleEntry<>(ip, isOnline);
});
}
List<Future<Map.Entry<String, Boolean>>> futures = PING_EXECUTOR.invokeAll(tasks);
Map<String, Boolean> results = new ConcurrentHashMap<>();
for (Future<Map.Entry<String, Boolean>> future : futures) {
Map.Entry<String, Boolean> entry = future.get();
results.put(entry.getKey(), entry.getValue());
}
return results;
}
}