必须为每次 conn.Read() 设置 SetReadDeadline,否则高并发下 goroutine 会永久挂起并泄漏资源;写操作需用带缓冲 channel 解耦读写 goroutine,避免竞争和数据错乱。

conn.Read() 卡住不是 bug,是没设 SetReadDeadline —— 高并发下不加超时,成千上万个 goroutine 就会停在 net.(*conn).Read,pprof 看似“卡死”,实为永久挂起+资源泄漏。
每次 conn.Read() 前必须重置读超时
Go 的 net.Conn 默认阻塞,Read() 会一直等,直到有数据、连接关闭或出错。客户端静默断连、网络抖动、或只是发包慢,都会让 goroutine 永远卡住。
- 正确写法:
conn.SetReadDeadline(time.Now().Add(30 * time.Second)),必须在每次Read()调用前设置 - 不能复用旧时间值(比如存成变量反复用),否则 deadline 越来越早,连接被误杀
- HTTP/1.1 keep-alive 场景可只设一次,但得确保首次
SetReadDeadline成功且发生在Read()之前 - 错误类型是
*net.OpError,用os.IsTimeout(err)判断,别用strings.Contains(err.Error(), "i/o timeout")
写操作必须用带缓冲 chan []byte 解耦
多个 goroutine 直接调 conn.Write() 会竞争 socket 底层状态,轻则 panic(write: broken pipe),重则数据错乱、粘包。
- 标准结构:一个 goroutine 专读 → 解析消息 → 发到
writeChan;另一个专从writeChan取数据 → 调conn.Write() -
writeChan必须带缓冲:make(chan []byte, 16);容量按平均消息大小和突发流量调, 64 可能浪费内存 - 读 goroutine 发送前加
select { case writeChan ,避免因 channel 满而阻塞 - 写 goroutine 每次
Write()前设conn.SetWriteDeadline(time.Now().Add(5 * time.Second)),并检查n != len(data),未写全需重试或记录
别碰非阻塞 socket,也别共用 conn 的读写 goroutine
有人想用 ioctlsocket 或 accept4(..., SOCK_NONBLOCK) 手动切非阻塞模式——这在 Go 里是弯路。Go 的调度器靠 gopark() + pollDesc 实现高效等待,手动非阻塞反而破坏 netpoll 机制。
-
net.Conn不是并发安全的:两个 goroutine 同时Read()可能跳字节或重复解析;同时Write()可能拼错包 - 常见现象:客户端收不到完整消息、服务端日志频繁报
i/o timeout但连接未断、pprof 显示大量 goroutine 卡在conn.Read()或conn.Write() -
conn.Close()应由读 goroutine 在 EOF 或 error 后触发,并显式close(writeChan),通知写 goroutine 退出 - 跨设备(如 Raspberry Pi ↔ MacBook)高频
binary.Write()后突然阻塞?大概率是 Nagle 算法 + 缓冲区积压 + 生命周期不同步;加conn.SetNoDelay(true)关 Nagle
sync.RWMutex 读锁别升级写锁,也别漏 RUnlock
这不是 socket 层问题,但高并发连接管理中极易踩坑:用 RWMutex 管理每个连接的状态(如 lastPing、sessionData)时,读锁未配对释放或试图升级,会导致写 goroutine 永久等待。
- 读锁不能升级为写锁:已持
RLock()的 goroutine 再调Lock(),会永远卡在(*RWMutex).Lock,因为写锁必须等所有读锁释放 -
defer mu.RUnlock()必须紧跟mu.RLock(),同一作用域;否则 panic 或提前 return 会让读锁泄漏 - 读多写少场景下,若写操作占比超 15%~20%,
RWMutex开销反超sync.Mutex,此时应换回普通互斥锁 - 多个
RWMutex间加锁顺序不一致(AB-BA)也会静默卡死,统一按变量名或uintptr(unsafe.Pointer(&mu1))排序
真正容易被忽略的是:deadline 不是设一次就完事,而是每次 I/O 前都要重算;channel 缓冲不是越大越好,而是要匹配你的消息分布和下游消费能力;读写 goroutine 分离不是可选优化,而是并发安全的底线。

















