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