From ed801a05c985fdbbcdde9f77294e78d8159a50ed Mon Sep 17 00:00:00 2001 From: gaoyutao Date: Mon, 22 Dec 2025 18:53:49 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0frpc=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/config/ApplicationProperties.java | 53 +---- .../agent/client/core/enums/MsgEnum.java | 2 + .../agent/client/core/eo/FrpMsgEO.java | 15 ++ .../client/service/impl/AgentServiceImpl.java | 198 ++++++++++++++++++ src/main/resources/application-dev.yml | 6 +- src/main/resources/application.yml | 3 +- 6 files changed, 223 insertions(+), 54 deletions(-) create mode 100644 src/main/java/com/tongran/agent/client/core/eo/FrpMsgEO.java diff --git a/src/main/java/com/tongran/agent/client/core/config/ApplicationProperties.java b/src/main/java/com/tongran/agent/client/core/config/ApplicationProperties.java index 627daf3..7851edd 100644 --- a/src/main/java/com/tongran/agent/client/core/config/ApplicationProperties.java +++ b/src/main/java/com/tongran/agent/client/core/config/ApplicationProperties.java @@ -1,10 +1,12 @@ package com.tongran.agent.client.core.config; +import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "spring.application") +@Data public class ApplicationProperties { private String name; @@ -13,54 +15,5 @@ public class ApplicationProperties { private String scriptPath; private String tmpPath; private String tempPath; - - - // Getter 和 Setter 方法 - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public String getConfPath() { - return confPath; - } - - public void setConfPath(String confPath) { - this.confPath = confPath; - } - - public String getScriptPath() { - return scriptPath; - } - - public void setScriptPath(String scriptPath) { - this.scriptPath = scriptPath; - } - - public String getTmpPath() { - return tmpPath; - } - - public void setTmpPath(String tmpPath) { - this.tmpPath = tmpPath; - } - - public String getTempPath() { - return tempPath; - } - - public void setTempPath(String tempPath) { - this.tempPath = tempPath; - } + private String frpPath; } \ No newline at end of file diff --git a/src/main/java/com/tongran/agent/client/core/enums/MsgEnum.java b/src/main/java/com/tongran/agent/client/core/enums/MsgEnum.java index 1892650..70536bc 100644 --- a/src/main/java/com/tongran/agent/client/core/enums/MsgEnum.java +++ b/src/main/java/com/tongran/agent/client/core/enums/MsgEnum.java @@ -67,6 +67,8 @@ public enum MsgEnum { Agent版本更新应答("AGENT_VERSION_UPDATE_RSP"), + 修改frp配置文件应答("UPDATE_FRP_RSP"), + 多网IP探测上报("NETWORK_DETECT"); private String value; diff --git a/src/main/java/com/tongran/agent/client/core/eo/FrpMsgEO.java b/src/main/java/com/tongran/agent/client/core/eo/FrpMsgEO.java new file mode 100644 index 0000000..4649263 --- /dev/null +++ b/src/main/java/com/tongran/agent/client/core/eo/FrpMsgEO.java @@ -0,0 +1,15 @@ +package com.tongran.agent.client.core.eo; + +import lombok.Data; + +@Data +public class FrpMsgEO { + /** 远程端口 */ + private Integer remotePort; + + /** 服务器端口 */ + private String serverPort; + + /** 服务器地址 */ + private String serverAddr; +} diff --git a/src/main/java/com/tongran/agent/client/service/impl/AgentServiceImpl.java b/src/main/java/com/tongran/agent/client/service/impl/AgentServiceImpl.java index e4a3f8b..4305eaf 100644 --- a/src/main/java/com/tongran/agent/client/service/impl/AgentServiceImpl.java +++ b/src/main/java/com/tongran/agent/client/service/impl/AgentServiceImpl.java @@ -9,6 +9,7 @@ import com.tongran.agent.client.core.config.GlobalConfig; import com.tongran.agent.client.core.enums.MsgEnum; import com.tongran.agent.client.core.eo.AgentVersionUpdateEO; import com.tongran.agent.client.core.eo.CollectEO; +import com.tongran.agent.client.core.eo.FrpMsgEO; import com.tongran.agent.client.core.eo.ScriptPolicyEO; import com.tongran.agent.client.core.session.SessionManager; import com.tongran.agent.client.netty.MultiTargetNettyClient; @@ -29,6 +30,7 @@ import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.io.*; +import java.net.ServerSocket; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.text.SimpleDateFormat; @@ -862,9 +864,205 @@ public class AgentServiceImpl implements AgentService { // 添加防火墙策略 addFirewall(netName); } + if(jsonObject.containsKey("frpMsg")){ + String frpMsg = jsonObject.getString("frpMsg"); + FrpMsgEO frpMsgEO = JSON.parseObject(frpMsg, FrpMsgEO.class); + String serverAddr = frpMsgEO.getServerAddr(); + String serverPort = frpMsgEO.getServerPort(); + Integer remotePort = frpMsgEO.getRemotePort(); + long timestamp = System.currentTimeMillis(); + timestamp = Math.round(timestamp / 1000.0); + // 先检查端口是否被占用 + if (remotePort != null && isPortInUse(remotePort)) { + AssertLog.info("端口{}被占用", remotePort); + JSONObject json = new JSONObject(); + json.put("resCode",2); + json.put("remotePort",remotePort); + json.put("resMsg", "端口"+remotePort+"被占用"); + json.put("timestamp",timestamp); + // 判定客户端与服务端是否连接 + if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) { + Message message = Message.builder().clientId(GlobalConfig.CLIENT_ID).dataType(MsgEnum.修改frp配置文件应答.getValue()) + .data(json.toString()).build(); + sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message); + AssertLog.info("发送FRP配置文件更新应答={}",JSON.toJSONString(message)); + } + }else{ + // 端口未被占用,修改frpc.toml配置文件 + updateFrpcConfig(serverAddr, serverPort, remotePort); + // 启动frpc服务 + restartFrpc(properties.getFrpPath()); + } + } } + /** 启动frpc服务 */ + private void restartFrpc(String frpPath) { + long timestamp = System.currentTimeMillis() / 1000; + JSONObject json = new JSONObject(); + int resCode = 1; + String resMsg = "frpc.toml修改成功"; + try { + // 停止进程 + Runtime.getRuntime().exec("pkill -f frpc").waitFor(); + Thread.sleep(1000); + + // 启动进程 + String[] cmd = {frpPath + "/frpc", "-c", frpPath + "/frpc.toml"}; + new ProcessBuilder(cmd) + .directory(new File(frpPath)) + .redirectErrorStream(true) + .start(); + + } catch (Exception e) { + resCode = 0; + resMsg = "操作失败: " + e.getMessage(); + } + + // 基本响应 + json.put("resCode", resCode); + json.put("resMsg", resMsg); + json.put("timestamp", timestamp); + json.put("isRunning", isFrpcRunning()?"1":"0"); + + // 从配置文件获取所有配置 + Map config = readFrpcConfig(frpPath + "/frpc.toml"); + + json.put("serverAddr", config.getOrDefault("serverAddr", "")); + json.put("serverPort", config.getOrDefault("serverPort", "")); + json.put("remotePort", config.getOrDefault("remotePort", "")); + json.put("name", config.getOrDefault("name", "")); + json.put("type", config.getOrDefault("type", "")); + json.put("localIP", config.getOrDefault("localIP", "")); + json.put("localPort", config.getOrDefault("localPort", "")); + + if (Objects.nonNull(sessionManager.getSessionById(GlobalConfig.CLIENT_ID))) { + Message message = Message.builder() + .clientId(GlobalConfig.CLIENT_ID) + .dataType(MsgEnum.修改frp配置文件应答.getValue()) + .data(json.toString()) + .build(); + sessionManager.writeAndFlush(sessionManager.getSessionById(GlobalConfig.CLIENT_ID).getChannel(), message); + AssertLog.info("发送FRP配置文件更新应答={}", JSON.toJSONString(message)); + } + } + + /** + * 从frpc.toml配置文件读取配置 + */ + private Map readFrpcConfig(String configPath) { + Map config = new HashMap<>(); + + try { + File configFile = new File(configPath); + if (!configFile.exists()) { + return config; + } + + List lines = Files.readAllLines(configFile.toPath()); + for (String line : lines) { + String trimmed = line.trim(); + if (trimmed.isEmpty() || trimmed.startsWith("#")) { + continue; + } + + if (trimmed.contains("=")) { + String[] parts = trimmed.split("=", 2); + if (parts.length == 2) { + String key = parts[0].trim(); + String value = parts[1].trim(); + + // 去除引号 + if (value.startsWith("\"") && value.endsWith("\"")) { + value = value.substring(1, value.length() - 1); + } else if (value.startsWith("'") && value.endsWith("'")) { + value = value.substring(1, value.length() - 1); + } + + // 去除尾部注释 + int commentIndex = value.indexOf("#"); + if (commentIndex != -1) { + value = value.substring(0, commentIndex).trim(); + } + + config.put(key, value); + } + } + } + } catch (Exception e) { + AssertLog.warn("读取配置文件失败: {}", e.getMessage()); + } + + return config; + } + /** + * 检查frpc进程是否在运行 + */ + private boolean isFrpcRunning() { + try { + Process process = Runtime.getRuntime().exec(new String[]{"ps", "-ef"}); + String output = new String(process.getInputStream().readAllBytes()); + return output.contains("frpc") && output.contains("-c"); + } catch (IOException e) { + return false; + } + } + /** + * 修改frpc.toml配置文件 + */ + private void updateFrpcConfig(String serverAddr, String serverPort, Integer remotePort) { + try { + // 配置文件路径 + String configFile = properties.getFrpPath() + File.separator + "frpc.toml"; + File file = new File(configFile); + + // 如果文件不存在,创建默认配置 + if (!file.exists()) { + AssertLog.info("FRP配置文件不存在"); + return; + } + + // 读取现有配置文件 + List lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8); + List newLines = new ArrayList<>(); + + for (String line : lines) { + // 替换serverAddr + if (line.trim().startsWith("serverAddr =")) { + newLines.add("serverAddr = \"" + serverAddr + "\""); + } + // 替换serverPort + else if (line.trim().startsWith("serverPort =")) { + newLines.add("serverPort = " + serverPort); + } + // 替换remotePort + else if (remotePort != null && line.trim().startsWith("remotePort =")) { + newLines.add("remotePort = " + remotePort); + } + // 保持其他行不变 + else { + newLines.add(line); + } + } + + // 写入配置文件 + Files.write(file.toPath(), newLines, StandardCharsets.UTF_8); + + } catch (IOException e) { + throw new RuntimeException("修改frpc配置失败: " + e.getMessage(), e); + } + } + /** + * 检查端口是否被占用 + */ + private boolean isPortInUse(int port) { + try (ServerSocket serverSocket = new ServerSocket(port)) { + return false; // 端口可用 + } catch (IOException e) { + return true; // 端口已被占用或其他异常 + } + } @Override public void checkTrAgent() { String CRON_FILE_PATH = properties.getScriptPath()+"/tr_live.cron"; diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index edac848..48e360c 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -1,5 +1,5 @@ server: - port: 7011 + port: -1 servlet: context-path: /tr-agent-client @@ -16,8 +16,8 @@ logging: netty: server: - host: 127.0.0.1 - port: 6620 + host: 120.211.95.173 + port: 56620 client: client-id: client-001 reconnect-interval: 5 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 1a37421..3c92356 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -6,11 +6,12 @@ spring: matching-strategy: ant_path_matcher application: name: tr-agent-client - version: 1.1 + version: 1.1.7 conf-path: /usr/local/tongran/conf script-path: /usr/local/tongran/sbin tmp-path: /usr/local/tongran/tmp temp-path: /usr/local/tongran/temp + frp-path: /usr/local/tongran/frp web: resources: static-locations: classpath*:/META-INF/resources/