net.listen返回的监听器必须显式关闭,因defer在函数返回时执行,而main中accept循环无限运行导致listener.close()永不调用;正确做法是封装函数或捕获信号主动关闭,并设读写deadline防goroutine泄漏。

net.Listen 返回的监听器必须显式关闭,否则进程退出时可能残留文件描述符,尤其在频繁启停调试时容易触发 too many open files 错误。
为什么 net.Listen("tcp", ":8080") 不能直接用 defer listener.Close()?
因为 defer 在函数返回时才执行,而 main 函数里监听循环是无限的,listener.Close() 实际永远不会被调用。常见错误是把 defer 放在 main 开头,结果服务跑着跑着就耗尽端口或 fd。
- 正确做法:把监听逻辑封装进单独函数,或用
signal.Notify捕获os.Interrupt后主动关闭 - 调试阶段可加日志:
log.Printf("Listener closed: %v", err)确认关闭是否生效 - Linux 下可通过
lsof -i :8080验证端口释放情况
conn.Read 和 bufio.NewReader(conn).ReadString('\n') 的行为差异
前者读原始字节流,不识别消息边界;后者按分隔符切分,适合文本协议但遇到粘包或无换行数据会阻塞。实际项目中常混用——比如先用 conn.Read 读固定长度头部,再用 bufio 处理内容。
-
conn.Read([]byte):返回实际读到的字节数n,需手动处理buffer[:n],io.EOF表示连接关闭 -
ReadString('\n'):若缓冲区无\n,会一直等待(直到超时或连接断开),不适合二进制协议 - 生产环境建议设
conn.SetReadDeadline,避免 goroutine 卡死
goroutine 泄漏:为什么 go handleConnection(conn) 可能失控?
每个连接启动一个 goroutine 是标准做法,但若 handleConnection 内部没做超时控制或异常退出,连接不断开时 goroutine 就不会结束。观察 runtime.NumGoroutine() 持续上涨就是典型信号。
- 必须在
handleConnection开头调用defer conn.Close() - 所有阻塞读写操作都应配 deadline:
conn.SetReadDeadline(time.Now().Add(30 * time.Second)) - 避免在 handler 里启动子 goroutine 后不 wait,尤其不要用
go func() {}()忘记同步
真正难的不是写通一个回显服务,而是让 net.Conn 在各种异常网络条件下(如客户端突然断网、中间设备重置连接)仍能及时释放资源、不堆积 goroutine、不泄漏 fd。这些细节不会报错,但会在高并发压测时集中爆发。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











