JavaM002——System.getProperty("user.dir")获得程序当前路径

本文详细介绍了如何在Java中使用System.getProperty方法获取各种系统属性,包括Java运行环境、操作系统、文件路径等信息,并展示了如何获取当前类的完整路径。

因为所有在java.io中的类都是将相对路径名解释为起始于用户的当前工作目录,所以应该清楚当前的目录。

可以通过调用System.getProperty("user.dir") 来获得。

/**
获得当前类的完整路径。最后一句
*/
 
package org.outman.dms.server;
 
 
 
import java.net.MalformedURLException;
 
import java.net.URI;
 
import java.net.URISyntaxException;
 
import java.net.URL;
 
 
 
/**
*
java.version          Java 运行时环境版本
java.vendor         Java 运行时环境供应商
java.vendor.url         Java 供应商的 URL
java.vm.specification.version         Java 虚拟机规范版本
java.vm.specification.vendor         Java 虚拟机规范供应商
java.vm.specification.name         Java 虚拟机规范名称
java.vm.version         Java 虚拟机实现版本
java.vm.vendor         Java 虚拟机实现供应商
java.vm.name         Java 虚拟机实现名称
java.specification.version         Java 运行时环境规范版本
java.specification.vendor         Java 运行时环境规范供应商
java.specification.name         Java 运行时环境规范名称
os.name         操作系统的名称
os.arch         操作系统的架构
os.version         操作系统的版本
file.separator         文件分隔符(在 UNIX 系统中是“ / ”)
path.separator         路径分隔符(在 UNIX 系统中是“ : ”)
line.separator         行分隔符(在 UNIX 系统中是“ /n ”)
 
java.home         Java 安装目录
java.class.version         Java 类格式版本号
java.class.path         Java 类路径
java.library.path          加载库时搜索的路径列表
java.io.tmpdir         默认的临时文件路径
java.compiler         要使用的 JIT 编译器的名称
java.ext.dirs         一个或多个扩展目录的路径
user.name         用户的账户名称
user.home         用户的主目录
user.dir
*/
 
public class Test {
 
        public static void main(String[] args) throws MalformedURLException, URISyntaxException {
 
                System.out.println("java.home : "+System.getProperty("java.home"));
 
                System.out.println("java.class.version : "+System.getProperty("java.class.version"));
 
                System.out.println("java.class.path : "+System.getProperty("java.class.path"));
 
                System.out.println("java.library.path : "+System.getProperty("java.library.path"));
 
                System.out.println("java.io.tmpdir : "+System.getProperty("java.io.tmpdir"));
 
                System.out.println("java.compiler : "+System.getProperty("java.compiler"));
 
                System.out.println("java.ext.dirs : "+System.getProperty("java.ext.dirs"));
 
                System.out.println("user.name : "+System.getProperty("user.name"));
 
                System.out.println("user.home : "+System.getProperty("user.home"));
 
                System.out.println("user.dir : "+System.getProperty("user.dir"));
 
                System.out.println("===================");
 
                System.out.println("package: "+Test.class.getPackage().getName());
 
                System.out.println("package: "+Test.class.getPackage().toString());
 
                System.out.println("=========================");
 
                String packName = Test.class.getPackage().getName();
 
                /*URL packurl = new URL(packName);
                System.out.println(packurl.getPath());*/
 
                URI packuri = new URI(packName);
 
                System.out.println(packuri.getPath());
 
                //System.out.println(packuri.toURL().getPath());
 
                System.out.println(packName.replaceAll("//.", "/"));
 
                System.out.println(System.getProperty("user.dir")+"/"+(Test.class.getPackage().getName()).replaceAll("//.", "/")+"/");
 
        }
 
} 

System.getProperty("user.dir")  当前工程路径

(Test.class.getPackage().getName()).replaceAll("//.","/")   当前包路径。

转自https://blog.youkuaiyun.com/zhuhuiby/article/details/8569516

