121 lines
4.2 KiB
Java
121 lines
4.2 KiB
Java
|
|
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;
|
|||
|
|
}
|
|||
|
|
}
|