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 checkReachabilityAsync(Collection ips) throws ExecutionException, InterruptedException { if (ips == null || ips.isEmpty()) { return Collections.emptyMap(); } List>> 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>> futures = PING_EXECUTOR.invokeAll(tasks); Map results = new ConcurrentHashMap<>(); for (Future> future : futures) { Map.Entry entry = future.get(); results.put(entry.getKey(), entry.getValue()); } return results; } }