GolangTCP通信办理粘包问题

打印 上一主题 下一主题

主题 999|帖子 999|积分 2997

重要原因就是tcp数据传递模式是流模式,在保持长毗连的时间可以举行多次的收和发。
“粘包"可发生在发送端也可发生在接收端:
   

  • 由Nagle算法造成的发送端的粘包:Nagle算法是一种改善网络传输效率的算法。简单来说就是当我们提交一段数据给TCP发送时,TCP并不立刻发送此段数据,而是等待一小段时间看看在等待期间是否还有要发送的数据,如有则会一次把这两段数据发送出去。
  • 接收端接收不实时造成的接收端粘包:TCP会把接收到的数据存在自己的缓冲区中,然后关照应用层取数据。当应用层由于某些原因不能实时的把TCP的数据取出来,就会造成TCP缓冲区中存放了几段数据。
  办理办法

出现"粘包"的关键在于接收方不确定将要传输的数据包的大小,因此我们可以对数据包举行封包和拆包的操纵。
封包:封包就是给一段数据加上包头,如许一来数据包就分为包头和包体两部门内容了(过滤非法包时封包会参加"包尾"内容)。包头部门的长度是固定的,并且它存储了包体的长度,根据包头长度固定以及包头中含有包体长度的变量就能正确的拆分出一个完备的数据包。
我们可以自己定义一个协议,好比数据包的前4个字节为包头,里面存储的是发送的数据的长度。
protocol包:
  1. package protocol
  2. import (
  3.         "bufio"
  4.         "bytes"
  5.         "encoding/binary"
  6. )
  7. // Encode 将消息编码
  8. func Encode(message string) ([]byte, error) {
  9.         // 读取消息的长度,转换成int32类型(占4个字节)
  10.         var length = int32(len(message))
  11.         var pkg = new(bytes.Buffer)
  12.         // 写入消息头
  13.         err := binary.Write(pkg, binary.LittleEndian, length)
  14.         if err != nil {
  15.                 return nil, err
  16.         }
  17.         // 写入消息实体
  18.         err = binary.Write(pkg, binary.LittleEndian, []byte(message))
  19.         if err != nil {
  20.                 return nil, err
  21.         }
  22.         return pkg.Bytes(), nil
  23. }
  24. // Decode 解码消息
  25. func Decode(reader *bufio.Reader) (string, error) {
  26.         // 读取消息的长度
  27.         lengthByte, _ := reader.Peek(4) // 读取前4个字节的数据
  28.         lengthBuff := bytes.NewBuffer(lengthByte)
  29.         var length int32
  30.         err := binary.Read(lengthBuff, binary.LittleEndian, &length)
  31.         if err != nil {
  32.                 return "", err
  33.         }
  34.         // Buffered返回缓冲中现有的可读取的字节数。
  35.         if int32(reader.Buffered()) < length+4 {
  36.                 return "", err
  37.         }
  38.         // 读取真正的消息数据
  39.         pack := make([]byte, int(4+length))
  40.         _, err = reader.Read(pack)
  41.         if err != nil {
  42.                 return "", err
  43.         }
  44.         return string(pack[4:]), nil
  45. }
复制代码

客户端:
  1. func main() {
  2.         conn, err := net.Dial("tcp", "127.0.0.1:30000")
  3.         if err != nil {
  4.                 fmt.Println("dial failed, err", err)
  5.                 return
  6.         }
  7.         defer conn.Close()
  8.         for i := 0; i < 20; i++ {
  9.                 msg := `Hello, Hello. How are you?`
  10.                 data, err := proto.Encode(msg)
  11.                 if err != nil {
  12.                         fmt.Println("encode msg failed, err:", err)
  13.                         return
  14.                 }
  15.                 conn.Write(data)
  16.         }
  17. }
复制代码
服务器:
  1. package main
  2. import (
  3.         "bufio"
  4.         "fmt"
  5.         "io"
  6.         "net"
  7.         proto "studygo/studygo/tcp/protocol"
  8.         "sync"
  9. )
  10. var wg sync.WaitGroup
  11. func process(conn net.Conn) {
  12.         //与客户端通信
  13.         defer conn.Close()
  14.         reader := bufio.NewReader(conn)
  15.         for {
  16.                 msg, err := proto.Decode(reader)
  17.                 if err == io.EOF {
  18.                         break
  19.                 }
  20.                 if err!=nil{
  21.                         fmt.Println("decode msg failed,err:",err)
  22.                         return
  23.                 }
  24.                 fmt.Println(msg)
  25.         }
  26. }
  27. func main() {
  28.         //本地端口启动服务
  29.         listener, err := net.Listen("tcp", "127.0.0.1:8000")
  30.         if err != nil {
  31.                 fmt.Println("listen failed,err:%v\n", err)
  32.                 return
  33.         }
  34.         defer listener.Close()
  35.         for {
  36.                 //等待别人连接
  37.                 conn, err := listener.Accept()
  38.                 if err != nil {
  39.                         fmt.Println("accept failed,err:%v\n", err)
  40.                         return
  41.                 }
  42.                 go process(conn)
  43.         }
  44. }
复制代码
参考出处:Go语言基础之网络编程 | 李文周的博客

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

河曲智叟

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表