tcp粘包本质是字节流无消息边界,write/read不一一对应;应用层须自定义协议(如分隔符、长度头)明确边界,解包逻辑应独立为goroutine通过channel输出完整消息。

为什么 TCP 长连接里字符串会粘包
因为 TCP 是字节流协议,Write 和 Read 不保证一一对应——你调一次 Write 发 10 个字节,对方可能分两次 Read 到;也可能你连续发两个 5 字节的包,对方一次 Read 拿到 10 字节。所谓“粘包”,本质是应用层没约定好消息边界。
在 Go 的 net.Conn 上直接用 bufio.Reader.ReadString('\n') 或 io.ReadFull 很容易 panic 或阻塞,尤其当协程多、连接活跃时,错误会扩散得很快。
- 别依赖
conn.SetReadDeadline来“防卡死”——它只解决超时,不解决拆包逻辑错乱 - 别在每个 goroutine 里反复 new
bufio.Reader——内存分配和 buffer 复用成本高 - 别把粘包处理逻辑塞进业务 handler —— 一旦协议升级(比如改分隔符或加 length header),所有地方都要动
用 channel + goroutine 做解包层的关键结构
核心是把“从 conn 读字节 → 缓存 → 按规则切出完整消息 → 发给业务”这个流程独立成一个 goroutine,并用 channel 向外吐 string 或自定义消息结构体。这样业务侧只管收,不操心底层。
示例中用 \n 作分隔符,但结构可轻松改成固定头长(如前 4 字节表示长度):
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
// 解包 goroutine 示例(简化版) func startPacketReader(conn net.Conn, ch chan
-
ch类型必须是带缓冲的:make(chan string, 128),否则业务消费慢时,解包 goroutine 会阻塞在 send 上,导致 conn 读取停滞 - 不要在解包 goroutine 里做耗时操作(如 JSON 解析、DB 查询)——那是业务 goroutine 的事
- 如果协议用 length-header,就不用
ReadString,改用io.ReadFull(reader, headerBuf)+io.ReadFull(reader, payloadBuf)
goroutine 泄漏和连接关闭时的清理陷阱
最常被忽略的是:连接断开后,解包 goroutine 还在等 reader.ReadString,而 channel 没人接收,整个 goroutine 就永远卡住。
正确做法是用 context.WithCancel 控制生命周期,并在 conn 关闭时触发 cancel:
ctx, cancel := context.WithCancel(context.Background()) go startPacketReaderWithContext(ctx, conn, ch) <p>// 在 defer 或连接异常时调用 defer cancel() // 注意:必须确保 cancel 只调一次</p>
-
startPacketReaderWithContext内部要监听ctx.Done(),并在每次ReadString前检查是否已取消 - 别用
time.AfterFunc或select { case 做超时——它不能中断正在阻塞的 <code>ReadString - channel 关闭前,先关写端(
close(ch)),再 cancel ctx,避免业务 goroutine 收到零值后还继续读
多个连接共用同一解包逻辑时的复用要点
如果你有几百个连接,为每个都起 goroutine + channel,内存和 goroutine 调度压力不小。这时可以把解包逻辑抽成可复用函数,但注意三点:
- 每个连接必须有自己的
bufio.Reader实例——bufio.Reader不是线程安全的,不能共享 - channel 缓冲大小要按预期吞吐设:高频率小包用
64,低频大包用16,别盲目设 1024 - 别把
conn直接传进闭包然后 deferconn.Close()——Go 的net.Conn关闭后,关联的bufio.Reader读行为未定义,必须先停掉解包 goroutine,再关 conn
真正难的不是写一个能跑的解包器,而是让几百个连接在高并发下不泄漏、不丢包、不 panic。关键不在语法多炫,而在每处 cancel() 是否配对、每个 ch 是否被消费完、每次 Read 是否有兜底判断。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










