IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

Windows 下重定向当前进程的 stdout 到网络连接

云风的 BLOG 2018-07-05 13:44:37 累计浏览 2,634 次
本机暂存

   前段时间碰到一个需求,想把当前进程的标准输出重定向到一个 tcp 连接上。

   如果依照 posix 标准,调用一下 dup2 这个 api 就能搞定,但是 windows 并不是基于 posix 标准的操作系统,所以做起来要麻烦的多。

   我在 stackoverflow 和 msdn 上找了一番,没有看到什么靠谱的做法,所以自己折腾了一天。这里的难点在于:windows 上虽然有 _dup2 来模拟 posix 的 api dup2 ,但 fd 在 windows 上并不是内核对象, HANDLE 才是。fd 是在 runtime 层模拟出来的东西。msdn 上引用最多的一篇是:Creating a Child Process with Redirected Input and Output ,做的事情是创建一个子进程,然后重定向标准输入输出。

   重定向子进程和重定向当前进程有什么区别?我是这样理解的:

   Windows 虽然也有标准输入输出的概念,但是是基于 HANDLE 的。GetStdHandle 和 SetStdHandle 两个 API 虽然可以读写 Windows 的标准输入输出句柄,但这个句柄似乎(我猜想)是在 runtime 初始化阶段绑定到 fd 0 1 2 上的,之后,基于 fd 的一套 runtime 机制都不再经过这个转换过程。在进程运行过程中,调用 SetStdHandle 并不能重定向 C 的标准输入输出库。

   在 stackoverflow 上的一个帖子也谈及了这个问题,SetStdHandle has no effect on cout/printf 。模仿 posix 的做法,使用 GetStdHandle 获得 windows 标准输出,然后利用 DuplicateHandle 复制 handle 的做法是无效的。

   帖子里给出的方案是在 runtime 层用 _dup  复制 fd 再重定向到文件。不过想重定向到网络连接却没这么简单。因为 windows 下 socket 并不是 first class 的 handle ,不能直接当普通的文件 handle 使用,也就无法用 dup/dup2 传过去。

   所以,我们需要先将 stdout 用 dup2 重定向到匿名管道,再创建一个线程去读这个管道,转发到 socket 。

   光启动这么一个转发线程也有潜在的问题:在进程结束的时候,runtime 在处理最后的标准输出时,无法直接让转发线程感知到,这样有可能丢失结束前最后的输出。我们需要额外的机制在进程要结束时通知转发线程停止转发。主动通知可以让转发线程处理完所有已经转发的数据,而不会有遗漏,主线程则可以等待转发线程工作作完再自行退出。

   因为读转发管道 Handle 使用的是 ReadFile ,windows 下的文件 Handle 无法和 Windows 的 Event 一起通过 WaitForMultipleObjects 工作(这或许是 windows 要额外引入 IOCP 的原因之一?)。我们不能利用额外的 Event 来做这件通知工作,只能通过关闭管道,让 ReadFile 感知。

   这里要小心,dup 内部会增加内核 handle 的引用计数,所以不要漏掉了 close ,否则会让管道没有正确关闭,ReadFile 无法在关闭后返回。

   我 写了一个示例供参考 。它把转发开启和转发结束的过程封装在一个 lua 模块中。

同分类推荐文章

  1. 等了十年的 Go 链式管道,终于来了:seq 让你像写 Scala 一样写 Go (2026-06-25 18:38:18)
  2. Go 实验特性详解 (2026-06-21 10:05:27)
  3. amd64 微架构级别对 Go 程序性能提升多少? (2026-06-21 09:38:49)

查看更多 后端 文章 →

建议继续学习

  1. gen_tcp发送进程被挂起起因分析及对策 (累计阅读 37,821)
  2. TCP 的那些事儿(上) (累计阅读 22,696)
  3. 从输入 URL 到页面加载完成的过程中都发生了什么事情? (累计阅读 15,933)
  4. 自建DNS以防止GFW干扰 (累计阅读 13,125)
  5. 浅谈TCP优化 (累计阅读 11,082)
  6. 推荐一些socket工具,TCP、UDP调试、抓包工具 (累计阅读 10,844)
  7. 查看 Apache并发请求数及其TCP连接状态 (累计阅读 10,068)
  8. 推荐一些socket工具,TCP、UDP调试、抓包工具 (累计阅读 8,840)
  9. Emacs安装配置 (累计阅读 8,371)
  10. websocket 连接 C Server的尝试 (累计阅读 7,922)