七叶笔记 » golang编程 » golang tcp沾包处理之server

golang tcp沾包处理之server

什么是粘包,为什么会有粘包处理

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())
 }
 }
}
 

相关文章