golang中解决tcp传输中的粘包问题

前端之家收集整理的这篇文章主要介绍了golang中解决tcp传输中的粘包问题前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

golang中解决tcp传输中的粘包问题

Author: 岳东卫
Email: usher.yue@gmail.com

什么是粘包?

最近在写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封装代码如下,可直接使用:

  1. package packet
  2.  
  3. import (
  4. "bytes"
  5. "encoding/binary"
  6. )
  7.  
  8. const (
  9. DEFAULE_HEADER = "[**********]"
  10. DEFAULT_HEADER_LENGTH = 12
  11. DEFAULT_SAVE_DATA_LENGTH = 4
  12. )
  13.  
  14. type Packet struct {
  15. Header string
  16. HeaderLengh int32
  17. SaveDataLength int32
  18. Data []byte
  19. }
  20.  
  21. //set delimiter header
  22. func (self *Packet) SetHeader(header string) *Packet {
  23. self.Header = header
  24. self.HeaderLengh = int32(len([]byte(header)))
  25. return self
  26. }
  27.  
  28. //create default package
  29. func NewDefaultPacket(data []byte) *Packet {
  30. return &Packet{DEFAULE_HEADER,DEFAULT_HEADER_LENGTH,DEFAULT_SAVE_DATA_LENGTH,data}
  31. }
  32.  
  33. //convert to net package
  34. func (self *Packet) Packet() []byte {
  35. return append(append([]byte(self.Header),self.IntToBytes(int32(len(self.Data)))...),self.Data...)
  36. }
  37.  
  38. //return value is sticky data
  39. func (self *Packet) UnPacket(readerChannel chan []byte) []byte {
  40. dataLen := int32(len(self.Data))
  41. var i int32
  42. for i = 0; i < dataLen; i++ {
  43. //Termiate for loop when the remaining data is insufficient .
  44. if dataLen < i+self.HeaderLengh+self.SaveDataLength {
  45. break
  46. }
  47. //find Header
  48. if string(self.Data[i:i+self.HeaderLengh]) == self.Header {
  49. saveDataLenBeginIndex := i + self.HeaderLengh
  50. actualDataLen := self.BytesToInt(self.Data[saveDataLenBeginIndex : saveDataLenBeginIndex+self.SaveDataLength])
  51. //The remaining data is less than one package
  52. if dataLen < i+self.HeaderLengh+self.SaveDataLength+actualDataLen {
  53. break
  54. }
  55. //Get a packet
  56. packageData := self.Data[saveDataLenBeginIndex+self.SaveDataLength : saveDataLenBeginIndex+self.SaveDataLength+actualDataLen]
  57. //send pacakge data to reader channel
  58. readerChannel <- packageData
  59. //get next package index
  60. i += self.HeaderLengh + self.SaveDataLength + actualDataLen - 1
  61. }
  62. }
  63. //Reach the end
  64. if i >= dataLen {
  65. return []byte{}
  66. }
  67. //Returns the remaining data
  68. return self.Data[i:]
  69. }
  70.  
  71. func (self *Packet) IntToBytes(i int32) []byte {
  72. byteBuffer := bytes.NewBuffer([]byte{})
  73. binary.Write(byteBuffer,binary.BigEndian,i)
  74. return byteBuffer.Bytes()
  75. }
  76.  
  77. func (self *Packet) BytesToInt(data []byte) int32 {
  78. var val int32
  79. byteBuffer := bytes.NewBuffer(data)
  80. binary.Read(byteBuffer,&val)
  81. return val
  82. }

Client实现伪代码代码如下:

  1. dataPackage := NewDefaultPacket([]byte(jsonString)).Packet()
  2. Client.Write(dataPackage)

Server实现伪代码代码如下:

  1. //Declare a pipe for receiving unpacked data
  2. readerChannel := make(chan []byte, 1024)
  3. //Store truncated data
  4. remainBuffer := make([]byte, 0)
  5. //read unpackage data from buffered channel
  6. go func(reader chan []byte) {
  7. for {
  8. packageData := <-reader
  9. //....balabala....
  10. }
  11. }(readerChannel)
  12. remainBuffer = NewDefaultPacket(append(remainBuffer,recvData)).UnPacket(readerChannel)

猜你在找的Go相关文章