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

如何将Node.js Express服务器的stdout/stderr通过Socket.io发送至网页?

实现服务器stdout/stderr通过Socket.io转发至网页

服务端修改方案

直接监听process.stdout/process.stderr的data事件可能因为流的缓冲模式(尤其是TTY环境下的行缓冲)失效,更可靠的方式是重写输出流的write方法,确保所有输出都能被捕获并转发:

const express = require("express");
const app = express();
const port = 3000;

const server = app.listen(port, () =>
    console.log(`app listening on port ${port}!`)
);

const io = require("socket.io")(server);

// 保留原始输出方法,避免丢失控制台日志
const originalStdoutWrite = process.stdout.write;
const originalStderrWrite = process.stderr.write;

// 重写stdout输出逻辑
process.stdout.write = function(chunk, encoding, callback) {
    // 将输出转为字符串并通过Socket.io发送给所有客户端
    io.emit("console-output", {
        type: "stdout",
        data: chunk.toString(encoding || "utf8")
    });
    // 执行原始输出逻辑
    return originalStdoutWrite.apply(process.stdout, arguments);
};

// 重写stderr输出逻辑
process.stderr.write = function(chunk, encoding, callback) {
    io.emit("console-output", {
        type: "stderr",
        data: chunk.toString(encoding || "utf8")
    });
    return originalStderrWrite.apply(process.stderr, arguments);
};

// 处理客户端连接与命令接收
io.on("connection", (socket) => {
    console.log("客户端已连接");

    // 监听客户端发送的命令
    socket.on("send-cmd", (cmd) => {
        input(cmd);
    });

    socket.on("disconnect", () => {
        console.log("客户端已断开连接");
    });
});

// 原有的命令处理函数
function input(cmd) {
    if (cmd != 'exit') {
        try {
            eval(cmd);
        } catch (e) {
            console.log(e);
        }
    } else {
        process.exit(0)
    }
}

// 托管静态页面(客户端页面放在public目录)
app.use(express.static("public"));

客户端实现(示例HTML)

创建public/index.html,实现命令输入和日志展示:

<!DOCTYPE html>
<html>
<head>
    <title>Server Console</title>
    <style>
        #console {
            height: 300px;
            overflow-y: auto;
            border: 1px solid #ccc;
            padding: 10px;
            margin-bottom: 10px;
        }
        .stderr {
            color: #dc3545;
        }
    </style>
</head>
<body>
    <div id="console"></div>
    <div>
        <input type="text" id="cmdInput" placeholder="输入命令执行..." style="width: 300px;">
        <button onclick="sendCommand()">执行</button>
    </div>

    <script src="/socket.io/socket.io.js"></script>
    <script>
        const socket = io();
        const consoleEl = document.getElementById("console");
        const cmdInput = document.getElementById("cmdInput");

        // 接收服务端日志
        socket.on("console-output", (msg) => {
            const line = document.createElement("div");
            line.textContent = msg.data;
            if (msg.type === "stderr") {
                line.classList.add("stderr");
            }
            consoleEl.appendChild(line);
            // 自动滚动到底部
            consoleEl.scrollTop = consoleEl.scrollHeight;
        });

        // 发送命令到服务端
        function sendCommand() {
            const cmd = cmdInput.value.trim();
            if (cmd) {
                socket.emit("send-cmd", cmd);
                cmdInput.value = "";
            }
        }

        // 回车触发命令执行
        cmdInput.addEventListener("keypress", (e) => {
            if (e.key === "Enter") {
                sendCommand();
            }
        });
    </script>
</body>
</html>

关键说明

  1. 重写write方法的优势:所有通过console.log、console.error直接输出,或是程序运行产生的stdout/stderr内容,最终都会调用process.stdout.write/process.stderr.write,因此重写该方法能确保捕获所有输出,避免流缓冲导致的data事件失效问题。
  2. 编码处理:将输出的chunk(可能是Buffer或字符串)转为UTF-8字符串,确保客户端能正常显示。
  3. 保留原始输出:通过保存并调用原始write方法,服务器控制台仍能正常看到日志,便于调试。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 01:20:39