golang 解决tcp逻辑粘包以及对协议流大小的约束逻辑
原创
©著作权归作者所有:来自51CTO博客作者fwhezfwhez的原创作品,请联系作者获取转载授权,否则将追究法律责任
tcp数据流在接收方的buf大小不足时,可能会出现逻辑粘包,可以使用buffer确保接完
以前在限制单条流消息大小时,是手动用[]byte实现了一个buffer,单条请求里,每次读到EOF前的n大小,一超过阈值,就报警拒绝,这个做法其实很蠢,至少会在读取刀阈值以前,全接受。
实际上更好的逻辑是,用指定长度的[]byte去读取一些定长的头部信息
比如假设协议是:
[4]byte 总长度
[4]byte 头部长度
[4]byte body长度
[]byte 头部
[]byte body
那么,与其读取过程中去动态判读是否超值,更好的方法是用[4]byte 接受总长度
如果 总长度的值 >阈值,直接报警拒绝,则该逻辑对非法请求最多就是读取12位,而不是读到阈值大小。
一般总长度是不包括自身的[4]byte的,所以严格逻辑是 总长度的值>阈值+4,但是这种限制是弱一致的,不碍事!
具体的实现如下:
func UnpackToBlockFromReader(reader io.Reader, maxLength int32) ([]byte, error) {
if reader == nil {
return nil, errors.New("reader is nil")
}
var info = make([]byte, 4, 4)
n, e := reader.Read(info)
if e != nil {
return nil, e
}
if n != 4 {
return nil, errors.New("can't read 4 length info block from reader, but read " + strconv.Itoa(n))
}
length:= binary.BigEndian.Uint32(info[0:4])
if length >= maxLength {
return info, errors.New(fmt.Sprintf("beyond max size limit %d but got %d", maxLength, length))
}
var content = make([]byte, length, length)
n, e = reader.Read(content)
if e != nil {
return nil, e
}
if n != int(length) {
return nil, errors.New(fmt.Sprintf("can't read %d length content block from reader, but read %d", length, n))
}
return append(info, content ...), nil
}