Go中Listen后必须循环调用Accept并配合goroutine处理并发连接,否则仅能处理首个连接;每个conn需设读写超时并显式关闭以防fd泄漏。

Listen 之后必须调用 Accept 才能接收连接
Go 的 net.Listen 只是创建监听套接字并绑定地址,它不阻塞、也不处理任何客户端连接;真正等待并获取新连接的是 Accept。常见错误是只调用 Listen 就结束程序,结果服务看似“启动了”,但根本收不到请求。
-
Listen返回net.Listener接口,需持续调用其Accept()方法 -
Accept()是阻塞调用,返回net.Conn(代表一个具体连接)和可能的错误 - 若不循环调用
Accept,服务器只能处理第一个连接就退出
Accept 必须配合 goroutine 处理并发连接
每个 Accept 返回的 net.Conn 是单次连接,读写操作(如 Read/Write)也是阻塞的。如果在主线程里同步处理,后续连接会排队等待——这不是“服务器没响应”,而是逻辑卡死在上一个连接的 I/O 上。
- 典型模式:主 goroutine 循环
Accept,每拿到一个conn就起一个新 goroutine 处理 - 不加 goroutine 的写法会导致吞吐量归零,尤其在连接持续发送数据时
- 注意关闭
conn:应在处理 goroutine 结束前调用conn.Close(),否则 fd 泄漏
listener, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal(err)
}
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
log.Println("Accept error:", err)
continue // 不要直接 break,避免整个服务中断
}
go func(c net.Conn) {
defer c.Close()
buf := make([]byte, 1024)
n, _ := c.Read(buf)
c.Write(buf[:n])
}(conn)
}
Listen 地址字符串格式与端口占用问题
net.Listen("tcp", addr) 的 addr 必须是形如 "host:port" 的字符串。本地监听常用 ":8080"(等价于 "localhost:8080"),但要注意:
-
":8080"监听所有 IPv4/IPv6 地址,"127.0.0.1:8080"仅限 IPv4 回环 - 若端口已被占用,
Listen返回*net.OpError,错误信息含"bind: address already in use" - Linux/macOS 下可复用端口需设置
SO_REUSEADDR,Go 默认不启用;可通过net.ListenConfig配置Control函数实现
Accept 返回的 Conn 需要显式超时控制
net.Conn 默认无读写超时,一旦客户端异常断连或发半包,Read 可能永远阻塞。不能依赖 listener.SetDeadline(它不存在),而要对每个 conn 单独设。
立即学习“go语言免费学习笔记(深入)”;
- 使用
conn.SetReadDeadline或conn.SetDeadline设置绝对时间点 - 推荐用
conn.SetReadTimeout(相对时长),更直观 - 超时后
Read返回io.EOF或带net.ErrTimeout的错误,需检查并退出处理 goroutine
实际部署中,最易被忽略的是连接超时和资源清理——没有超时,几百个空闲连接就能拖垮服务;没有 defer conn.Close(),文件描述符耗尽后 Accept 会开始报 "too many open files"。










