go端口转发核心是防goroutine卡死、连接泄漏和地址绑定错误:需用":8080"等正确格式监听,双向io.copy配setdeadline与defer关闭,ssh转发须手动启监听协程而非依赖不存在的localportforward。

Go 实现端口转发,核心不是“写转发逻辑”,而是“不让 goroutine 卡死、不漏关连接、不绑错地址”。
net.Listen 地址格式写错直接 panic
常见错误是把 ":8080" 写成 "8080" 或 "localhost:8080"。前者报 "listen tcp: address 8080: missing port in address";后者在某些系统(尤其是 macOS)会被解析为 IPv6 地址,导致 http://127.0.0.1:8080 访问失败。
- 必须用
":8080"(监听所有接口)或"127.0.0.1:8080"(仅本地) - 绑定
":80"或":443"需 root 权限,普通用户会遇到"bind: permission denied" - Windows 下若提示
"WSAEADDRINUSE",说明端口已被占用,可用netstat -ano | findstr :8080查进程
io.Copy 必须双向启动,缺一不可
只写一行 io.Copy(rConn, lConn),数据能发出去但收不到响应——TCP 是全双工,两条流独立存在,必须分别搬运。
- 一个 goroutine 执行
io.Copy(lConn, rConn)(服务端 → 客户端) - 另一个 goroutine 执行
io.Copy(rConn, lConn)(客户端 → 服务端) - 两个
io.Copy都要配defer lConn.Close()和defer rConn.Close(),否则连接泄漏,跑几小时就触发"too many open files" - 别用
io.CopyN或自定义 buffer 大小“优化”,默认 32KB 已足够;改小反而增加系统调用次数
没设 SetDeadline 就等于放任 goroutine 僵尸化
没有超时控制的转发,在客户端断网、服务端宕机、中间网络中断时,io.Copy 会永久阻塞在 Read 或 Write 上,goroutine 不退出,内存缓慢上涨。
- 对每个新 accept 的
lConn,立刻调lConn.SetDeadline(time.Now().Add(5 * time.Minute)) - 对
rConn也设,建议略短(如 4 分钟),避免本地已断但远端还在写 - 必须同时设
ReadDeadline和WriteDeadline:写失败(比如对方 close)同样会卡住 - 错误判断要过滤
net.ErrClosed:if err != nil && !errors.Is(err, net.ErrClosed)才记日志,否则满屏干扰
SSH 端口转发不能依赖 LocalPortForward 自动生效
ssh.LocalPortForward 不是函数也不是方法,golang.org/x/crypto/ssh 标准库压根没提供这个接口。所谓“连不上”,90% 是因为只建了 SSH 连接,没手动启动监听和转发协程。
- 先
ssh.Dial拿到*ssh.Client - 再调
client.ListenTCP("127.0.0.1:8080")(别用"localhost:8080") - 对每个
listener.Accept()到的连接,起 goroutine 调client.Dial("tcp", "10.0.0.5:3306") - 反向隧道更麻烦:
client.RequestReversePortForward("0.0.0.0:2222")才是正确入口,且远端/etc/ssh/sshd_config必须开GatewayPorts yes和AllowTcpForwarding yes
真正难的不是写完第一版能跑通,而是让它在持续 7×24 小时运行时不卡、不漏、不崩——Deadline 设置、close 时机、地址解析细节,这些地方一疏忽,就是凌晨三点的告警电话。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











