yeg
2026-09-29 b78295e8da96c3e5e76befeef9582ce5273dbb74
battery-device-simulator/src/main/java/com/battery/device/simulator/ScriptedBtsTcpSimulator.java
@@ -13,9 +13,11 @@
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
/**
@@ -27,7 +29,9 @@
    private final ServerSocket serverSocket;
    private final int port;
    private final ExecutorService executor = Executors.newSingleThreadExecutor();
    /** 连接服务线程显式创建并命名。该池只提交一个常驻服务任务,队列容量 1 只是声明不再接受更多任务。 */
    private final ExecutorService executor = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<>(1), simulatorThreadFactory());
    private final BlockingQueue<ScriptedAction> actions = new LinkedBlockingQueue<>();
    private final List<String> requests = new CopyOnWriteArrayList<>();
    private final Function<String, String> responseProvider;
@@ -51,6 +55,7 @@
            serverSocket.close();
    }
    /** 创建一启动就关闭监听、任何连接都会被拒绝的模拟器。 */
    public static ScriptedBtsTcpSimulator refusingConnections() throws IOException
    {
        return new ScriptedBtsTcpSimulator(0, false, null);
@@ -64,32 +69,38 @@
        return new ScriptedBtsTcpSimulator(port, true, responseProvider);
    }
    /** 返回该模拟器绑定的本机端口;构造时传入 0 时返回的是系统分配端口而非 0。 */
    public int port()
    {
        return port;
    }
    /** 向脚本队列追加一条零延迟应答,按 UTF-8 编码,由下一次请求按顺序取用。 */
    public void enqueue(String response)
    {
        actions.add(new ScriptedAction(Duration.ZERO, response.getBytes(StandardCharsets.UTF_8), false));
    }
    /** 追加一条延迟应答:连接服务线程收到请求后先等待 delay 再写出;delay 不得为负。 */
    public void enqueueDelayed(Duration delay, String response)
    {
        if (delay.isNegative()) throw new IllegalArgumentException("delay must not be negative");
        actions.add(new ScriptedAction(delay, response.getBytes(StandardCharsets.UTF_8), false));
    }
    /** 追加一条零延迟的原始字节应答:字节原样写出后追加帧结束符,不做字符编码转换。 */
    public void enqueueRaw(byte[] response)
    {
        actions.add(new ScriptedAction(Duration.ZERO, response.clone(), false));
    }
    /** 追加一条断开动作:处理到该条时结束当前连接,且不写出任何应答。 */
    public void enqueueDisconnect()
    {
        actions.add(new ScriptedAction(Duration.ZERO, null, true));
    }
    /** 返回已接收请求帧的只读快照;只有脚本模式(未配置 responseProvider)才记录。 */
    public List<String> requests()
    {
        return List.copyOf(requests);
@@ -166,6 +177,7 @@
        }
    }
    /** 关闭监听并终止连接服务线程,最多等待 2 秒,避免测试退出时残留线程。 */
    @Override
    public void close() throws Exception
    {
@@ -174,6 +186,17 @@
        executor.awaitTermination(2, TimeUnit.SECONDS);
    }
    /** 模拟器连接服务线程统一命名并设为守护线程,避免测试退出时残留。 */
    private static ThreadFactory simulatorThreadFactory()
    {
        AtomicInteger sequence = new AtomicInteger();
        return runnable -> {
            Thread thread = new Thread(runnable, "bts-simulator-pool-" + sequence.incrementAndGet());
            thread.setDaemon(true);
            return thread;
        };
    }
    private record ScriptedAction(Duration delay, byte[] payload, boolean disconnect)
    {
    }