最近在写 https://github.com/UsherYue/ActivedRouter (一个 http/https 反向代理服务) 的时候遇到了粘包问题,
如果有做过网络编程的小伙伴应该都知道粘包问题, 举个例子: 比如客户端在和服
务器进行通信采用的是 json 格式的数据包。那么此时 Client 和 Server 的数据交互流程应该如下:
Client Send Json Data-> 经过网络 ->Server Reveive Data->Server Decode Json ->Done (一次交互只有一个 Json 数据包)
上述流程我们假设从客户端发送到服务器接收这个一次的性动作中间交互的数
据是一个完成的 json 数据包,因此我们的程序可以正常工作。
但是实际情况并不是我们想的这样, 由于 TCP 协议的特点、以及网络环境的复杂
多变、以及服务器对客户端的数据接收处理不及时等等原因,会导致网络传输
过程中出现粘包。 也就是说在服务器进行一次数据读取的时候,我们假想这
个数据包是一个完整的 json 数据包,但是实际上他确实 ,2 个 Json 数据包、3
个 json 数据包、2.5 个 json 数据包,这就是我们所说的粘包。 如果你还不能理解那么看下图。
我们在开发过程中通常会在 server 端接收数据的时候定义一个固定长度的 buffer 来存储从客户端连接发来的数据包 ,然后对这个数据包进行反序列化, 所以要解决这个问题我们就要从收发数据的时候做一些手脚, 思路如下:
Client Send Json Data-> 调用封装方法将数据封装成固定格式的 Packet-> 经过网络 ->Server Reveive Data-> 调用解封装方法取出粘包 packet 中所有 json 数据包, 并将剩余截断数据和下一次到来的数据包进行拼接 ->Server Decode Json ->Done (一次交互只有一个 Json 数据包)
我在 golang 中实现了一个 Packet 封装代码如下, 可直接使用:
- packagepacketimport("bytes"
- "encoding/binary")const(
- DEFAULE_HEADER ="[**********]"DEFAULT_HEADER_LENGTH = 12DEFAULT_SAVE_DATA_LENGTH = 4)typePacketstruct{
- HeaderstringHeaderLenghint32SaveDataLengthint32Data []byte}//set delimiter header
- func(self *Packet) SetHeader(headerstring) *Packet {
- self.Header = header
- self.HeaderLengh =int32(len([]byte(header)))returnself
- }//create default package
- funcNewDefaultPacket(data []byte) *Packet {return&Packet{DEFAULE_HEADER, DEFAULT_HEADER_LENGTH, DEFAULT_SAVE_DATA_LENGTH, data}
- }//convert to net package
- func(self *Packet) Packet() []byte{return append(append([]byte(self.Header), self.IntToBytes(int32(len(self.Data)))...), self.Data...)
- }//return value is sticky data
- func(self *Packet) UnPacket(readerChannelchan[]byte) []byte{
- dataLen :=int32(len(self.Data))variint32
- fori = 0; i < dataLen; i++ {//Termiate for loop when the remaining data is insufficient .
- ifdataLen < i+self.HeaderLengh+self.SaveDataLength {break}//find Header
- if string(self.Data[i:i+self.HeaderLengh]) == self.Header {
- saveDataLenBeginIndex := i + self.HeaderLengh
- actualDataLen := self.BytesToInt(self.Data[saveDataLenBeginIndex : saveDataLenBeginIndex+self.SaveDataLength])//The remaining data is less than one package
- ifdataLen < i+self.HeaderLengh+self.SaveDataLength+actualDataLen {break}//Get a packetpackageData := self.Data[saveDataLenBeginIndex+self.SaveDataLength : saveDataLenBeginIndex+self.SaveDataLength+actualDataLen]//send pacakge data to reader channelreaderChannel <- packageData//get next package indexi += self.HeaderLengh + self.SaveDataLength + actualDataLen - 1}
- }//Reach the end
- ifi >= dataLen {return[]byte{}
- }//Returns the remaining data
- returnself.Data[i:]
- }func(self *Packet) IntToBytes(iint32) []byte{
- byteBuffer := bytes.NewBuffer([]byte{})
- binary.Write(byteBuffer, binary.BigEndian, i)returnbyteBuffer.Bytes()
- }func(self *Packet) BytesToInt(data []byte)int32{varvalint32byteBuffer := bytes.NewBuffer(data)
- binary.Read(byteBuffer, binary.BigEndian, &val)returnval
- }
Client 实现伪代码代码如下:
- dataPackage := NewDefaultPacket([]byte(jsonString)).Packet()
- Client.Write(dataPackage)
Server 实现伪代码代码如下:
- //Declare a pipe for receiving unpacked datareaderChannel :=make(chan[]byte, 1024)//Store truncated dataremainBuffer :=make([]byte, 0)//read unpackage data from buffered channel
- go func(readerchan[]byte) {for{
- packageData := <-reader//....balabala....}
- }(readerChannel)
- remainBuffer = NewDefaultPacket(append(remainBuffer,recvData)).UnPacket(readerChannel)
来源: http://blog.csdn.net/yue7603835/article/details/72511713