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

使用Deno原生API创建TCP代理遇中断错误求助

问题描述

原本基于Node.js实现的TCP代理代码,改为导入node:net后可在Deno兼容层正常运行,但改用Deno原生API实现相同功能时,程序运行约1分钟后会抛出未捕获的Interrupted: operation canceled错误,即使添加了try-catch也无法捕获,需要解决客户端取消操作导致服务器崩溃的问题。

Node.js 示例代码

var net = require('net');
net.createServer(function(from) {
    var to = net.createConnection({
        host: '<toHost>',
        port: <toPort>
    });
    from.on('error', (err) => {
        to.destroy(err);
    });
    to.on('error', (err) => {
        from.destroy(err);
    });
    from.pipe(to);
    to.pipe(from);
}).listen('<someport>', undefined);

Deno 原生API代码(存在问题版本)

const listener = Deno.listen({
    port: <fromPort>,
    host: undefined
});
try{
for await (const conn1 of listener) {
    try{
        const conn2 = await Deno.connect({
            host: '<toHost>',
            port: <toPort>   
        });

        if (conn2) {
            conn1.readable.pipeTo(conn2.writable);
            conn2.readable.pipeTo(conn1.writable);
        }
    } catch (e) { console.log(e); }

}} catch (e) { console.log(e); }

错误信息

error: Uncaught (in promise) Interrupted: operation canceled
    at async Object.pull (ext:deno_web/06_streams.js:1001:27)
解决方案

这个错误源于pipeTo返回的Promise未被处理,当连接中断时,这些未处理的Promise会抛出未捕获异常。需要为每个pipeTo添加错误处理,并在连接异常时正确关闭两端连接。

修改后的Deno代码

const listener = Deno.listen({
    port: <fromPort>,
    host: undefined
});

async function handleProxy(conn1) {
    let conn2;
    try {
        conn2 = await Deno.connect({
            host: '<toHost>',
            port: <toPort>   
        });

        // 为管道操作添加错误捕获,避免未处理的Promise异常
        const pipe1 = conn1.readable.pipeTo(conn2.writable, { preventClose: true })
            .catch(err => console.debug("conn1->conn2 管道错误:", err));
        const pipe2 = conn2.readable.pipeTo(conn1.writable, { preventClose: true })
            .catch(err => console.debug("conn2->conn1 管道错误:", err));

        // 等待任一管道操作完成后关闭连接
        await Promise.race([pipe1, pipe2]);
    } catch (e) {
        console.error("代理初始化错误:", e);
    } finally {
        // 确保两端连接都被关闭,防止资源泄漏
        conn1.close();
        if (conn2) conn2.close();
    }
}

try {
    for await (const conn of listener) {
        // 异步处理每个连接,不阻塞监听逻辑
        handleProxy(conn);
    }
} catch (e) {
    console.error("监听错误:", e);
}

关键修改说明

  • 将连接代理逻辑抽离为单独函数,便于集中管理错误
  • 给每个pipeTo调用添加.catch(),捕获管道传输中的错误,避免未捕获Promise异常
  • 使用preventClose: true参数,禁止管道自动关闭连接,由我们手动统一管理连接生命周期
  • 在finally块中强制关闭两端连接,防止资源泄漏
  • 通过Promise.race监听任一管道操作的完成或异常,及时触发连接清理

修改后,当客户端取消操作或连接中断时,错误会被正确捕获处理,不会导致服务器崩溃。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 13:06:03