Go net包使用需注意:监听地址格式必须为“tcp:port”或“tcp:host:port”,Accept后须用goroutine并发处理连接,每个conn需defer关闭以防泄漏;Dial应设超时避免阻塞;UDP服务端用ListenUDP,客户端用DialUDP,二者语义不同。
go 的 net 包开箱即用,但直接写 tcp/udp 服务很容易在线上崩——不是卡死就是连接泄漏,核心问题不在语法,而在对连接生命周期、超时和并发模型的理解偏差。
net.Listen 启动服务时地址格式错就直接 panic
监听地址必须带端口,且协议+地址要匹配。写成 "8080" 或 "localhost" 都会报 "listen tcp: address 8080: missing port in address";"tcp4://:8080" 这种 URL 格式也不合法,net.Listen 只认 "tcp" / "tcp4" / "tcp6" + 纯地址字符串。
- 正确写法是
":8080"(监听所有接口)或"127.0.0.1:8080"(仅本地) - IPv6 要用
"[::1]:8080",方括号不能省 - 启动后记得
defer ln.Close(),否则进程退出时端口可能残留
Accept 后不启 goroutine 就变成串行阻塞
ln.Accept() 是阻塞调用,返回一个 net.Conn;如果你在 for 循环里不加 go handle(conn),而是直接 handle(conn),那下一个连接必须等当前连接处理完才能被接受——本质是单线程轮询,压根没用上 Go 的并发优势。
- 每个
conn必须在独立 goroutine 里处理,否则服务器吞吐量归零 -
handle函数开头建议加defer conn.Close(),避免连接泄漏 - 别在 handler 里直接用
conn.Read()读不定长数据,TCP 是字节流,不处理黏包必出错
net.Dial 不设超时,DNS 卡住 30 秒才失败
默认 net.Dial("tcp", "api.example.com:443") 完全没超时控制:DNS 解析慢、目标端口无响应、中间防火墙静默丢包,都会让 goroutine 在那里干等系统级 timeout(Linux 通常 30 秒),导致连接池耗尽、请求堆积。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 优先用
&net.Dialer{Timeout: 3*time.Second, KeepAlive: 30*time.Second}+dialer.DialContext(ctx, "tcp", addr) -
ctx应来自上游(比如 HTTP handler 的r.Context()),确保能响应 cancel - 如果 DNS 是瓶颈(如
/etc/resolv.conf配了多个慢 nameserver),得自己构造net.Resolver并设Timeout
UDP 的 ListenUDP 和 DialUDP 根本不是一回事
TCP 有“连接”概念,UDP 没有。net.ListenUDP 返回的 *net.UDPConn 是通配型 socket,能用 ReadFromUDP 收任意 IP 的包,再用 WriteToUDP 回复指定地址;而 net.DialUDP 创建的是“绑定远端”的连接,Write 只能发给那个固定地址,Read 也只收它的回包,WriteToUDP 直接 panic。
- 做 UDP 服务器(如 DNS、NTP 服务端)用
ListenUDP - 做客户端(如向 NTP 服务器发请求)用
DialUDP更简洁 - 两者都需手动处理丢包、乱序、校验和——UDP 本身不保证可靠,应用层该重传还得重传
最常被跳过的其实是连接关闭阶段:调 ln.Close() 只是让后续 Accept() 失败,已建立的 conn 还在跑;真正关干净得靠 sync.WaitGroup 记录活跃 handler,或者用 context.WithCancel 通知所有正在读写的 goroutine 主动退出。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










