
本文介绍如何在 go 中将 tcp/unix 套接字与 os.stdin/os.stdout 无缝对接,实现类似 socat 的双向数据转发,核心在于利用 go 标准库的 io 接口抽象与 io.copy 的组合能力。
本文介绍如何在 go 中将 tcp/unix 套接字与 os.stdin/os.stdout 无缝对接,实现类似 socat 的双向数据转发,核心在于利用 go 标准库的 io 接口抽象与 io.copy 的组合能力。
在 Go 中,os.Stdin 和 os.Stdout 分别实现了 io.Reader 和 io.Writer 接口,而绝大多数网络连接类型(如 net.Conn)也同时实现了这两个接口。这意味着无需底层文件描述符重定向(如 Unix 的 dup2),即可通过标准 I/O 工具函数完成 socket 与标准流的桥接——这是 Go 区别于 C/socat 的简洁优势。
以下是一个完整、健壮的双向转发示例(支持 TCP 客户端场景):
package main
import (
"io"
"log"
"net"
"os"
)
func main() {
// 示例:连接到本地 echo 服务(可替换为任意 TCP 地址)
conn, err := net.Dial("tcp", "127.0.0.1:8080")
if err != nil {
log.Fatal("无法建立 socket 连接:", err)
}
defer conn.Close()
// 启动两个 goroutine,分别处理 stdin→socket 和 socket→stdout 的双向复制
done := make(chan error, 2)
go func() {
_, err := io.Copy(conn, os.Stdin)
done <p>⚠️ 关键注意事项: </p>
- 必须使用 io.Copy 而非 bufio.Writer.ReadFrom:原答案中 writer.ReadFrom(os.Stdin) 仅支持单向写入且无法优雅终止;实际生产中需双向 io.Copy 并配合 goroutine 协作;
- net.Conn 天然满足 io.Reader + io.Writer,无需额外包装(bufio 在高吞吐场景下可选,但非必需);
- 错误处理需区分 io.EOF:它表示正常流结束,不应视为异常;
- 若需支持 Stdin 关闭后自动终止 socket 写入,可结合 context.WithCancel 控制生命周期;
- 对于 Unix domain socket 或 TLS 连接,只需替换 net.Dial 为对应 Dialer 即可复用相同逻辑。
总结:Go 的接口设计让 socket 与标准流的集成变得直观可靠。与其模拟传统 Unix 文件描述符操作,不如拥抱 io.Copy + net.Conn 的组合范式——简洁、安全、符合 Go 的并发哲学。











