TCP Socket消息串扰Bug求助:旧连接消息片段混入新消息如何解决?
发现TCP Socket数据粘包问题:前一消息片段混入下一条消息
意外发现一个TCP Socket数据粘包问题:前一个连接的消息片段会混入下一条消息中。我搭建了基础的客户端-服务端Demo,为简化示例移除了所有错误处理;由于数据读取过快无法及时断开连接复现Bug,故将部分Printf替换为time.Sleep。消息包采用简单结构:前4字节为长度字段,后续为内容。
客户端代码
package main import ( "encoding/binary" "fmt" "net" ) func main() { conn, _ := net.Dial("tcp", "0.0.0.0:8081") defer conn.Close() str := "msadsakdjsajdklsajdklsajdk" // 创建数据包 buf := make([]byte, len(str)+4) copy(buf[4:], str) binary.LittleEndian.PutUint32(buf[:4], uint32(len(str))) for { _, err := conn.Write(buf) if err != nil { fmt.Println(err) return } } }
服务端代码
package main import ( "encoding/binary" "fmt" "net" "sync" "time" ) func ReadConnection(conn net.Conn, buf []byte) (err error) { maxLen := cap(buf) readSize := 0 for readSize < maxLen { // 替换Printf为sleep time.Sleep(time.Nanosecond * 10) readN, err := conn.Read(buf[readSize:]) if err != nil { return err } readSize += readN } return nil } func handleConnection(conn net.Conn, waitGroup *sync.WaitGroup) { waitGroup.Add(1) defer conn.Close() defer waitGroup.Done() fmt.Printf("服务连接 %s\n", conn.RemoteAddr().String()) var packageSize int32 = 0 int32Buf := make([]byte, 4) for { // 读取长度 conn.Read(int32Buf) packageSize = int32(binary.LittleEndian.Uint32(int32Buf)) // 假设长度应为26 if packageSize > 26 { fmt.Println("数据包大小错误") return } // 读取内容 packageBuf := make([]byte, packageSize) if err := ReadConnection(conn, packageBuf); err != nil { fmt.Printf("错误: %s\n", err) return } // 替换Printf为sleep time.Sleep(time.Nanosecond * 100) } } func main() { // 建立监听 listener, _ := net.Listen("tcp", "0.0.0.0:8081") defer listener.Close() waitGroup := sync.WaitGroup{} for { conn, err := listener.Accept() if err != nil { break } go handleConnection(conn, &waitGroup) } waitGroup.Wait() }
问题现象
服务端读取包长度时,int32Buf会接收到前一条消息的最后2字节(d、k)与当前包长度的前2字节,形成[107,100,26,0]的字节切片(正确应为[26,0,0,0]),导致解析出错误的包长度,触发包大小错误提示。
异常数据截图:
内容的提问来源于stack exchange,提问作者LiaVa
相关产品推荐
相关产品推荐

