什么是粘包,为什么会有粘包处理
tcp (transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的
处理方法
一般有两种处理方式,一种为发送数据流用分隔符,另一种在客户端发包的时候定义包体的长度,服务端按照一定的规则解包
栗子-server端
package main import ( " bytes " " encoding /binary" "fmt" "net" "unsafe" "errors" ) var ( pkgHeaderLen int ErrNotEnoughData = errors.New("packet stream is not enough") ) // 用来接收tcp 数据流 var buffer *bytes.Buffer func init() { buffer = new(bytes.Buffer) // 自定义包头长度 pkgHeaderLen = (int)((uint)(unsafe.Sizeof(PackageHeader{}))) } type PackageHeader struct { ServiceID uint32 // service id Command uint32 // operation command code Code int32 // error code Len uint16 // body length } type Package struct { H Package Header B string } func (p Package) Marshal() (* byte s.Buffer, error) { var ( err error buf *bytes.Buffer ) buf = &bytes.Buffer{} err = binary.Write(buf, binary.LittleEndian, p.H) if err != nil { return nil, err } buf.WriteByte((byte)(len(p.B))) buf.WriteString(p.B) return buf, nil } func (p *Package) Unmarshal(buf *bytes.Buffer) (int, error) { var ( err error len byte ) // header err = binary.Read(buf, binary.LittleEndian, &(p.H)) if err != nil { return 0, err } // 获取包体长度 len, err = buf.ReadByte() if err != nil { return 0, nil } // 是否是包体的长度 if buf.Len() < (int)(p.H.Len) { return 0, ErrNotEnoughData } // 获取具体数据,并把buffer 的offset后置 p.B = (string)(buf.Next((int)(len))) return (int)(p.H.Len) + pkgHeaderLen, nil } func main() { netListen, _ := net.Listen("tcp", ":9988") defer netListen.Close() fmt.Println("Waiting for clients") for { conn, err := netListen.Accept() if err != nil { continue } fmt.Println(conn.RemoteAddr().String(), " tcp connect success") go handleConnection(conn) } } func handleConnection(conn net.Conn) { pkg := &Package{} buf := make([]byte, 1024) for { n, err := conn.Read(buf) if err != nil { fmt.Println(conn.RemoteAddr().String(), " connection error: ", err) return } // 一直往buffer 写数据, 然后在按照规则解包 buffer.Write(buf) // 解码包体长度 length , err :=pkg.Unmarshal(bytes.NewBuffer(buf)) if err == nil { buffer.Next(n) fmt.Println("==========") fmt.Printf("pkg len %d, %s\n",length, pkg.B) fmt.Println("==========") } else { fmt.Println(err.Error()) } } }