使用Bun --hot与Bun.listen时热重载遇EADDRINUSE错误的解决问询
解决Bun热重载与Bun.listen()的EADDRINUSE问题
核心问题
Bun的--hot热重载机制在启动新进程前,不会自动清理旧进程中Bun.listen()创建的服务器资源,导致端口被占用,触发EADDRINUSE错误。需要手动监听进程退出信号,实现服务器和连接的优雅关闭。
解决方案
实现思路
- 监听进程退出信号(Bun热重载时会向旧进程发送
SIGTERM信号) - 跟踪所有活跃的socket连接,确保关闭服务器时能逐一清理
- 优雅关闭文件写入器,避免数据丢失
修改后的完整代码
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热重载发送的终止信号,先停止服务器接收新请求,再关闭现有连接,最后等待资源清理完成- 资源清理顺序:先停止服务器,再关闭连接,最后结束文件写入器,保证数据完整性和端口释放
验证步骤
- 执行
bun --hot index.ts启动服务器 - 修改任意代码触发热重载,旧进程会自动优雅关闭
- 新进程将正常监听9100端口,无
EADDRINUSE错误
内容的提问来源于stack exchange,提问作者sultanmyrza
相关产品推荐
相关产品推荐

