使用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
相关产品推荐
相关产品推荐

