v1.1初始化

This commit is contained in:
qiminbao
2025-10-21 14:16:25 +08:00
parent 36ff2dbb48
commit 62c8736728
31 changed files with 2023 additions and 2595 deletions
@@ -1,15 +1,17 @@
package com.tongran.agent.client.utils;
import cn.hutool.json.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tongran.agent.client.core.vo.NetworkInterfaceInfo;
import org.snmp4j.smi.OID;
import oshi.hardware.NetworkIF;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
@@ -240,4 +242,314 @@ public class AgentUtil {
return swapped;
}
/**
* 收集所有 Ethernet 类型网卡信息
*/
public static List<NetworkInterfaceInfo> collectNetworkInfo() throws Exception {
List<NetworkInterfaceInfo> result = new ArrayList<>();
// String publicIp = getPublicIp(); // 获取公网 IP(全局出口)
String ipInfo = PublicIpFetcher.getPublicIp();
// System.out.println("完整信息: " + ipInfo);
String publicIp = PublicIpFetcher.extractIp(ipInfo);
if (publicIp != null) {
System.out.println("公网 IP: " + publicIp);
} else {
System.out.println("未能提取 IP 地址");
}
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
// 跳过回环、虚拟、关闭的接口
if (ni.isLoopback() || ni.isVirtual() || !ni.isUp()) continue;
// 判断是否为 Ethernet(通过名称约定:eth*, en*, 等)
String name = ni.getName();
if (!isEthernetInterface(name)) continue;
NetworkInterfaceInfo info = NetworkInterfaceInfo.builder()
.name(name)
.mac(getMacAddress(ni))
.ipv4(getIPv4Address(ni))
.gateway(getGatewayAddress()) // 网关通常是默认路由,全局一致
.publicIp(publicIp)
.build();
// 如果有公网 IP,查询归属地
if (publicIp != null && !publicIp.isEmpty()) {
JSONObject location = queryIpLocation(publicIp);
if (location != null) {
info.setCarrier(location.getStr("org", "未知").replaceAll("^AS\\d+\\s*", ""));
info.setProvince(location.getStr("region", "未知"));
info.setCity(location.getStr("city", "未知"));
} else {
info.setCarrier("查询失败");
info.setProvince("查询失败");
info.setCity("查询失败");
}
}
result.add(info);
}
return result;
}
/**
* 判断是否为 Ethernet 类型网卡(基于常见命名)
*/
private static boolean isEthernetInterface(String name) {
return name.startsWith("eth") || // Linux 传统
name.startsWith("en") || // systemd 命名 (enp3s0)
name.startsWith("em"); // 有些主板网卡
}
/**
* 获取 MAC 地址
*/
private static String getMacAddress(NetworkInterface ni) {
try {
byte[] mac = ni.getHardwareAddress();
if (mac == null) return "N/A";
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mac.length; i++) {
sb.append(String.format("%02X", mac[i]));
if (i < mac.length - 1) sb.append(":");
}
return sb.toString();
} catch (SocketException e) {
e.printStackTrace();
}
return null;
}
/**
* 获取第一个 IPv4 地址
*/
private static String getIPv4Address(NetworkInterface ni) {
Enumeration<InetAddress> addresses = ni.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress addr = addresses.nextElement();
if (addr instanceof Inet4Address) {
return addr.getHostAddress();
}
}
return "N/A";
}
/**
* 获取默认网关(调用 shell 命令)
*/
private static String getGatewayAddress() {
try {
Process process = Runtime.getRuntime().exec("ip route");
java.util.Scanner scanner = new java.util.Scanner(process.getInputStream());
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (line.startsWith("default")) {
String[] parts = line.split(" ");
return parts[2]; // default via <gateway> dev ...
}
}
scanner.close();
} catch (Exception e) {
e.printStackTrace();
}
return "N/A";
}
/**
* 获取公网 IP
*/
private static String getPublicIp() {
return sendHttpGet("https://ifconfig.me");
}
/**
* 查询 IP 归属地(使用 ipinfo.io
*/
private static JSONObject queryIpLocation(String ip) {
String url = "https://ipinfo.io/" + ip + "/json";
String response = sendHttpGet(url);
AssertLog.info("查询 IP 归属地={}", response);
if (response != null) {
try {
return new JSONObject(response);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
/**
* 发送 HTTP GET 请求
*/
private static String sendHttpGet(String urlString) {
try {
URL url = new URL(urlString);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
if (conn.getResponseCode() == 200) {
java.util.Scanner scanner = new java.util.Scanner(conn.getInputStream());
String result = scanner.useDelimiter("\\A").next();
scanner.close();
return result;
}
} catch (Exception e) {
System.err.println("HTTP 请求失败: " + e.getMessage());
}
return null;
}
public static void bufferedWriter(String filePath, String[] lines){
try (BufferedWriter writer = Files.newBufferedWriter(
Paths.get(filePath),
StandardCharsets.UTF_8,
StandardOpenOption.CREATE, // 创建文件
StandardOpenOption.TRUNCATE_EXISTING // 覆盖写入
)) {
for (String line : lines) {
writer.write(line);
writer.newLine(); // 换行
}
System.out.println("文件写入完成!");
} catch (IOException e) {
System.err.println("写入异常:" + e.getMessage());
e.printStackTrace();
}
}
/**
* 获取 /etc/issue 文件的第二行内容
* @return 第二行字符串,如果不存在则返回 null
*/
public static String getDeviceSN() {
Path issuePath = Paths.get("/etc/issue");
if (!Files.exists(issuePath)) {
System.err.println("文件不存在: /etc/issue");
return null;
}
if (!Files.isReadable(issuePath)) {
System.err.println("无读取权限: /etc/issue");
return null;
}
try (BufferedReader reader = Files.newBufferedReader(issuePath)) {
String line;
int lineNumber = 0;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (!line.isEmpty()) { // 只计数非空行
lineNumber++;
if (lineNumber == 2) {
return line;
}
}
}
// 如果文件少于两行
System.err.println("文件行数不足,只有 " + lineNumber + "");
return null;
} catch (NoSuchFileException e) {
System.err.println("文件未找到: " + e.getMessage());
return null;
} catch (IOException e) {
System.err.println("读取文件异常: " + e.getMessage());
return null;
}
}
public static void main(String[] args) throws Exception {
List<NetworkInterfaceInfo> infos = collectNetworkInfo();
for (NetworkInterfaceInfo info : infos) {
System.out.println(info);
}
}
public static String getFileMD5(String filePath) {
// 检查文件是否存在
Path path = Paths.get(filePath);
if (!Files.exists(path)) {
System.out.println("文件不存在: " + filePath);
return null;
}
// 检查是否是文件(不是目录)
if (!Files.isRegularFile(path)) {
System.out.println("路径不是文件: " + filePath);
return null;
}
// 检查文件是否可读
if (!Files.isReadable(path)) {
System.out.println("文件不可读: " + filePath);
return null;
}
try {
MessageDigest md = MessageDigest.getInstance("MD5");
try (FileInputStream fis = new FileInputStream(filePath)) {
byte[] buffer = new byte[4096];
int length;
while ((length = fis.read(buffer)) != -1) {
md.update(buffer, 0, length);
}
}
byte[] digest = md.digest();
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b));
}
return sb.toString();
} catch (NoSuchAlgorithmException | IOException e) {
System.out.println("计算MD5失败: " + e.getMessage());
return null;
}
}
/**
* 安排脚本在 3 分钟后执行
*/
public static void scheduleScriptIn3Minutes(String SCRIPT_PATH) {
// 使用 at 命令:3 minutes from now
String atCommand = String.format("echo '%s' | at now + 3 minutes", SCRIPT_PATH);
ProcessBuilder pb = new ProcessBuilder("bash", "-c", atCommand);
pb.redirectErrorStream(true); // 合并 stdout 和 stderr
try {
Process process = pb.start();
// 读取命令输出(at 通常会打印任务编号)
java.io.BufferedReader reader = new java.io.BufferedReader(
new java.io.InputStreamReader(process.getInputStream())
);
String line;
StringBuilder output = new StringBuilder();
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
reader.close();
int exitCode = process.waitFor();
if (exitCode == 0) {
System.out.println("✅ 成功安排脚本在 3 分钟后执行:");
System.out.println(" 脚本: " + SCRIPT_PATH);
if (output.length() > 0) {
System.out.println(" at 响应: " + output.toString().trim());
}
} else {
System.err.println("❌ 调度失败,exit code: " + exitCode);
System.err.println(" 输出: " + output.toString().trim());
}
} catch (IOException | InterruptedException e) {
System.err.println("执行 at 命令时出错: " + e.getMessage());
e.printStackTrace();
}
}
}
@@ -0,0 +1,47 @@
package com.tongran.agent.client.utils;
import com.tongran.agent.client.netty.config.ConnectionConfig;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class ClientExample {
public static void main(String[] args) {
EnhancedConnectionManager manager = new EnhancedConnectionManager();
try {
// 配置多个连接
List<ConnectionConfig> configs = Arrays.asList(
// new ConnectionConfig("db_server", "192.168.1.101", 3306, 5),
// new ConnectionConfig("redis_server", "192.168.1.102", 6379, 3),
// new ConnectionConfig("api_server", "api.example.com", 8080, 10),
new ConnectionConfig("server1", "127.0.0.1", 6610, 5)
);
// 批量创建连接
Map<String, Boolean> results = manager.createConnections(configs);
System.out.println("连接创建结果: " + results);
// 等待连接建立
Thread.sleep(3000);
// 根据消息类型路由
manager.routeMessageByType("TYPE_A", "Database query");
// manager.routeMessageByType("TYPE_B", "Cache operation");
// manager.routeMessageByType("UNKNOWN", "Broadcast message");
// 检查连接状态
Map<String, Boolean> status = manager.getConnectionStatus();
System.out.println("连接状态: " + status);
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
// manager.shutdown();
}
}
}
@@ -0,0 +1,109 @@
package com.tongran.agent.client.utils;
import java.io.*;
import java.util.concurrent.TimeUnit;
public class CrontabManager {
// // 要添加的定时任务
// private static final String CRON_JOB = "*/10 * * * * /usr/local/tongran/sbin/test2.sh >/dev/null 2>&1";
// // 用于判断是否已存在的关键标识(可以是脚本路径)
// private static final String JOB_IDENTIFIER = "/usr/local/tongran/sbin/test2.sh";
/**
* 检查并添加定时任务
*/
public static boolean ensureCronJobExists(String CRON_JOB, String JOB_IDENTIFIER) {
try {
// 1. 读取当前用户的 crontab
ProcessBuilder pb = new ProcessBuilder("crontab", "-l");
pb.redirectErrorStream(true);
Process process = pb.start();
// 设置超时(防止卡死)
if (!process.waitFor(5, TimeUnit.SECONDS)) {
process.destroy();
throw new IOException("crontab -l timeout");
}
StringBuilder crontabContent = new StringBuilder();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
crontabContent.append(line).append("\n");
}
}
int exitCode = process.exitValue();
if (exitCode != 0 && exitCode != 1) {
// exit code 1 表示没有 crontab 文件(正常)
throw new IOException("crontab -l failed with exit code: " + exitCode);
}
String content = crontabContent.toString();
// 2. 检查是否已存在该任务
if (content.contains(JOB_IDENTIFIER)) {
System.out.println("Crontab 任务已存在,无需添加。");
return true;
}
// 3. 如果不存在,追加任务
String newCrontab;
if (content.trim().isEmpty()) {
// 原来没有 crontab
newCrontab = CRON_JOB + "\n";
} else {
// 原来有 crontab,在末尾添加新任务
newCrontab = content;
if (!content.endsWith("\n")) {
newCrontab += "\n";
}
newCrontab += CRON_JOB + "\n";
}
// 4. 写入新的 crontab
ProcessBuilder pbWrite = new ProcessBuilder("crontab", "-");
pbWrite.redirectErrorStream(true);
Process writeProcess = pbWrite.start();
try (OutputStreamWriter writer = new OutputStreamWriter(writeProcess.getOutputStream())) {
writer.write(newCrontab);
writer.flush();
}
if (!writeProcess.waitFor(5, TimeUnit.SECONDS)) {
writeProcess.destroy();
throw new IOException("crontab - write timeout");
}
int writeExitCode = writeProcess.exitValue();
if (writeExitCode != 0) {
throw new IOException("crontab - write failed with exit code: " + writeExitCode);
}
System.out.println("Crontab 任务添加成功:\n" + CRON_JOB);
return true;
} catch (IOException | InterruptedException e) {
System.err.println("操作 crontab 失败:" + e.getMessage());
e.printStackTrace();
return false;
}
}
// === 使用示例 ===
public static void main(String[] args) {
// 要添加的定时任务
String CRON_JOB = "*/10 * * * * /usr/local/tongran/sbin/test2.sh >/dev/null 2>&1";
// 用于判断是否已存在的关键标识(可以是脚本路径)
String JOB_IDENTIFIER = "/usr/local/tongran/sbin/test2.sh";
// boolean success = ensureCronJobExists(CRON_JOB,JOB_IDENTIFIER);
// if (success) {
// System.out.println("✅ Crontab 状态正常。");
// } else {
// System.err.println("❌ 操作失败,请检查权限或路径。");
// }
}
}
@@ -0,0 +1,82 @@
package com.tongran.agent.client.utils;
import com.tongran.agent.client.netty.config.ConnectionConfig;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 增强版连接管理器
*/
public class EnhancedConnectionManager {
// private final MultiTargetNettyClient client;
private final Map<String, ConnectionConfig> connectionConfigs;
public EnhancedConnectionManager() {
// this.client = new MultiTargetNettyClient();
this.connectionConfigs = new ConcurrentHashMap<>();
}
/**
* 批量创建连接
*/
public Map<String, Boolean> createConnections(List<ConnectionConfig> configs) {
Map<String, Boolean> results = new HashMap<>();
// configs.forEach(config -> {
// boolean success = client.createConnection(
// config.getConnectionKey(),
// config.getHost(),
// config.getPort(),
// config.getTimeout()
// );
// if (success) {
// connectionConfigs.put(config.getConnectionKey(), config);
// }
// results.put(config.getConnectionKey(), success);
// });
return results;
}
/**
* 根据消息类型路由到不同连接
*/
public void routeMessageByType(String messageType, String message) {
// 这里可以根据业务逻辑决定发送到哪个连接
// switch (messageType) {
// case "TYPE_A":
// client.sendMessage("server1", "[TYPE_A] " + message);
// break;
// case "TYPE_B":
// client.sendMessage("server2", "[TYPE_B] " + message);
// break;
// case "TYPE_C":
// client.sendMessage("server3", "[TYPE_C] " + message);
// break;
// default:
// // 广播到所有连接
// connectionConfigs.keySet().forEach(key ->
// client.sendMessage(key, "[BROADCAST] " + message));
// }
}
/**
* 获取所有连接状态
*/
public Map<String, Boolean> getConnectionStatus() {
Map<String, Boolean> status = new HashMap<>();
// connectionConfigs.forEach((key, config) -> {
// // 这里可以添加更详细的状态检查
// status.put(key, client.sendMessage(key, "PING"));
// });
return status;
}
// public void shutdown() {
// client.shutdown();
// }
}
@@ -0,0 +1,125 @@
package com.tongran.agent.client.utils;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Enumeration;
public class MachineFingerprint {
/**
* 获取服务器硬件指纹:MD5( MAC + CPU信息 )
*/
public static String getHardwareFingerprint() {
StringBuilder data = new StringBuilder();
// 1. 获取第一个非回环网卡的 MAC 地址
String mac = getFirstValidMacAddress();
if (mac != null) {
data.append(mac);
System.out.println("MAC 信息="+mac);
} else {
System.err.println("无法获取 MAC 地址");
}
// 2. 获取 CPU 信息(如 CPU 型号、序列号)
String cpuInfo = getCpuInfo();
if (cpuInfo != null) {
System.out.println("CPU 信息="+cpuInfo);
data.append(cpuInfo);
} else {
System.err.println("无法获取 CPU 信息");
}
data.append(System.currentTimeMillis());
if (data.length() == 0) {
return "unknown";
}
// 3. 生成 MD5
return md5(data.toString());
}
/**
* 获取第一个非回环、UP 状态网卡的 MAC 地址
*/
private static String getFirstValidMacAddress() {
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
if (ni.isLoopback() || !ni.isUp()) continue;
byte[] mac = ni.getHardwareAddress();
if (mac != null && mac.length > 0) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mac.length; i++) {
sb.append(String.format("%02X", mac[i]));
if (i < mac.length - 1) sb.append(":");
}
return sb.toString();
}
}
} catch (SocketException e) {
e.printStackTrace();
}
return null;
}
/**
* 读取 CPU 信息(CPU 型号 + 序列号,如果存在)
*/
private static String getCpuInfo() {
StringBuilder cpuInfo = new StringBuilder();
try {
Process process = Runtime.getRuntime().exec("cat /proc/cpuinfo");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
boolean found = false;
while ((line = reader.readLine()) != null) {
if (line.contains("model name")) {
cpuInfo.append(line.split(":")[1].trim()).append(";");
found = true;
}
// 树莓派等设备有 cpu serial
if (line.contains("Serial")) {
cpuInfo.append("Serial=").append(line.split(":")[1].trim());
found = true;
}
// 多数 x86 服务器没有 serial,可用 processor 数量做补充
if (line.startsWith("processor")) {
// 可选:记录核心数
}
}
reader.close();
return found ? cpuInfo.toString() : null;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 生成 MD5 哈希
*/
private static String md5(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(input.getBytes());
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b & 0xff));
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("MD5 算法不可用", e);
}
}
// ================== 测试 ==================
public static void main(String[] args) {
String fingerprint = getHardwareFingerprint();
System.out.println("服务器硬件指纹 (MD5): " + fingerprint);
}
}
@@ -0,0 +1,80 @@
package com.tongran.agent.client.utils;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class PublicIpFetcher {
private static final String IP_SERVICE_URL = "https://myip.ipip.net";
private static final int TIMEOUT_MS = 10_000; // 10秒超时
/**
* 获取公网 IP(包含归属地信息)
*
* @return IP 信息字符串,如 "当前 IP112.96.15.145,来自于:中国 广东省 深圳市 联通"
* @throws IOException 如果网络请求失败
*/
public static String getPublicIp() throws IOException {
URL url = new URL(IP_SERVICE_URL);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置请求方法
connection.setRequestMethod("GET");
connection.setConnectTimeout(TIMEOUT_MS);
connection.setReadTimeout(TIMEOUT_MS);
// 发起请求
int responseCode = connection.getResponseCode();
if (responseCode != 200) {
throw new IOException("HTTP " + responseCode + " from " + IP_SERVICE_URL);
}
// 读取响应(注意:myip.ipip.net 返回的是 GBK 编码)
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(connection.getInputStream(), "GBK"))) { // 使用 GBK 解码
StringBuilder response = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
response.append(line);
}
return response.toString().trim();
}
}
/**
* 从返回文本中提取纯 IP 地址(如 112.96.15.145
*
* @param ipInfo 来自 getPublicIp() 的完整信息
* @return 纯 IP 字符串,提取失败返回 null
*/
public static String extractIp(String ipInfo) {
if (ipInfo == null || ipInfo.isEmpty()) return null;
// 匹配 IP 地址的正则
java.util.regex.Pattern pattern = java.util.regex.Pattern.compile("(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})");
java.util.regex.Matcher matcher = pattern.matcher(ipInfo);
return matcher.find() ? matcher.group(1) : null;
}
// === 使用示例 ===
public static void main(String[] args) {
try {
String ipInfo = getPublicIp();
System.out.println("完整信息: " + ipInfo);
String pureIp = extractIp(ipInfo);
if (pureIp != null) {
System.out.println("公网 IP: " + pureIp);
} else {
System.out.println("未能提取 IP 地址");
}
} catch (IOException e) {
System.err.println("获取公网 IP 失败: " + e.getMessage());
e.printStackTrace();
}
}
}