package com.example.demo.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.UUID; import cn.hutool.core.thread.ThreadFactoryBuilder; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.demo.dao.entity.WExecuteHost; import com.example.demo.dao.entity.WHostProcess; import com.example.demo.dao.entity.WPersonalHost; import com.example.demo.dao.mapper.WExecuteHostMapper; import com.example.demo.request.ProcessRequest; import com.example.demo.service.WExecuteHostService; import com.example.demo.service.WHostProcessService; import com.example.demo.service.WPersonalHostService; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.interceptor.TransactionAspectSupport; import javax.annotation.PreDestroy; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; @Service public class WExecuteHostServiceImpl extends ServiceImpl<WExecuteHostMapper, WExecuteHost> implements WExecuteHostService { @Autowired private WPersonalHostService wPersonalHostService; @Autowired private WHostProcessService wHostProcessService; @Data private static class UserSession { private Process cmdProcess; private volatile boolean isProcessRunning; private String sessionId; private String logFilePath; private final Queue<String> logBuffer = new ConcurrentLinkedQueue<>(); private static final int BATCH_SIZE = 50; // 批量写入阈值 // 命令执行状态锁 private final AtomicBoolean isExecuting = new AtomicBoolean(false); } private final Map<String, UserSession> userSessions = new ConcurrentHashMap<>(); private final SimpMessagingTemplate messagingTemplate; // 异步日志写入线程池 private static final ExecutorService LOG_WRITER_POOL = Executors.newCachedThreadPool( new ThreadFactoryBuilder().setNamePrefix("log-writer-").build()); // 日志刷新调度器 private static final ScheduledExecutorService LOG_FLUSH_SCHEDULER = Executors.newSingleThreadScheduledExecutor(); public WExecuteHostServiceImpl(SimpMessagingTemplate messagingTemplate) { this.messagingTemplate = messagingTemplate; startLogFlushService(); } // 初始化日志刷新服务 private void startLogFlushService() { LOG_FLUSH_SCHEDULER.scheduleAtFixedRate(() -> { userSessions.values().forEach(session -> { if (!session.getLogBuffer().isEmpty()) { List<String> batch = CollUtil.newArrayList(session.getLogBuffer()); session.getLogBuffer().clear(); asyncWriteLog(session.getLogFilePath(), String.join("\n", batch)); } }); }, 0, 1, TimeUnit.SECONDS); } @Override public void executeCommand(String command, String host, String userId) { // 0. ABORT命令特殊处理(优先处理终止请求) if ("ABORT".equalsIgnoreCase(command)) { handleAbort(userId); return; } // 1. 权限校验 if (!validateUserHost(userId, host)) { sendError("无权访问该主机", userId); return; } // 2. 检查用户当前会话状态 UserSession session = userSessions.get(userId); if (session != null && session.isExecuting.get()) { sendError("已有命令执行中,请等待完成或使用ABORT终止", userId); return; } // 3. 创建新会话(带原子状态检查) session = userSessions.computeIfAbsent(userId, key -> { UserSession newSession = createNewSession(userId, host); if (newSession != null) { newSession.isExecuting.set(true); // 标记为执行中 } return newSession; }); if (session == null) return; // 4. 写入日志并执行命令 try { // 确保获得执行锁 if (!session.isExecuting.compareAndSet(true, true)) { sendError("命令执行冲突,请重试", userId); return; } session.getLogBuffer().offer("——————————————— " + DateUtil.now() + " ———————————————"); // 发送命令到进程 IoUtil.write(session.getCmdProcess().getOutputStream(), Charset.forName("gbk"), true, command + "\n"); } catch (Exception e) { session.isExecuting.set(false); // 发生异常时释放锁 sendError("命令发送失败: " + e.getMessage(), userId); } } @Override public Boolean isCommandExecuting(String userId) { UserSession session = userSessions.get(userId); return session != null && session.isExecuting.get(); } @Override public void handleAbort(String userId) { UserSession session = userSessions.get(userId); if (session == null || session.getCmdProcess() == null) { sendError("没有活动的命令进程", userId); return; } try { long pid = session.getCmdProcess().pid(); System.out.println("Attempting to kill process with PID: " + pid); // 使用 taskkill 命令终止进程 ProcessBuilder taskKill = new ProcessBuilder("taskkill", "/F", "/T", "/PID", String.valueOf(pid)); Process killProcess = taskKill.start(); // 等待命令执行完成 int exitCode = killProcess.waitFor(); System.out.println("taskkill exit code: " + exitCode); if (exitCode == 0) { // 进程终止成功 session.isExecuting.set(false); cleanupSession(userId); messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, "✔️" + "进程已通过 taskkill 终止 (PID: " + pid + ")"); messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, ""); messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, System.getProperty("user.dir") + ">"); } else { // 进程终止失败 sendError("终止进程失败,错误码: " + exitCode, userId); } } catch (IOException | InterruptedException e) { System.err.println("Error killing process: " + e.getMessage()); sendError("终止进程失败: " + e.getMessage(), userId); } } @Override public String startProcess(ProcessRequest processRequest) { try { // 数据库表新增数据 String p13 = processRequest.getP13().trim(); String processName = processRequest.getProcessName().trim(); String productNumber = processRequest.getProductNumber().trim(); String executeHost = processRequest.getExecuteHost().trim(); String department = processRequest.getDepartment().trim(); String version = processRequest.getVersion().trim(); if (StrUtil.isEmpty(p13) || StrUtil.isEmpty(processName) || StrUtil.isEmpty(productNumber) || StrUtil.isEmpty(executeHost) || StrUtil.isEmpty(department) || StrUtil.isEmpty(version)) { return "新增进程失败。"; } WHostProcess wHostProcess = new WHostProcess(); wHostProcess.setP13(p13); wHostProcess.setProcessName(processName); wHostProcess.setProductNumber(productNumber); wHostProcess.setHost(executeHost); wHostProcess.setPid(null); wHostProcess.setDepartment(department); wHostProcess.setState("离线"); wHostProcess.setVersion(version); wHostProcess.setBeginTime(new Date()); boolean saveResult = wHostProcessService.save(wHostProcess); if (saveResult) { LambdaQueryWrapper<WPersonalHost> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper .eq(WPersonalHost::getExecuteHost, processRequest.getExecuteHost()) .eq(WPersonalHost::getSharedHost, processRequest.getSharedHost()); WPersonalHost wPersonalHost = wPersonalHostService.getOne(queryWrapper); // 执行py启动命令 //todo 后续动态 String pythonEXEPath = "D:\\miniforge\\envs" + File.separator + p13 + File.separator + "python" + wPersonalHost.getPythonEnv() + File.separator + "python.exe"; String mainPyPath = System.getProperty("user.dir") + File.separator + "python-package" + File.separator + executeHost + File.separator + p13 + File.separator + "test" + File.separator + "main.py"; executeCommand(pythonEXEPath + " " + mainPyPath, processRequest.getExecuteHost(), processRequest.getP13()); return "正在启动项目..."; } } catch (Exception e) { e.printStackTrace(); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } return "新增进程失败。"; } private boolean validateUserHost(String userId, String executeHost) { LambdaQueryWrapper<WPersonalHost> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(WPersonalHost::getP13, userId) .eq(WPersonalHost::getExecuteHost, executeHost) .eq(WPersonalHost::getState, "在线"); return wPersonalHostService.getOne(queryWrapper) != null; } private UserSession createNewSession(String userId, String executeHost) { try { UserSession session = new UserSession(); session.setSessionId(UUID.randomUUID().toString()); session.setLogFilePath(initLogFile(userId, executeHost)); // 启动CMD进程(带唯一标题) ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/k", "title " + generateUniqueTitle(userId)); session.setCmdProcess(pb.redirectErrorStream(true).start()); // 启动输出监听线程 startOutputThread(session, userId); return session; } catch (IOException e) { sendError("进程启动失败: " + e.getMessage(), userId); return null; } } private String initLogFile(String userId, String executeHost) { // 1. 构建基础路径(使用File.separator) String baseDir = FileUtil.normalize( System.getProperty("user.dir") + File.separator + "command-log"); // 2. 构建安全路径(统一使用File.separator) String safePath = FileUtil.normalize( baseDir + File.separator + userId + File.separator + executeHost + File.separator + "项目名称"); // 3. 安全校验(现在路径分隔符一致) if (!safePath.startsWith(baseDir)) { throw new SecurityException("非法日志路径: " + safePath); } // 4. 创建目录(自动处理路径分隔符) FileUtil.mkdir(safePath); // 5. 生成日志文件 String logFileName = DateUtil.today() + ".log"; return FileUtil.touch(safePath + File.separator + logFileName).getAbsolutePath(); } private void startOutputThread(UserSession session, String userId) { new Thread(() -> { try (BufferedReader reader = new BufferedReader( new InputStreamReader(session.getCmdProcess().getInputStream(), "GBK"))) { String line; while ((line = reader.readLine()) != null) { messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, line); session.getLogBuffer().offer(line); } } catch (Exception e) { sendError("输出流异常: " + e.getMessage(), userId); } finally { session.isExecuting.set(false); // 命令结束释放锁 cleanupSession(userId); // 通知前端命令执行结束 messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, "⏹ 该命令执行已结束"); } }).start(); } private void asyncWriteLog(String logFilePath, String content) { CompletableFuture.runAsync(() -> { try { // 替换掉日志文件中没用的信息,如多余的路径信息 String currentDir = System.getProperty("user.dir"); String escapedDir = currentDir.replace("\\", "\\\\"); String regex = "(?m)^\\s*" + escapedDir + ">(?!\\S)\\s*"; // 创建 Pattern 和 Matcher 对象 Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(content); // 检查是否存在匹配的模式 if (matcher.find()) { // 如果存在匹配,则进行替换 String cleaned = content.replaceAll(regex, ""); FileUtil.appendString(cleaned + System.lineSeparator(), logFilePath, "GBK"); } else { FileUtil.appendString(content + System.lineSeparator(), logFilePath, "GBK"); } } catch (Exception e) { System.err.println("日志写入失败: " + e.getMessage()); } }, LOG_WRITER_POOL); } private void cleanupSession(String userId) { UserSession session = userSessions.remove(userId); if (session != null) { try { if (session.getCmdProcess() != null) { session.getCmdProcess().destroyForcibly(); } // 强制将剩余日志写入文件(新增代码) if (!session.getLogBuffer().isEmpty()) { asyncWriteLog(session.getLogFilePath(), String.join("\n", session.getLogBuffer())); session.getLogBuffer().clear(); } } catch (Exception ignored) { } } } @PreDestroy public void cleanup() { LOG_FLUSH_SCHEDULER.shutdown(); LOG_WRITER_POOL.shutdown(); userSessions.forEach((userId, session) -> { try { if (session.getCmdProcess() != null) { session.getCmdProcess().destroyForcibly(); } } catch (Exception ignored) { } }); userSessions.clear(); } /** * 发送错误日志 */ private void sendError(String message, String userId) { try { messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, "❌" + message); } catch (Exception ignored) { } } /** * 生成cmd窗口唯一id */ private String generateUniqueTitle(String userId) { return "CMD_SESSION_" + userId + "_" + System.currentTimeMillis(); } } 仔细阅读我的代码,现在有个bug,我执行下面的main.py文件,会报错请输入你的名字: 发生未知错误: EOF when reading a line import random import sys from datetime import datetime def generate_random_number(min_val=1, max_val=100): """ 生成一个指定范围内的随机整数 参数: min_val (int): 最小值,默认为1 max_val (int): 最大值,默认为100 返回: int: 随机生成的整数 """ return random.randint(min_val, max_val) def check_even_odd(number): """ 检查一个数字是奇数还是偶数 参数: number (int): 要检查的数字 返回: str: "偶数" 或 "奇数" """ return "偶数" if number % 2 == 0 else "奇数" def greet_user(name): """ 向用户问好 参数: name (str): 用户名 """ print(f"你好, {name}! 当前时间是: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") def main(): """ 主函数 """ print("欢迎使用随机数生成器!") try: user_name = input("请输入你的名字: ") greet_user(user_name) count = int(input("你想生成多少个随机数? (1-10): ")) if count < 1 or count > 10: print("请输入1到10之间的数字!") return for i in range(count): num = generate_random_number() print(f"随机数 #{i+1}: {num} ({check_even_odd(num)})") print("感谢使用!") except ValueError: print("错误: 请输入有效的数字!") except KeyboardInterrupt: print("\n程序被用户中断") except Exception as e: print(f"发生未知错误: {e}") if __name__ == "__main__": main()
06-18
package com.NetSDKDemo; import com.demo.impl.Websocket; import com.sun.jna.Pointer; import com.sun.jna.ptr.IntByReference; import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import static com.NetSDKDemo.ClientDemo.hCNetSDK; /** * 视频取流预览,下载,抓图mok * * @create 2022-03-30-9:48 */ public class VideoDemo { static fPlayEScallback fPlayescallback; // 裸码流回调函数 static FileOutputStream outputStream; static IntByReference m_lPort = new IntByReference(-1); /** * 获取实时裸码流回调数据 * * @param userID 登录句柄 * @param iChannelNo 通道号参数 */ public static int getESRealStreamData(int userID, int iChannelNo) { if (userID == -1) { System.out.println("请先注册"); return -1; } HCNetSDK.NET_DVR_PREVIEWINFO previewInfo = new HCNetSDK.NET_DVR_PREVIEWINFO(); previewInfo.read(); previewInfo.hPlayWnd = null; // 窗口句柄,从回调取流不显示一般设置为空 previewInfo.lChannel = iChannelNo; // 通道号 previewInfo.dwStreamType = 0; // 0-主码流,1-子码流,2-三码流,3-虚拟码流,以此类推 previewInfo.dwLinkMode = 1; // 连接方式:0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4- RTP/RTSP,5- RTP/HTTP,6- HRUDP(可靠传输) ,7- RTSP/HTTPS,8- NPQ previewInfo.bBlocked = 1; // 0- 非阻塞取流,1- 阻塞取流 previewInfo.byProtoType = 0; // 应用层取流协议:0- 私有协议,1- RTSP协议 previewInfo.write(); // 开启预览 int Handle = hCNetSDK.NET_DVR_RealPlay_V40(userID, previewInfo, null, null); if (Handle == -1) { int iErr = hCNetSDK.NET_DVR_GetLastError(); System.err.println("取流失败" + iErr); return -1; } System.out.println("取流成功"); // 设置裸码流回调函数 if (fPlayescallback == null) { System.out.println("111"); fPlayescallback = new fPlayEScallback(); } if (!hCNetSDK.NET_DVR_SetESRealPlayCallBack(Handle, fPlayescallback, null)) { System.err.println("设置裸码流回调失败,错误码:" + hCNetSDK.NET_DVR_GetLastError()); } /* Boolean bStopSaveVideo = hCNetSDK.NET_DVR_StopSaveRealData(lPlay); if (bStopSaveVideo == false) { int iErr = hCNetSDK.NET_DVR_GetLastError(); System.out.println("NET_DVR_StopSaveRealData failed" + iErr); return; } System.out.println("NET_DVR_StopSaveRealData suss"); if (lPlay>=0) { if (hCNetSDK.NET_DVR_StopRealPlay(lPlay)) { System.out.println("停止预览成功"); return; } }*/ return Handle; } static class fPlayEScallback implements HCNetSDK.FPlayESCallBack { private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); private boolean start = false; @Override public void invoke(int lPreviewHandle, HCNetSDK.NET_DVR_PACKET_INFO_EX pstruPackInfo, Pointer pUser) { System.out.println("111"); pstruPackInfo.read(); // 保存I帧和P帧数据 // 从第一帧 I 帧开始解析 if (pstruPackInfo.dwPacketType == 1) { start = true; } if (!start) { return; } if (pstruPackInfo.dwPacketType == 1 || pstruPackInfo.dwPacketType == 3) { // 如果是 I 帧,则将上一帧 I 帧到当前 I 帧的数据发送给 Websocket 解析 if (pstruPackInfo.dwPacketType == 1) { byte[] byteArray = outputStream.toByteArray(); outputStream.reset(); if (byteArray.length > 0) { // 通过websocket发送 long start = System.currentTimeMillis(); Websocket.sendBuffer(byteArray); System.out.println("cost: "+(System.currentTimeMillis() - start)); } } // System.out.println("dwPacketType:" + pstruPackInfo.dwPacketType // + ":wWidth:" + pstruPackInfo.wWidth // + ":wHeight:" + pstruPackInfo.wHeight // + ":包长度:" + pstruPackInfo.dwPacketSize // + ":帧号:" + pstruPackInfo.dwFrameNum); ByteBuffer buffers = pstruPackInfo.pPacketBuffer.getByteBuffer(0, pstruPackInfo.dwPacketSize); byte[] bytes = new byte[pstruPackInfo.dwPacketSize]; buffers.rewind(); buffers.get(bytes); try { outputStream.write(bytes); } catch (IOException e) { e.printStackTrace(); } } } } } package com.NetSDKDemo; import Common.osSelect; import com.sun.jna.Native; import com.sun.jna.Pointer; /** * @create 2020-12-24-17:55 */ public class ClientDemo { static HCNetSDK hCNetSDK = null; static int lUserID = -1;// 用户句柄 static int lPlayHandle = -1; // 预览句柄 static FExceptionCallBack_Imp fExceptionCallBack; static class FExceptionCallBack_Imp implements HCNetSDK.FExceptionCallBack { public void invoke(int dwType, int lUserID, int lHandle, Pointer pUser) { System.out.println("异常事件类型:" + dwType); } } /** * 动态库加载 * * @return */ private static boolean createSDKInstance() { if (hCNetSDK == null) { synchronized (HCNetSDK.class) { String strDllPath = ""; try { if (osSelect.isWindows()) // win系统加载库路径 strDllPath = System.getProperty("user.dir") + "\\lib\\HCNetSDK.dll"; else if (osSelect.isLinux()) // Linux系统加载库路径 strDllPath = System.getProperty("user.dir") + "/lib/libhcnetsdk.so"; hCNetSDK = (HCNetSDK) Native.loadLibrary(strDllPath, HCNetSDK.class); } catch (Exception ex) { System.out.println("loadLibrary: " + strDllPath + " Error: " + ex.getMessage()); return false; } } } return true; } public static void start() { if (hCNetSDK == null) { if (!createSDKInstance()) { System.out.println("Load SDK fail"); return; } } // linux系统建议调用以下接口加载组件库 if (osSelect.isLinux()) { HCNetSDK.BYTE_ARRAY ptrByteArray1 = new HCNetSDK.BYTE_ARRAY(256); HCNetSDK.BYTE_ARRAY ptrByteArray2 = new HCNetSDK.BYTE_ARRAY(256); // 这里是库的绝对路径,请根据实际情况修改,注意改路径必须有访问权限 String strPath1 = System.getProperty("user.dir") + "/lib/libcrypto.so.1.1"; String strPath2 = System.getProperty("user.dir") + "/lib/libssl.so.1.1"; System.arraycopy(strPath1.getBytes(), 0, ptrByteArray1.byValue, 0, strPath1.length()); ptrByteArray1.write(); hCNetSDK.NET_DVR_SetSDKInitCfg(HCNetSDK.NET_SDK_INIT_CFG_LIBEAY_PATH, ptrByteArray1.getPointer()); System.arraycopy(strPath2.getBytes(), 0, ptrByteArray2.byValue, 0, strPath2.length()); ptrByteArray2.write(); hCNetSDK.NET_DVR_SetSDKInitCfg(HCNetSDK.NET_SDK_INIT_CFG_SSLEAY_PATH, ptrByteArray2.getPointer()); String strPathCom = System.getProperty("user.dir") + "/lib/"; HCNetSDK.NET_DVR_LOCAL_SDK_PATH struComPath = new HCNetSDK.NET_DVR_LOCAL_SDK_PATH(); System.arraycopy(strPathCom.getBytes(), 0, struComPath.sPath, 0, strPathCom.length()); struComPath.write(); hCNetSDK.NET_DVR_SetSDKInitCfg(HCNetSDK.NET_SDK_INIT_CFG_SDK_PATH, struComPath.getPointer()); } // SDK初始化,一个程序只需要调用一次 boolean initSuc = hCNetSDK.NET_DVR_Init(); // 异常消息回调 if (fExceptionCallBack == null) { fExceptionCallBack = new FExceptionCallBack_Imp(); } Pointer pUser = null; if (!hCNetSDK.NET_DVR_SetExceptionCallBack_V30(0, 0, fExceptionCallBack, pUser)) { return; } System.out.println("设置异常消息回调成功"); // 启动SDK写日志 hCNetSDK.NET_DVR_SetLogToFile(3, "./sdkLog", false); // 设备登录 lUserID = loginDevice("109.136.71.182", (short) 10000, "admin", "sess2025"); System.out.println("实时获取裸码流示例代码"); lPlayHandle = VideoDemo.getESRealStreamData(lUserID, 33); } /** * 登录设备,支持 V40 和 V30 版本,功能一致。 * * @param ip 设备IP地址 * @param port SDK端口,默认为设备的8000端口 * @param user 设备用户名 * @param psw 设备密码 * @return 登录成功返回用户ID,失败返回-1 */ public static int loginDevice(String ip, short port, String user, String psw) { // 创建设备登录信息和设备信息对象 HCNetSDK.NET_DVR_USER_LOGIN_INFO loginInfo = new HCNetSDK.NET_DVR_USER_LOGIN_INFO(); HCNetSDK.NET_DVR_DEVICEINFO_V30 deviceInfo = new HCNetSDK.NET_DVR_DEVICEINFO_V30(); // 设置设备IP地址 byte[] deviceAddress = new byte[HCNetSDK.NET_DVR_DEV_ADDRESS_MAX_LEN]; byte[] ipBytes = ip.getBytes(); System.arraycopy(ipBytes, 0, deviceAddress, 0, Math.min(ipBytes.length, deviceAddress.length)); loginInfo.sDeviceAddress = deviceAddress; // 设置用户名和密码 byte[] userName = new byte[HCNetSDK.NET_DVR_LOGIN_USERNAME_MAX_LEN]; byte[] password = psw.getBytes(); System.arraycopy(user.getBytes(), 0, userName, 0, Math.min(user.length(), userName.length)); System.arraycopy(password, 0, loginInfo.sPassword, 0, Math.min(password.length, loginInfo.sPassword.length)); loginInfo.sUserName = userName; // 设置端口和登录模式 loginInfo.wPort = port; loginInfo.bUseAsynLogin = false; // 同步登录 loginInfo.byLoginMode = 0; // 使用SDK私有协议 // 执行登录操作 int userID = hCNetSDK.NET_DVR_Login_V30(ip, port, user, psw, deviceInfo); if (userID == -1) { System.err.println("登录失败,错误码为: " + hCNetSDK.NET_DVR_GetLastError()); } else { System.out.println(ip + " 设备登录成功!"); // 处理通道号逻辑 int startDChan = deviceInfo.byStartDChan; System.out.println("预览起始通道号: " + startDChan); } return userID; // 返回登录结果 } }我控制台输出取流成功之后为什么显示异常事件类型:32773
最新发布
09-19
我现在有多个服务,a是前端,b是java后端,还有很多个小java客户端。我的小客户端是部署在多台电脑上的,使用websocket执行代码,我现在想要的是过去小客户端的所有websocket要用b进行一个中转,即a<=>b<=>小客户端。 这是我的a的代码: import { Client } from '@stomp/stompjs'; import SockJS from 'sockjs-client'; // npm remove stompjs // npm install @stomp/stompjs sockjs-client --save export default { connect() { return new Promise((resolve, reject) => { const socket = new SockJS('http://10.228.73.15:31001/websocket'); const stompClient = new Client({ webSocketFactory: () => socket, debug: () => { // 禁用调试输出,或根据需要处理 }, reconnectDelay: 5000, heartbeatIncoming: 4000, heartbeatOutgoing: 4000, }); stompClient.onConnect = (frame) => { console.log('Connected:', frame); resolve(stompClient); // 连接成功后立即订阅个人主题 // stompClient.subscribe(`/topic/commandOutput/${userId}`, (message) => { // console.log(1,message) // }); }; stompClient.onStompError = (error) => { console.error('Connection error:', error); reject(error); }; stompClient.activate(); }); } };<template> <div> <span style="display: flex; justify-content: center; margin-bottom: 8px"> <img alt="Vue logo" src="../assets/logo.png" style="width: 90px;"> </span> <span style="display: flex; justify-content: center; margin-bottom: 8px"> 当前登录用户: <input v-model="userId" placeholder="请输入p13" @keyup.enter="connectWebSocket"> <button @click="connectWebSocket" style="margin-left: 10px;">登录</button> </span> <span style="display: flex; justify-content: center"> <input ref="commandInput" v-model="currentCommand" style="width: 600px" placeholder="输入任意命令(如:ipconfig / dir)" :disabled="isExecuting" @keyup.enter="sendCommand" > <button @click="sendCommand" style="margin-left: 10px;" :disabled="isExecuting">执行命令</button> <button @click="abortCommand" style="margin-left: 10px; background: #ff4d4f" :disabled="!isExecuting" > 中止命令 </button> </span> <div style="display: flex"> <div style="margin-top: 20px; width: 30%;"> <h3 style="text-align: center">已执行命令</h3> <div style="height: 482px; overflow-y: auto; background: #f8f8f8;"> <div v-for="(msg, index) in messages" :key="index" style="margin-top: 8px; margin-left: 8px; border-radius: 4px; overflow-y: auto;" > {{ msg }} </div> </div> </div> <div style="padding: 20px; width: 70%"> <h3 style="text-align: center">实时命令输出</h3> <pre ref="outputPre" style="background: #282c34; color: #fff; padding: 15px; border-radius: 4px; height: 450px; overflow-y: auto" > {{ currentOutput }} </pre> </div> </div> </div> </template> <script> import websocket from '@/utils/websocket' export default { name: 'HelloWorld', props: { msg: String }, data() { return { currentCommand: '', messages: [], currentOutput: '', stompClient: null, index: 1, userId: '', isExecuting: false // 控制命令是否正在执行 }; }, methods: { async connectWebSocket() { alert("登录成功"); try { this.stompClient = await websocket.connect(this.userId); this.$nextTick(() => { this.$refs.commandInput.focus(); // 命令结束后重新聚焦到输入框 }); const subscription = this.stompClient.subscribe(`/topic/commandOutput/${this.userId}`, (message) => { const output = message.body; if (output.startsWith("⏹")) { this.isExecuting = false; // 命令执行结束,允许用户继续输入 this.$nextTick(() => { this.$refs.commandInput.focus(); // 命令结束后重新聚焦到输入框 }); return; } this.currentOutput += output.trim() + '\n'; this.$nextTick(() => { if (this.$refs.outputPre) { this.$refs.outputPre.scrollTop = this.$refs.outputPre.scrollHeight; } }); }); this.stompClient.onDisconnect = () => { subscription.unsubscribe(); }; // 检查命令执行状态 await this.checkCommandStatus(); } catch (error) { console.error('WebSocket连接失败:', error); } }, async checkCommandStatus() { try { const response = await fetch(`/win/status/${this.userId}`); if (!response.ok) { console.error('无法获取命令状态'); return; } this.isExecuting = await response.json(); // 更新前端状态 if (this.isExecuting) { alert("上一个命令尚未完成,请等待或中止命令!"); } } catch (error) { console.error('检查命令状态时出错:', error); } }, sendCommand() { if (this.userId === '') { alert("请输入当前登录用户"); return; } this.isExecuting = true; if (this.currentCommand.trim() && this.stompClient) { // 删除 currentOutput 的最后一个非空行 if (this.currentOutput) { let lastNewLineIndex = this.currentOutput.lastIndexOf('\n'); let lastNonEmptyIndex = -1; // 从后往前遍历,找到最后一个非空行的起始位置 for (let i = this.currentOutput.length - 1; i >= 0; i--) { if (this.currentOutput[i] === '\n') { // 如果当前字符是换行符,并且之前的字符不是空白字符,则记录位置 if (lastNewLineIndex !== -1 && i < lastNewLineIndex) { lastNonEmptyIndex = lastNewLineIndex; break; } lastNewLineIndex = i; } else if (this.currentOutput[i].trim() !== '') { // 找到非空字符,更新 lastNewLineIndex lastNewLineIndex = i; } } // 如果找到了非空行,则删除它 if (lastNonEmptyIndex !== -1) { const nextNewLineIndex = this.currentOutput.indexOf('\n', lastNonEmptyIndex); if (nextNewLineIndex === -1) { this.currentOutput = this.currentOutput.substring(0, lastNonEmptyIndex); } else { this.currentOutput = this.currentOutput.substring(0, lastNonEmptyIndex) + this.currentOutput.substring(nextNewLineIndex); } } // 删除最后一个空行 if (this.currentOutput.endsWith('\n')) { this.currentOutput = this.currentOutput.substring(0, this.currentOutput.length - 1); } } // 发送命令 this.stompClient.publish({ destination: '/app/executeCommand', body: JSON.stringify({ command: this.currentCommand.trim(), //todo 之后改为动态 host: '10.228.73.15', userId: this.userId }), }); this.messages.push(this.index + '. ' + this.currentCommand); this.index += 1; this.currentCommand = ''; } }, async abortCommand() { if (this.stompClient) { const response = await fetch(`/win/handleAbort/${this.userId}`); if (response){ this.messages.push("已发送中止命令"); } } }, onTabChange(key, type) { this[type] = key; }, }, beforeDestroy() { if (this.stompClient) { this.stompClient.deactivate(); } } } </script> <style scoped> pre { white-space: pre-line; background-color: #f4f4f4; padding: 10px; border: 1px solid #ddd; } </style> 这是我的小客户端的代码: package com.example.demo.config; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/websocket") .setAllowedOriginPatterns("*") .withSockJS(); } }package com.example.demo.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.UUID; import cn.hutool.core.thread.ThreadFactoryBuilder; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.example.demo.dao.entity.WExecuteHost; import com.example.demo.dao.entity.WHostProcess; import com.example.demo.dao.entity.WPersonalHost; import com.example.demo.dao.mapper.WExecuteHostMapper; import com.example.demo.request.ProcessRequest; import com.example.demo.service.WExecuteHostService; import com.example.demo.service.WHostProcessService; import com.example.demo.service.WPersonalHostService; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.interceptor.TransactionAspectSupport; import javax.annotation.PreDestroy; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.regex.Matcher; import java.util.regex.Pattern; @Service public class WExecuteHostServiceImpl extends ServiceImpl<WExecuteHostMapper, WExecuteHost> implements WExecuteHostService { @Autowired private WPersonalHostService wPersonalHostService; @Autowired private WHostProcessService wHostProcessService; @Data private static class UserSession { private Process cmdProcess; private volatile boolean isProcessRunning; private String sessionId; private String logFilePath; private final Queue<String> logBuffer = new ConcurrentLinkedQueue<>(); private static final int BATCH_SIZE = 50; // 批量写入阈值 // 命令执行状态锁 private final AtomicBoolean isExecuting = new AtomicBoolean(false); } private final Map<String, UserSession> userSessions = new ConcurrentHashMap<>(); private final SimpMessagingTemplate messagingTemplate; // 异步日志写入线程池 private static final ExecutorService LOG_WRITER_POOL = Executors.newCachedThreadPool( new ThreadFactoryBuilder().setNamePrefix("log-writer-").build()); // 日志刷新调度器 private static final ScheduledExecutorService LOG_FLUSH_SCHEDULER = Executors.newSingleThreadScheduledExecutor(); public WExecuteHostServiceImpl(SimpMessagingTemplate messagingTemplate) { this.messagingTemplate = messagingTemplate; startLogFlushService(); } // 初始化日志刷新服务 private void startLogFlushService() { LOG_FLUSH_SCHEDULER.scheduleAtFixedRate(() -> { userSessions.values().forEach(session -> { if (!session.getLogBuffer().isEmpty()) { List<String> batch = CollUtil.newArrayList(session.getLogBuffer()); session.getLogBuffer().clear(); asyncWriteLog(session.getLogFilePath(), String.join("\n", batch)); } }); }, 0, 1, TimeUnit.SECONDS); } @Override public void executeCommand(String command, String host, String userId) { // 0. ABORT命令特殊处理(优先处理终止请求) if ("ABORT".equalsIgnoreCase(command)) { handleAbort(userId); return; } // 1. 权限校验 if (!validateUserHost(userId, host)) { sendError("无权访问该主机", userId); return; } // 2. 检查用户当前会话状态 UserSession session = userSessions.get(userId); if (session != null && session.isExecuting.get()) { sendError("已有命令执行中,请等待完成或使用ABORT终止", userId); return; } // 3. 创建新会话(带原子状态检查) session = userSessions.computeIfAbsent(userId, key -> { UserSession newSession = createNewSession(userId, host); if (newSession != null) { newSession.isExecuting.set(true); // 标记为执行中 } return newSession; }); if (session == null) return; // 4. 写入日志并执行命令 try { // 确保获得执行锁 if (!session.isExecuting.compareAndSet(true, true)) { sendError("命令执行冲突,请重试", userId); return; } session.getLogBuffer().offer("——————————————— " + DateUtil.now() + " ———————————————"); // 发送命令到进程 IoUtil.write(session.getCmdProcess().getOutputStream(), Charset.forName("GBK"), true, command + "\n"); } catch (Exception e) { session.isExecuting.set(false); // 发生异常时释放锁 sendError("命令发送失败: " + e.getMessage(), userId); } } @Override public Boolean isCommandExecuting(String userId) { UserSession session = userSessions.get(userId); return session != null && session.isExecuting.get(); } @Override public void handleAbort(String userId) { UserSession session = userSessions.get(userId); if (session == null || session.getCmdProcess() == null) { sendError("没有活动的命令进程", userId); return; } try { long pid = session.getCmdProcess().pid(); System.out.println("Attempting to kill process with PID: " + pid); // 使用 taskkill 命令终止进程 ProcessBuilder taskKill = new ProcessBuilder("taskkill", "/F", "/T", "/PID", String.valueOf(pid)); Process killProcess = taskKill.start(); // 等待命令执行完成 int exitCode = killProcess.waitFor(); System.out.println("taskkill exit code: " + exitCode); if (exitCode == 0) { // 进程终止成功 session.isExecuting.set(false); cleanupSession(userId); messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, "✔️" + "进程已通过 taskkill 终止 (PID: " + pid + ")"); messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, ""); messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, System.getProperty("user.dir") + ">"); } else { // 进程终止失败 sendError("终止进程失败,错误码: " + exitCode, userId); } } catch (IOException | InterruptedException e) { System.err.println("Error killing process: " + e.getMessage()); sendError("终止进程失败: " + e.getMessage(), userId); } } @Override public String startProcess(ProcessRequest processRequest) { try { // 数据库表新增数据 String id = processRequest.getId(); String p13 = processRequest.getP13().trim(); String processName = processRequest.getProcessName().trim(); String productNumber = processRequest.getProductNumber().trim(); String executeHost = processRequest.getExecuteHost().trim(); String department = processRequest.getDepartment().trim(); String version = processRequest.getVersion().trim(); String type = processRequest.getType(); boolean saveOrUpdateResult; if (type.equals("新增")) { // 判断产品编号是否唯一 LambdaQueryWrapper<WHostProcess> processWrapper = new LambdaQueryWrapper<>(); processWrapper.eq(WHostProcess::getProductNumber, productNumber); WHostProcess process = wHostProcessService.getOne(processWrapper); if (process != null) { return "该产品编号已被他人使用,请使用其他的产品编号。"; } if (StrUtil.isEmpty(p13) || StrUtil.isEmpty(processName) || StrUtil.isEmpty(productNumber) || StrUtil.isEmpty(executeHost) || StrUtil.isEmpty(department) || StrUtil.isEmpty(version)) { return "新增进程失败。"; } WHostProcess wHostProcess = new WHostProcess(); wHostProcess.setP13(p13); wHostProcess.setProcessName(processName); wHostProcess.setProductNumber(productNumber); wHostProcess.setHost(executeHost); wHostProcess.setDepartment(department); wHostProcess.setState("离线"); wHostProcess.setVersion(version); wHostProcess.setBeginTime(new Date()); saveOrUpdateResult = wHostProcessService.save(wHostProcess); } else { // 执行更新操作 WHostProcess wHostProcess = wHostProcessService.getById(id); // 判断产品编号是否唯一 if (!wHostProcess.getProductNumber().equals(productNumber)) { LambdaQueryWrapper<WHostProcess> processWrapper = new LambdaQueryWrapper<>(); processWrapper.eq(WHostProcess::getProductNumber, productNumber); WHostProcess process = wHostProcessService.getOne(processWrapper); if (process != null) { return "该产品编号已被他人使用,请使用其他的产品编号。"; } } wHostProcess.setProcessName(processName); wHostProcess.setProductNumber(productNumber); wHostProcess.setHost(executeHost); wHostProcess.setDepartment(department); wHostProcess.setState("离线"); wHostProcess.setVersion(version); wHostProcess.setUpdateTime(new Date()); saveOrUpdateResult = wHostProcessService.updateById(wHostProcess); } if (saveOrUpdateResult) { LambdaQueryWrapper<WPersonalHost> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper .eq(WPersonalHost::getExecuteHost, processRequest.getExecuteHost()) .eq(WPersonalHost::getSharedHost, processRequest.getSharedHost()); WPersonalHost wPersonalHost = wPersonalHostService.getOne(queryWrapper); // 执行py启动命令 //todo 后续动态 String pythonEXEPath = "D:\\miniforge\\envs" + File.separator + p13 + File.separator + p13 + "_python" + wPersonalHost.getPythonEnv() + File.separator + "python.exe -u"; String mainPyPath = System.getProperty("user.dir") + File.separator + "python-package" + File.separator + executeHost + File.separator + p13 + File.separator + "test" + File.separator + "main.py"; this.executeCommand(pythonEXEPath + " " + mainPyPath, processRequest.getExecuteHost(), processRequest.getP13()); return "正在启动项目..."; } } catch (Exception e) { e.printStackTrace(); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } return "新增进程失败。"; } @Override public String stopProcess(ProcessRequest processRequest) { try { //todo 后续动态 String account = "fangpeiyuan"; Integer pid = processRequest.getPid(); LambdaQueryWrapper<WHostProcess> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper .eq(WHostProcess::getP13, account) .eq(WHostProcess::getPid, pid); WHostProcess wHostProcess = wHostProcessService.getOne(queryWrapper); if (wHostProcess == null) { return "当前进程终止失败,请联系管理员!"; } // 执行终止命令并获取返回值 Process process = Runtime.getRuntime().exec( "taskkill /F /PID \"" + pid + "\""); int exitCode = process.waitFor(); if (exitCode == 0) { wHostProcess.setState("离线"); wHostProcess.setPid(null); wHostProcess.setUpdateTime(DateUtil.date()); wHostProcessService.updateById(wHostProcess); return "进程终止成功。"; } else { // 获取错误流信息(可选) BufferedReader errorReader = new BufferedReader( new InputStreamReader(process.getErrorStream())); String errorLine; StringBuilder errorMessage = new StringBuilder(); while ((errorLine = errorReader.readLine()) != null) { errorMessage.append(errorLine).append("\n"); } return "进程终止失败,错误码: " + exitCode + (errorMessage.length() > 0 ? ",错误信息: " + errorMessage : ""); } } catch (Exception e) { e.printStackTrace(); TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); return "当前进程终止失败: " + e.getMessage(); } } private boolean validateUserHost(String userId, String executeHost) { LambdaQueryWrapper<WPersonalHost> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(WPersonalHost::getP13, userId) .eq(WPersonalHost::getExecuteHost, executeHost) .eq(WPersonalHost::getState, "在线"); return wPersonalHostService.getOne(queryWrapper) != null; } private UserSession createNewSession(String userId, String executeHost) { try { UserSession session = new UserSession(); session.setSessionId(UUID.randomUUID().toString()); session.setLogFilePath(initLogFile(userId, executeHost)); // 启动CMD进程(带唯一标题) ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/k", "title " + generateUniqueTitle(userId)); session.setCmdProcess(pb.redirectErrorStream(true).start()); // 启动输出监听线程 startOutputThread(session, userId); return session; } catch (IOException e) { sendError("进程启动失败: " + e.getMessage(), userId); return null; } } private String initLogFile(String userId, String executeHost) { // 1. 构建基础路径(使用File.separator) String baseDir = FileUtil.normalize( System.getProperty("user.dir") + File.separator + "command-log"); // 2. 构建安全路径(统一使用File.separator) String safePath = FileUtil.normalize( baseDir + File.separator + userId + File.separator + executeHost + File.separator + "项目名称"); // 3. 安全校验(现在路径分隔符一致) if (!safePath.startsWith(baseDir)) { throw new SecurityException("非法日志路径: " + safePath); } // 4. 创建目录(自动处理路径分隔符) FileUtil.mkdir(safePath); // 5. 生成日志文件 String logFileName = DateUtil.today() + ".log"; return FileUtil.touch(safePath + File.separator + logFileName).getAbsolutePath(); } private void startOutputThread(UserSession session, String userId) { new Thread(() -> { try (BufferedReader reader = new BufferedReader( new InputStreamReader(session.getCmdProcess().getInputStream(), Charset.forName("GBK")))) { String line; while ((line = reader.readLine()) != null) { messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, line); session.getLogBuffer().offer(line); } } catch (Exception e) { sendError("输出流异常: " + e.getMessage(), userId); } finally { session.isExecuting.set(false); // 命令结束释放锁 cleanupSession(userId); // 通知前端命令执行结束 messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, "⏹ 该命令执行已结束"); } }).start(); } private void asyncWriteLog(String logFilePath, String content) { CompletableFuture.runAsync(() -> { try { // 替换掉日志文件中没用的信息,如多余的路径信息 String currentDir = System.getProperty("user.dir"); String escapedDir = currentDir.replace("\\", "\\\\"); String regex = "(?m)^\\s*" + escapedDir + ">(?!\\S)\\s*"; // 创建 Pattern 和 Matcher 对象 Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(content); // 检查是否存在匹配的模式 if (matcher.find()) { // 如果存在匹配,则进行替换 String cleaned = content.replaceAll(regex, ""); FileUtil.appendString(cleaned + System.lineSeparator(), logFilePath, Charset.forName("GBK")); } else { FileUtil.appendString(content + System.lineSeparator(), logFilePath, Charset.forName("GBK")); } } catch (Exception e) { System.err.println("日志写入失败: " + e.getMessage()); } }, LOG_WRITER_POOL); } private void cleanupSession(String userId) { UserSession session = userSessions.remove(userId); if (session != null) { try { if (session.getCmdProcess() != null) { session.getCmdProcess().destroyForcibly(); } // 强制将剩余日志写入文件(新增代码) if (!session.getLogBuffer().isEmpty()) { asyncWriteLog(session.getLogFilePath(), String.join("\n", session.getLogBuffer())); session.getLogBuffer().clear(); } } catch (Exception ignored) { } } } @PreDestroy public void cleanup() { LOG_FLUSH_SCHEDULER.shutdown(); LOG_WRITER_POOL.shutdown(); userSessions.forEach((userId, session) -> { try { if (session.getCmdProcess() != null) { session.getCmdProcess().destroyForcibly(); } } catch (Exception ignored) { } }); userSessions.clear(); } /** * 发送错误日志 */ private void sendError(String message, String userId) { try { messagingTemplate.convertAndSend("/topic/commandOutput/" + userId, "❌" + message); } catch (Exception ignored) { } } /** * 生成cmd窗口唯一id */ private String generateUniqueTitle(String userId) { return "CMD_SESSION_" + userId + "_" + System.currentTimeMillis(); } } 请参考这两个,帮我实现b作为中转需要的代码,以及a和小客户端需要修改的东西,不要什么心跳检测等复杂的东西,先简单实现,但是代码要给全
06-22
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值