Переглянути джерело

feat(lamp-service): 重构路灯控制接口与服务端通信逻辑

- 引入Swagger注解增强接口文档
- 抽取常量避免魔法值,提高可维护性- 复用继电器控制方法减少重复代码
- 增加定时任务查询接口- 统一异常处理与日志记录
-优化服务端连接与重连机制,增加超时控制- 增加频率限制与锁机制防止频繁操作- 完善时间格式校验与错误提示

fix(infrared-reading-meter-service): 优化数据解析与日志记录

- 数据长度不足时抛出异常而非静默返回
- 解析电能数据含无效值时记录详细错误并抛出异常- 调整日志输出内容,记录原始数据而非字节长度
- 明确区分心跳数据与无效消息
- 增强协议匹配日志信息,标注具体协议年份
林仔 6 місяців тому
батько
коміт
068585457e

+ 5 - 2
infrared-reading-meter-service/src/main/java/com/zksy/infrared/utils/DataParser.java

@@ -28,8 +28,9 @@ public class DataParser {
 
             // 检查数据部分是否足够
             if (dataParts.size() < 1 + 1 + 1 + 4) { // address(1) + function(1) + 跳过(1) + energy(4)
+                String errorMsg = "数据长度不足,无法解析: " + msgString;
                 logger.error("数据长度不足,无法解析: {}", msgString);
-                return result;
+                throw new IllegalArgumentException(errorMsg);
             }
 
             String res1 = getStringList(dataParts, 1);
@@ -45,7 +46,9 @@ public class DataParser {
             // 检查是否有null值
             if (res3.contains("null")) {
                 logger.error("解析得到的电能数据包含无效值: {}", res3);
-                return result;
+                String errorMsg = "解析得到的电能数据包含无效值: " + res3;
+                logger.error(errorMsg);
+                throw new IllegalArgumentException(errorMsg);
             }
 
             BigInteger energyValue = new BigInteger(res3, 16);

+ 5 - 5
infrared-reading-meter-service/src/main/java/com/zksy/infrared/utils/MessageHandler.java

@@ -66,13 +66,13 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
 				return;
 			}
 
-			logger.info("接收到 {} 字节的数据,来自: {}", msgByteBuf.readableBytes(), ctx.channel().remoteAddress());
 			byte[] msgBytes = new byte[msgByteBuf.readableBytes()];
 			msgByteBuf.readBytes(msgBytes);
 			String result = printHexBinary(msgBytes);
+			logger.info("接收到 {} 字节的数据,来自: {}", result, ctx.channel().remoteAddress());
 			String msgString = result.replaceAll("\\s", "");
 			if("7777772E7573722E636E".equals(msgString)){
-				logger.warn("接收到无效的消息");
+				logger.warn("接收到心跳数据");
 				return;
 			}
 			// 数据校验
@@ -117,17 +117,17 @@ public class MessageHandler extends ChannelInboundHandlerAdapter {
 			case "0000":
 				sendDataToDevice(ctx, "01 03 03 E8 00 02 44 7B");
 				stateAttr.set(CommunicationState.WAITING_FOR_DATA);
-				logger.info("协议匹配0000,已发送指令并更新通信状态");
+				logger.info("协议匹配0000(97年协议),已发送指令并更新通信状态");
 				break;
 			case "0001":
 				sendDataToDevice(ctx, "01 03 07 D0 00 02 C4 86");
 				stateAttr.set(CommunicationState.WAITING_FOR_DATA);
-				logger.info("协议匹配0001,已发送指令并更新通信状态");
+				logger.info("协议匹配0001(2007协议),已发送指令并更新通信状态");
 				break;
 			case "0002":
 				sendDataToDevice(ctx, "01 03 0B B8 00 02 46 0A");
 				stateAttr.set(CommunicationState.WAITING_FOR_DATA);
-				logger.info("协议匹配0002,已发送指令并更新通信状态");
+				logger.info("协议匹配0002(2017年协议 ),已发送指令并更新通信状态");
 				break;
 			default:
 				throw new InvalidMessageException("数据协议不匹配,未知协议类型: " + agreement);

+ 188 - 65
lamp-service/src/main/java/com/zksy/lamp/controller/ExecutionController.java

@@ -1,9 +1,12 @@
 package com.zksy.lamp.controller;
 
 import com.zksy.common.core.domain.Result;
+import com.zksy.common.exception.CommonException;
 import com.zksy.lamp.server.ExecutionServer;
+import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import io.swagger.annotations.ApiParam;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -15,120 +18,240 @@ import java.time.Instant;
 import java.time.LocalTime;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
-import java.util.Date;
-import java.util.Objects;
 import java.util.concurrent.TimeUnit;
 
 /**
  * @author Administrator
  * @version 1.0
  * @project dh-server-micro
- * @description 路灯服务
+ * @description 路灯服务控制器
  * @date 2025/2/10 15:00:46
  */
 @RequestMapping("/execution")
 @RestController
+@Api(tags = "路灯控制接口")
+@Slf4j // 引入日志注解,替代手动创建Logger
 public class ExecutionController {
+
     @Autowired
     private ExecutionServer server;
+
     @Autowired
     private RedisTemplate<String, String> redisTemplate;
 
+    // 常量抽取:避免魔法值,提高可维护性
+    private static final String LOCK_ON_KEY = "lock:on";
+    private static final String LOCK_OFF_KEY = "lock:off";
+    private static final long LOCK_DURATION_MS = 3600000; // 1小时(毫秒)
+    private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("HH:mm:ss");
+
+
     @GetMapping("/firstRelay")
-    @ApiOperation(value = "第一路继电器闭合、断开", notes = "第一路继电器闭合、断开")
-    public Result<?> firstRelay(@ApiParam(value = "第一路继电器(0-断开,1-闭合)", required = true)
-                                @RequestParam(value = "value") Integer value) {
-        String data = "AT+STACH1=" + value + "\r\n";
-        String msg = server.ExecutionData(data);
-        return Result.ok(msg);
+    @ApiOperation(value = "第一路继电器控制", notes = "控制第一路继电器闭合(1)或断开(0)")
+    public Result<?> firstRelay(
+            @ApiParam(value = "控制值(0-断开,1-闭合)", required = true, allowableValues = "0,1")
+            @RequestParam Integer value) {
+        // 复用通用继电器控制方法,减少重复代码
+        return controlRelay(1, value);
     }
 
+
     @GetMapping("/secondRelay")
-    @ApiOperation(value = "第二路继电器闭合、断开", notes = "第二路继电器闭合、断开")
-    public Result<?> secondRelay(@ApiParam(value = "第二路继电器(0-断开,1-闭合)", required = true)
-                                 @RequestParam(value = "value") Integer value) {
-        String data = "AT+STACH2=" + value + "\r\n";
-        String msg = server.ExecutionData(data);
-        return Result.ok(msg);
+    @ApiOperation(value = "第二路继电器控制", notes = "控制第二路继电器闭合(1)或断开(0)")
+    public Result<?> secondRelay(
+            @ApiParam(value = "控制值(0-断开,1-闭合)", required = true, allowableValues = "0,1")
+            @RequestParam Integer value) {
+        // 复用通用继电器控制方法
+        return controlRelay(2, value);
     }
 
+
     @GetMapping("/timingOn")
-    @ApiOperation(value = "定时开启", notes = "定时开启")
-    public Result<?> timingOn(@ApiParam(value = "定时开启时间,时:分:秒,示例:17:00:00", required = true)
-                              @RequestParam(value = "value") String value) {
+    @ApiOperation(value = "定时开启", notes = "设置定时开启时间(格式:HH:mm:ss,每天执行)")
+    public Result<?> timingOn(
+            @ApiParam(value = "定时开启时间(示例:17:00:00)", required = true)
+            @RequestParam String value) {
+        // 校验时间格式
         if (!isValidTime(value)) {
             return Result.error(400, "无效的时间格式,请使用 HH:mm:ss 格式");
         }
 
-        if (Boolean.TRUE.equals(redisTemplate.hasKey("lock:on"))) {
-            Instant now = Instant.now();
-            long currentTime = now.toEpochMilli();
-            String lockTimeStr = redisTemplate.opsForValue().get("lock:on");
-            if (lockTimeStr != null) {
-                try {
-                    long lockTime = Long.parseLong(lockTimeStr);
-                    long remainingTime = (lockTime - currentTime) / 1000;
-                    return Result.error(429, "一小时只能发布一次还剩" + remainingTime + "秒");
-                } catch (NumberFormatException e) {
-                    return Result.error(500, "内部服务器错误");
-                }
-            }
+        // 校验频率限制(复用通用方法)
+        Result<?> rateLimitResult = checkRateLimit(LOCK_ON_KEY);
+        if (rateLimitResult != null) {
+            return rateLimitResult;
         }
 
-        long t = Instant.now().toEpochMilli();
-        long lockTime = 3600000; // 3600000 毫秒 = 1 小时
-        String data = "AT+AUTOCONT=8,task1,[CYC:1],[T:3,0|1|2|3|4|5|6," + value + "],[DO:0,1,1,100000,100000,1000000],[N:1,0]\r\n";
-        String msg = server.ExecutionData(data);
-        redisTemplate.opsForValue().set("lock:on", String.valueOf(t + lockTime), lockTime, TimeUnit.MILLISECONDS);
-        return Result.ok(msg);
+        // 构建指令并执行
+        String data = buildTimingCommand("task1", value, 1); // 1表示开启
+        return executeWithRateLimit(data, LOCK_ON_KEY);
     }
 
+
     @GetMapping("/timedShutdown")
-    @ApiOperation(value = "定时关闭", notes = "定时关闭")
-    public Result<?> timedShutdown(@ApiParam(value = "定时关闭时间,时:分:秒,示例:06:00:00", required = true)
-                                   @RequestParam(value = "value") String value) {
+    @ApiOperation(value = "定时关闭", notes = "设置定时关闭时间(格式:HH:mm:ss,每天执行)")
+    public Result<?> timedShutdown(
+            @ApiParam(value = "定时关闭时间(示例:06:00:00)", required = true)
+            @RequestParam String value) {
+        // 校验时间格式
         if (!isValidTime(value)) {
             return Result.error(400, "无效的时间格式,请使用 HH:mm:ss 格式");
         }
 
-        if (Boolean.TRUE.equals(redisTemplate.hasKey("lock:off"))) {
-            Instant now = Instant.now();
-            long currentTime = now.toEpochMilli();
-            String lockTimeStr = redisTemplate.opsForValue().get("lock:off");
-            if (lockTimeStr != null) {
-                try {
-                    long lockTime = Long.parseLong(lockTimeStr);
-                    long remainingTime = (lockTime - currentTime) / 1000;
-                    return Result.error(429, "一小时只能发布一次还剩" + remainingTime + "秒");
-                } catch (NumberFormatException e) {
-                    return Result.error(500, "内部服务器错误");
+        // 校验频率限制
+        Result<?> rateLimitResult = checkRateLimit(LOCK_OFF_KEY);
+        if (rateLimitResult != null) {
+            return rateLimitResult;
+        }
+
+        // 构建指令并执行
+        String data = buildTimingCommand("task2", value, 0); // 0表示关闭
+        return executeWithRateLimit(data, LOCK_OFF_KEY);
+    }
+
+
+    @GetMapping("/queryScheduledTasks")
+    @ApiOperation(value = "查询定时任务", notes = "查询所有已设置的定时任务")
+    public Result<?> queryScheduledTasks() {
+        try {
+            String data = "AT+AUTOCONT=0\r\n";
+            log.info("查询定时任务,发送指令:{}", data);
+            String msg = server.executeData(data);
+            log.info("查询定时任务成功,响应:{}", msg);
+            return Result.ok(msg);
+        } catch (Exception e) {
+            return handleException("查询定时任务", e);
+        }
+    }
+
+
+    // ------------------------------ 私有工具方法(复用逻辑) ------------------------------
+
+    /**
+     * 通用继电器控制方法
+     *
+     * @param relayNum 继电器编号(1/2)
+     * @param value    控制值(0/1)
+     */
+    private Result<?> controlRelay(int relayNum, Integer value) {
+        // 参数校验
+        if (value == null || (value != 0 && value != 1)) {
+            return Result.error(400, "参数错误,仅支持 0(断开)或 1(闭合)");
+        }
+
+        try {
+            String data = String.format("AT+STACH%d=%d\r\n", relayNum, value);
+            log.info("控制第{}路继电器,发送指令:{}", relayNum, data);
+            String msg = server.executeData(data);
+            log.info("控制第{}路继电器成功,响应:{}", relayNum, msg);
+            return Result.ok(msg);
+        } catch (Exception e) {
+            return handleException("控制第" + relayNum + "路继电器", e);
+        }
+    }
+
+
+    /**
+     * 检查频率限制(1小时内只能执行一次)
+     *
+     * @param lockKey redis锁键名
+     * @return 若触发限制则返回错误Result,否则返回null
+     */
+    private Result<?> checkRateLimit(String lockKey) {
+        try {
+            if (Boolean.TRUE.equals(redisTemplate.hasKey(lockKey))) {
+                long currentTime = Instant.now().toEpochMilli();
+                String lockTimeStr = redisTemplate.opsForValue().get(lockKey);
+                // 防御性判空,避免空指针
+                if (lockTimeStr == null) {
+                    log.warn("redis锁键{}存在,但值为null,清除无效锁", lockKey);
+                    redisTemplate.delete(lockKey);
+                    return null;
+                }
+
+                long lockTime = Long.parseLong(lockTimeStr);
+                long remainingTime = (lockTime - currentTime) / 1000;
+                if (remainingTime > 0) {
+                    return Result.error(429, "一小时内只能操作一次,还剩" + remainingTime + "秒");
+                } else {
+                    // 锁已过期,清除旧锁
+                    redisTemplate.delete(lockKey);
+                    return null;
                 }
             }
+            return null;
+        } catch (NumberFormatException e) {
+            log.error("解析redis锁时间失败,key:{}", lockKey, e);
+            return Result.error(500, "系统异常:时间解析失败");
         }
+    }
 
-        long t = Instant.now().toEpochMilli();
-        long lockTime = 3600000; // 3600000 毫秒 = 1 小时
-        String data = "AT+AUTOCONT=8,task2,[CYC:1],[T:3,0|1|2|3|4|5|6," + value + "],[DO:0,1,0,100000,100000,1000000],[N:1,0]\r\n";
-        String msg = server.ExecutionData(data);
-        redisTemplate.opsForValue().set("lock:off", String.valueOf(t + lockTime), lockTime, TimeUnit.MILLISECONDS);
-        return Result.ok(msg);
+
+    /**
+     * 执行指令并设置频率限制锁
+     *
+     * @param data    要发送的指令
+     * @param lockKey redis锁键名
+     */
+    private Result<?> executeWithRateLimit(String data, String lockKey) {
+        try {
+            log.info("发送定时指令:{}", data);
+            String msg = server.executeData(data);
+            // 设置锁(当前时间+1小时)
+            long expireTime = Instant.now().toEpochMilli() + LOCK_DURATION_MS;
+            redisTemplate.opsForValue().set(lockKey, String.valueOf(expireTime), LOCK_DURATION_MS, TimeUnit.MILLISECONDS);
+            log.info("定时指令执行成功,响应:{},已设置锁{}", msg, lockKey);
+            return Result.ok(msg);
+        } catch (Exception e) {
+            return handleException("执行定时指令", e);
+        }
+    }
+
+
+    /**
+     * 构建定时任务指令
+     *
+     * @param taskName 任务名(task1/task2)
+     * @param time     定时时间(HH:mm:ss)
+     * @param action   动作(1-开启,0-关闭)
+     */
+    private String buildTimingCommand(String taskName, String time, int action) {
+        return String.format(
+                "AT+AUTOCONT=8,%s,[CYC:1],[T:3,0|1|2|3|4|5|6,%s],[DO:0,1,%d,100000,100000,1000000],[N:1,0]\r\n",
+                taskName, time, action
+        );
     }
 
+
+    /**
+     * 校验时间格式是否为HH:mm:ss
+     */
     private boolean isValidTime(String time) {
         try {
-            LocalTime.parse(time, DateTimeFormatter.ofPattern("HH:mm:ss"));
+            LocalTime.parse(time, TIME_FORMATTER);
             return true;
         } catch (DateTimeParseException e) {
+            log.warn("无效的时间格式:{},正确格式应为HH:mm:ss", time);
             return false;
         }
     }
 
 
-    @GetMapping("/queryScheduledTasks")
-    @ApiOperation(value = "查询定时", notes = "查询定时")
-    public Result<?> queryScheduledTasks() {
-        String data = "AT+AUTOCONT=0\r\n";
-        String msg = server.ExecutionData(data);
-        return Result.ok(msg);
+    /**
+     * 统一异常处理方法
+     *
+     * @param operation 操作描述(用于日志)
+     * @param e         异常对象
+     */
+    private Result<?> handleException(String operation, Exception e) {
+        log.error("{}失败", operation, e); // 打印完整堆栈,便于排查
+        if (e instanceof CommonException) {
+            CommonException ce = (CommonException) e;
+            return Result.error(ce.getCode(), ce.getMessage());
+        } else if (e instanceof RuntimeException) {
+            return Result.error(500, e.getMessage());
+        } else {
+            return Result.error(500, "系统异常:" + e.getMessage() + ",请联系管理员");
+        }
     }
-}
+}

+ 185 - 64
lamp-service/src/main/java/com/zksy/lamp/server/ExecutionServer.java

@@ -3,7 +3,6 @@ package com.zksy.lamp.server;
 import com.zksy.common.exception.CommonException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
 import javax.annotation.PostConstruct;
@@ -11,101 +10,223 @@ import javax.annotation.PreDestroy;
 import java.io.*;
 import java.net.ServerSocket;
 import java.net.Socket;
+import java.net.SocketTimeoutException;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicBoolean;
 
-/**
- * @author Administrator
- * @version 1.0
- * @project dh-server-micro
- * @description 指令执行
- * @date 2025/2/10 16:25:14
- */
 @Component
 public class ExecutionServer {
     private static final Logger log = LoggerFactory.getLogger(ExecutionServer.class);
+    private static final int SERVER_PORT = 15678;
+    private static final int CONNECT_TIMEOUT = 5000; // 连接超时(5秒)- 已启用
+    private static final int READ_TIMEOUT = 3000; // 读取响应超时(3秒)
+    private static final int RECONNECT_MAX_TIMES = 3; // 最大重连次数
+
     private BufferedReader reader;
     private BufferedWriter writer;
-    private ServerSocket server;
-    private Socket client;
-    private int serverPort = 15678;
+    private ServerSocket serverSocket;
+    private Socket clientSocket;
+    private ExecutorService executorService;
+    private final AtomicBoolean isRunning = new AtomicBoolean(false);
 
     @PostConstruct
-    public void init(){
-        Runnable func0 = this::startServer;
-        Thread thread = new Thread(func0);
-        thread.start();
+    public void init() {
+        executorService = Executors.newSingleThreadExecutor();
+        isRunning.set(true);
+        executorService.submit(this::startServer);
+        log.info("ExecutionServer 初始化完成,监听端口:{}", SERVER_PORT);
     }
 
     @PreDestroy
-    public void closeResource(){
-        try {
-            if (reader != null) reader.close();
-            if (writer != null) writer.close();
-            if (client != null) client.close();
-            if (server != null) server.close();
-        } catch (IOException e) {
-            e.printStackTrace();
+    public void closeResource() {
+        isRunning.set(false);
+        if (executorService != null) {
+            executorService.shutdownNow();
         }
+        closeAllResource();
+        log.info("ExecutionServer 资源已全部释放");
     }
 
     /**
-     * 启动服务器并等待客户端连接
+     * 启动服务端,设置连接超时,避免阻塞
      */
     public void startServer() {
         try {
-            System.out.println("本机作为服务端");
-            System.out.println("本机端口:" + serverPort);
-            server = new ServerSocket(serverPort);
-
-            System.out.println("等待客户机的连接");
-            client = server.accept();
-            System.out.println("连接成功");
+            serverSocket = new ServerSocket(SERVER_PORT);
+            // 关键:设置ServerSocket的连接超时(CONNECT_TIMEOUT生效)
+            serverSocket.setSoTimeout(CONNECT_TIMEOUT);
+            log.info("服务端启动成功,监听端口:{},连接超时:{}ms", SERVER_PORT, CONNECT_TIMEOUT);
 
-            reader = new BufferedReader(new InputStreamReader(client.getInputStream(), "GB2312"));
-            writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream(), "GB2312"));
-            System.out.println("测试完成!");
+            while (isRunning.get()) {
+                try {
+                    log.info("等待客户端连接(超时{}ms)...", CONNECT_TIMEOUT);
+                    clientSocket = serverSocket.accept(); // 超过5秒未连接会抛出SocketTimeoutException
+                    log.info("客户端连接成功,地址:{}:{}",
+                            clientSocket.getInetAddress(), clientSocket.getPort());
 
+                    clientSocket.setSoTimeout(READ_TIMEOUT);
+                    initIoStream();
+                } catch (SocketTimeoutException e) {
+                    // 连接超时,不报错,继续循环监听
+                    log.info("客户端连接超时({}ms),继续等待...", CONNECT_TIMEOUT);
+                } catch (IOException e) {
+                    if (isRunning.get()) {
+                        log.error("客户端连接失败,将继续监听", e);
+                        closeClientResource();
+                    } else {
+                        log.info("服务端正在关闭,停止监听");
+                    }
+                }
+            }
         } catch (IOException e) {
-            e.printStackTrace();
+            log.error("服务端启动失败,端口:{}", SERVER_PORT, e);
         }
     }
-    public String ExecutionData(String msg) {
+
+    /**
+     * 执行指令,重连失败返回明确提示
+     */
+    public String executeData(String msg) {
+        if (!isRunning.get()) {
+            throw new RuntimeException("服务端未运行");
+        }
+
+        // 校验连接,失败则重连
+        if (!checkClientAndIo()) {
+            log.warn("客户端未连接或IO流异常,启动重连(最多{}次)", RECONNECT_MAX_TIMES);
+            boolean reconnectSuccess = reconnect(RECONNECT_MAX_TIMES);
+            if (!reconnectSuccess) {
+                // 超过3次重连失败,返回明确提示
+                throw new RuntimeException("客户端连接失败,已重试" + RECONNECT_MAX_TIMES + "次,请检查客户端是否在线");
+            }
+        }
+
         try {
-            String returnMsgReplace = "";
-            System.out.println("第1路闭合,发送指令:"+msg);
-            //发送数据
+            log.info("发送指令:{}", msg);
             writer.write(msg);
             writer.flush();
-            //读数据
+
             String returnMsg = reader.readLine();
-            System.out.println("returnMsg================:"+returnMsg);
-            if (returnMsg != null) {
-                returnMsgReplace = returnMsg.replace("\r\n", "\\r\\n");
-                System.out.println("设备应答:" + returnMsgReplace);
-            } else {
-                System.out.println("接收到的消息为 null,重新建立连接...");
-                // 关闭当前连接资源
-                closeResource();
-                // 重新启动服务器并等待客户端连接
-                startServer();
-                // 重新发送指令
-                writer.write(msg);
-                writer.flush();
-                String returnMsg2 = reader.readLine();
-                returnMsgReplace = returnMsg2.replace("\r\n", "\\r\\n");
-                System.out.println("重新连接后接收到的消息:" + returnMsgReplace);
+            log.info("客户端响应:{}", returnMsg);
+
+            if (returnMsg == null) {
+                throw new RuntimeException("未收到客户端响应");
             }
-            if("AT".equals(returnMsgReplace)) {
-                String s = "AT+ACK\r\n";
-                writer.write(s);
+
+            String returnMsgReplace = returnMsg.replace("\r\n", "\\r\\n");
+
+            if ("AT".equals(returnMsgReplace.trim())) {
+                writer.write("AT+ACK\r\n");
                 writer.flush();
-                throw new CommonException("当次设置失败,请再次设置",456);
+                throw new CommonException("当次设置失败,请再次设置", 456);
             }
-            System.out.println("指令执行成功!");
+
+            log.info("指令执行成功,响应:{}", returnMsgReplace);
             return returnMsgReplace;
+        } catch (SocketTimeoutException e) {
+            log.error("读取响应超时({}ms)", READ_TIMEOUT, e);
+            closeClientResource();
+            throw new RuntimeException("读取响应超时,请重试");
+        } catch (CommonException e) {
+            throw e;
         } catch (Exception e) {
-            log.info(e.toString());
-            throw new RuntimeException("指令执行失败");
+            log.error("指令执行异常", e);
+            closeClientResource();
+            throw new RuntimeException("指令执行失败:" + e.getMessage());
+        }
+    }
+
+    /**
+     * 重连逻辑(带连接超时,最多maxTimes次)
+     */
+    private boolean reconnect(int maxTimes) {
+        for (int i = 1; i <= maxTimes; i++) {
+            try {
+                log.info("第{}次重连(超时{}ms)...", i, CONNECT_TIMEOUT);
+                closeClientResource();
+
+                // 重连时也启用连接超时
+                serverSocket.setSoTimeout(CONNECT_TIMEOUT);
+                clientSocket = serverSocket.accept(); // 超时会抛出异常,进入下一次重试
+                clientSocket.setSoTimeout(READ_TIMEOUT);
+                initIoStream();
+
+                log.info("第{}次重连成功", i);
+                return true;
+            } catch (SocketTimeoutException e) {
+                log.warn("第{}次重连超时({}ms)", i, CONNECT_TIMEOUT);
+            } catch (Exception e) {
+                log.error("第{}次重连失败", i, e);
+            }
+            // 重试间隔1秒
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException ie) {
+                Thread.currentThread().interrupt();
+            }
+        }
+        log.error("超过{}次重连,全部失败", maxTimes);
+        return false;
+    }
+
+    // 以下方法保持不变
+    private boolean checkClientAndIo() {
+        return clientSocket != null
+                && !clientSocket.isClosed()
+                && reader != null
+                && writer != null;
+    }
+
+    private void initIoStream() throws IOException {
+        closeIoStream();
+        reader = new BufferedReader(new InputStreamReader(
+                clientSocket.getInputStream(), StandardCharsets.UTF_8));
+        writer = new BufferedWriter(new OutputStreamWriter(
+                clientSocket.getOutputStream(), StandardCharsets.UTF_8));
+        log.info("IO流初始化成功");
+    }
+
+    private void closeClientResource() {
+        closeIoStream();
+        try {
+            if (clientSocket != null && !clientSocket.isClosed()) {
+                clientSocket.close();
+            }
+        } catch (IOException e) {
+            log.error("关闭客户端Socket失败", e);
+        } finally {
+            clientSocket = null;
         }
     }
-}
 
+    private void closeIoStream() {
+        try {
+            if (reader != null) {
+                reader.close();
+            }
+            if (writer != null) {
+                writer.close();
+            }
+        } catch (IOException e) {
+            log.error("关闭IO流失败", e);
+        } finally {
+            reader = null;
+            writer = null;
+        }
+    }
+
+    private void closeAllResource() {
+        closeClientResource();
+        try {
+            if (serverSocket != null && !serverSocket.isClosed()) {
+                serverSocket.close();
+            }
+        } catch (IOException e) {
+            log.error("关闭服务端Socket失败", e);
+        } finally {
+            serverSocket = null;
+        }
+    }
+}