Files
yjearth/src/main/java/com/yj/earth/common/util/NetUtils.java
2025-11-25 14:27:10 +08:00

121 lines
4.2 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;
}
}