Golang中如何处理tcp粘包

原文链接:Golang中如何处理tcp粘包
发布时间:2021-09-05 18:05:37

文章目录

为什么要处理粘包

由于tcp协议是数据流传输,一次读数据不一定能得到一个完整的业务数据包,所以需要进行粘包处理,保证要处理的数据是一个或者多个完成的业务数据包。

处理粘包的方法

假设:

  • 包的格式为 四字节包头 + 包体, 包头指明包体的长度
  • 大端传输数据

处理过程:

  • 先读取四字节包头,解析出包头的长度bodyLen
  • 再读取bodyLen长度的包体,这次读取的数据就是一个完整的业务包bodyData
  • 把bodyData返回到业务层处理

处理代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
```go
const (
MAX_CONN_TIMEOUT = 30
PACKET_HEAD_LEN = 4
)

func Recv(conn net.Conn) ([]byte, error) {
//读取包头
head := make([]byte, PACKET_HEAD_LEN)
_ = c.Conn.SetReadDeadline(time.Now().Add(time.Second * MAX_CONN_TIMEOUT))
_, err := io.ReadFull(conn, head)
if nil != err {
log.Error("io.ReadFull() failed, error[%s]", err.Error())
return nil, err
}
bodyLen := binary.BigEndian.Uint32(head)

//读取包体
bodyData := make([]byte, bodyLen)
_ = c.Conn.SetReadDeadline(time.Now().Add(time.Second * MAX_CONN_TIMEOUT))
_, err = io.ReadFull(conn, bodyData)
if nil != err {
log.Error("io.ReadFull() failed, error[%s]", err.Error())
return nil, err
}

return bodyData, nil
}

总结

  • 每调用一次Recv函数就可以得到一个完整的业务包,返回到业务层进行处理
  • 代码中设置了超时时间,这个根据实际业务需要来设置,有些业务场景不设置也可以。如果不设置,那么读不够数据就会一直阻塞在那里。关于设置的超时时间的大小,也是根据实际业务场景,比如有的客户端与服务端之间是10秒一次心跳,那么超时时间设置就要大于10秒,比如20秒或者30秒等等。
  • Recv返回后,应当判断error是否为nil,进行错误处理,比如可能网络出错需要重新连接。