最近在使用Golang编写套接字层,发现有时候接收端会一次读到多个数据包的问题。于是通过查阅资料,发现这个就是传说中TCP的粘包问题。下面通过编写代码来重现这个问题:
服务端代码服务器/主要。去
函数main () { l,犯错:=净。听(“tcp”、“: 4044”) 如果犯错!=nil { 恐慌(err) } fmt。Println(“听4044”) 为{//监听到新的连接,创建新的goroutine交给handleConn函数处理 康涅狄格州,犯错:=l.Accept () 如果犯错!=nil { fmt。Println(“康涅狄格州犯错:“犯错) 其他}{ 去handleConn(康涅狄格州) } } } func handleConn(康涅狄格州net.Conn) { 推迟conn.Close () 推迟fmt.Println(“关闭”) fmt。Println(“新连接:“,conn.RemoteAddr ()) 结果:=bytes.NewBuffer(零) var buf[1024]字节 为{ n,犯错:=conn.Read (buf [0:]) result.Write (buf (0: n)) 如果犯错!=nil { 如果做错了==OF { 继续 其他}{ fmt。Println(“读错:“犯错) 打破 } 其他}{ fmt。Println (“recv:“, result.String ()) } result.Reset () } }
函数main () { 数据:=[]字节(“这里才是一个完整的数据包]”) 康涅狄格州,犯错:=净。DialTimeout (“tcp”、“localhost: 4044”, time.Second * 30) 如果犯错!=nil { fmt。Printf(“连接失败,犯错:% v \ n”, err.Error ()) 返回 } 我:=0;我& lt; 1000;我+ + { _,呃=conn.Write(数据) 如果犯错!=nil { fmt。Printf("写失败,错误:% v \ n”,呃) 打破 } } }
<>强运行结果强>
4044年听
引用>
新连接:[::1]:53079
recv:[这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据& # 65533;
recv: & # 65533;][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包]
recv:[这里才是一个完整的数据包]
recv:[这里才是一个完整的数据包]
recv:[这里才是一个完整的数据包][这里才是一个完整的数据包][这里才是一个完整的数据包]
recv:[这里才是一个完整的数据包]
…省略其它的…
从服务端的控制台输出可以看的出,存在三种类型的输出:
<李>一种是正常的一个数据包输出。李> <李>一种是多个数据包“粘”在了一起,我们定义这种读到的包为粘包。李> <李>一种是一个数据包被“拆”开,形成一个破碎的包,我们定义这种包为半包。李>
<强>为什么会出现半包和粘包吗? 强>
<李>客户端一段时间内发送包的速度太多,服务端没有全部处理完。于是数据就会积压起来,产生粘包。李> <李>定义的读的缓冲区不够大,而数据包太大或者由于粘包产生,服务端不能一次全部读完,产生半包。李>
<强>什么时候需要考虑处理半包和粘包吗? 强>
TCP连接是长连接,即一次连接多次发送数据。
每次发送的数据是结构的,比如JSON格式的数据或者数据包的协议是由我们自己定义的(包头部包含实际数据长度,协议魔数等)。<>强解决思路强>
<李>定长分隔(每个数据包最大为该长度,不足时使用特殊字符填充),但是数据不足时会浪费传输资源李
Golang TCP粘包拆包问题的解决方法