You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Bun --hot与Bun.listen时热重载遇EADDRINUSE错误的解决问询

解决Bun热重载与Bun.listen()的EADDRINUSE问题

核心问题

Bun的--hot热重载机制在启动新进程前,不会自动清理旧进程中Bun.listen()创建的服务器资源,导致端口被占用,触发EADDRINUSE错误。需要手动监听进程退出信号,实现服务器和连接的优雅关闭。

解决方案

实现思路

  1. 监听进程退出信号(Bun热重载时会向旧进程发送SIGTERM信号)
  2. 跟踪所有活跃的socket连接,确保关闭服务器时能逐一清理
  3. 优雅关闭文件写入器,避免数据丢失

修改后的完整代码

import { mkdir } from "node:fs/promises";
import type { FileSink, Socket } from "bun";

const PORT = 9100;
const RECEIPTS_DIR = "receipts";

type SocketData = {
  writer: FileSink;
  bytesReceived: number;
};

// 存储所有活跃连接,用于重载时批量清理
const activeSockets = new Set<Socket<SocketData>>();

// 确保目录存在
await mkdir(RECEIPTS_DIR, { recursive: true });

const server = Bun.listen<SocketData>({
  hostname: "0.0.0.0",
  port: PORT,

  socket: {
    open(socket) {
      console.log("Client connected:", socket.remoteAddress, socket.remotePort);
      activeSockets.add(socket);

      const timestamp = Date.now();
      const outputFile = `${RECEIPTS_DIR}/receipt-commands-${timestamp}.bin`;

      socket.data = {
        writer: Bun.file(outputFile).writer(),
        bytesReceived: 0,
      };
    },

    data(socket, data) {
      console.log("Data received:", data);
      const bytes = new Uint8Array(data);
      socket.data.bytesReceived += bytes.length;

      try {
        socket.data.writer.write(bytes);
      } catch (err) {
        console.error("Write error:", err);
      }
    },

    close(socket) {
      console.log("Client disconnected");
      activeSockets.delete(socket);

      if (socket.data.bytesReceived === 0) {
        console.log("No data received, skipping file write");
        socket.data.writer.end();
        return;
      }

      socket.data.writer.end();
      console.log(`Saved ${socket.data.bytesReceived} bytes`);
    },

    error(socket, err) {
      console.error("Socket error:", socket.remoteAddress, socket.remotePort);
      console.error(err);
      activeSockets.delete(socket);
    },
  },
});

// 处理热重载的进程退出信号
process.on("SIGTERM", async () => {
  console.log("\nReceived shutdown signal, cleaning up...");

  // 停止接收新连接
  server.stop();

  // 逐一关闭活跃连接并清理文件写入器
  for (const socket of activeSockets) {
    console.log("Closing connection:", socket.remoteAddress, socket.remotePort);
    socket.data.writer.end();
    socket.end();
  }

  // 等待资源清理完成后退出
  await new Promise(resolve => setTimeout(resolve, 500));
  process.exit(0);
});

console.log(`Bun ESC/POS test printer listening on TCP port ${PORT}`);

关键细节说明

  • activeSockets集合:实时跟踪所有当前连接的socket,确保重载时不会遗漏任何活跃连接
  • SIGTERM信号处理:捕获Bun热重载发送的终止信号,先停止服务器接收新请求,再关闭现有连接,最后等待资源清理完成
  • 资源清理顺序:先停止服务器,再关闭连接,最后结束文件写入器,保证数据完整性和端口释放

验证步骤

  1. 执行bun --hot index.ts启动服务器
  2. 修改任意代码触发热重载,旧进程会自动优雅关闭
  3. 新进程将正常监听9100端口,无EADDRINUSE错误

内容的提问来源于stack exchange,提问作者sultanmyrza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 17:42:44