原文链接: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,进行错误处理,比如可能网络出错需要重新连接。