listen函数绑定端口失败的常见原因:端口被占用或权限不足;Linux/macOS下1024以下端口需root权限;Windows可能因TIME_WAIT残留导致;应检查占用并改用高编号端口。

listen函数绑定端口失败的常见原因
Go 中用 net.Listen("tcp", ":8080") 启动服务器时,如果返回 "address already in use" 错误,通常不是代码问题,而是端口被占用或权限不足。Linux/macOS 下 1024 以下端口需要 root 权限;Windows 则可能因上次进程未退出干净导致 TIME_WAIT 状态残留。
- 检查端口占用:
lsof -i :8080(macOS/Linux)或netstat -ano | findstr :8080(Windows) - 快速释放:改用高编号端口(如
":8081")绕过权限限制 - 避免硬编码地址:用
":8080"而非"127.0.0.1:8080",否则无法响应外部请求
accept后必须显式启动 goroutine 处理连接
Listener.Accept() 是阻塞调用,但每次 accept 返回的是一个 net.Conn,它本身不自动读写——你得自己启动 goroutine 去 Read 和 Write,否则新连接会卡住,后续连接也无法 accept。
- 漏掉
go handleConn(conn)是新手最常犯的错误,表现是只能处理第一个连接 - 每个连接应独立 goroutine,但要注意资源控制:可加计数器或用
sync.WaitGroup管理生命周期 - 别在 handler 里直接用
conn.Close()后继续读写,会导致"use of closed network connection"
read/write 时容易忽略的 EOF 和超时处理
TCP 连接关闭时,conn.Read() 不会报错,而是返回 n == 0 且 err == io.EOF;而长时间无数据时,默认无读超时,客户端断连后服务端可能一直阻塞。
- 必须检查
err == io.EOF并正常退出读循环,而不是当成异常 panic - 设置读写超时:
conn.SetReadDeadline(time.Now().Add(30 * time.Second)),否则空闲连接会无限占着 goroutine -
bufio.Reader可提升小包读取效率,但注意ReadString('\n')遇到超时或 EOF 会返回部分数据 + error,需手动处理缓冲区
简单回声服务器示例的关键骨架
以下是最简可用的 TCP 回声服务器核心逻辑,去掉日志和错误恢复,仅保留主干:
立即学习“go语言免费学习笔记(深入)”;
listener, _ := net.Listen("tcp", ":8080")
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
continue // 如临时资源不足,跳过本次
}
go func(c net.Conn) {
defer c.Close()
buf := make([]byte, 1024)
for {
n, err := c.Read(buf)
if n > 0 {
c.Write(buf[:n]) // 回传收到的内容
}
if err != nil {
if err == io.EOF {
return
}
return
}
}
}(conn)
}真正上线时还得加连接数限制、心跳检测、粘包处理——但这些不是 listen/accept 的责任,而是协议层该解决的事。别一上来就堆 gob 或 json 编解码,先确保裸字节收发稳定。










