用 net.Listen("tcp", ":8080") 启动 TCP 服务,监听所有网卡;需 defer 关闭 listener,为连接设读写超时,按协议拆包,并用 goroutine+recover 处理连接以防止泄漏。

如何用 net.Listen 启动一个基础 TCP 服务?
net.Listen 是 Go 中开启 TCP 监听的入口,但它不直接处理连接——只返回一个 net.Listener。常见错误是忽略地址绑定细节,比如写成 "localhost:8080" 导致仅本地可连,或用 ":8080"(推荐)才能接受外部请求。
- 地址格式必须是
"host:port",空 host(即":8080")表示监听所有网卡 - 端口若被占用,
net.Listen会返回"listen tcp :8080: bind: address already in use"错误 - 不建议硬编码
"127.0.0.1:8080"——开发调试可以,但部署时客户端连不上 - 调用后必须 defer
ln.Close(),否则进程退出前 socket 可能未释放
ln, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal(err)
}
defer ln.Close()
为什么 conn.Read 会阻塞,又该怎么安全读取?
conn.Read 默认是阻塞式调用,直到有数据到达、连接关闭或发生错误。新手常犯的错是没设超时,导致 goroutine 卡死;或者用固定长度 buffer 读取变长协议(如 HTTP 或自定义文本协议),结果截断或残留。
- 必须为连接设置读写超时:
conn.SetReadDeadline或更推荐的conn.SetReadTimeout - buffer 大小要合理:太小(如 16 字节)频繁 syscall;太大(如 1MB)浪费内存且无意义
- 不要假设一次
Read就能拿到完整消息——TCP 是字节流,需自己按协议拆包(例如以\n分隔,或带长度头)
conn.SetReadTimeout(5 * time.Second)
buf := make([]byte, 1024)
n, err := conn.Read(buf)
if err != nil {
if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
// 超时,不是连接断开
}
return
}
data := buf[:n]
怎么避免 accept 后的 goroutine 泄漏?
Go 的惯用法是每 accept 一个 conn 就起一个 goroutine 处理,但如果客户端异常断开、或 handler 里 panic 没 recover,goroutine 就可能永远挂住。
- 所有 handler goroutine 应包裹
defer recover(),否则 panic 会让 goroutine 无法退出 - 连接关闭时,
conn.Read会返回io.EOF,这是正常终止信号,别当成错误打日志 - 如果业务逻辑含长耗时操作(如调用外部 API),应加 context 控制生命周期,避免“僵尸连接”堆积
go func(conn net.Conn) {
defer conn.Close()
defer func() {
if r := recover(); r != nil {
log.Printf("panic on conn %v: %v", conn.RemoteAddr(), r)
}
}()
handleConnection(conn)
}(conn)
本地测试时 telnet 连不上,该查什么?
telnet localhost 8080 连不上,不一定是代码问题,优先排查环境层:
立即学习“go语言免费学习笔记(深入)”;
- 检查 Go 程序是否真在运行:
ps aux | grep your_program - 查端口是否被监听:
lsof -i :8080(macOS/Linux)或netstat -ano | findstr :8080(Windows) - 防火墙是否放行:macOS 的「防火墙」设置、Windows Defender 高级防火墙、云服务器的安全组
- 如果用 Docker,确认端口是否正确映射(
-p 8080:8080),且程序监听的是":8080"而非"127.0.0.1:8080"
真正的难点不在写通连接,而在于边界条件:连接闪断、半开连接、粘包、超时策略与重试配合。这些没法靠一个 net.Listen + Accept 解决,得根据协议定规则。



















