golang中解決tcp傳輸中的粘包問題

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

golang中解決tcp傳輸中的粘包問題

Author: 嶽東衛
Email: usher.yue@gmail.com

什麼是粘包?

最近在寫https://github.com/UsherYue/ActivedRouter (一個http/https反向 Proxy服務)的時候遇到了粘包問題,

如果有做過網路編程的小夥伴應該都知道粘包問題,舉個例子: 比如用戶端在和服

務器進行通訊採用的是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封裝代碼如下,可直接使用:

package packetimport (    "bytes"    "encoding/binary")const (    DEFAULE_HEADER           = "[**********]"    DEFAULT_HEADER_LENGTH    = 12    DEFAULT_SAVE_DATA_LENGTH = 4)type Packet struct {    Header         string    HeaderLengh    int32    SaveDataLength int32    Data           []byte}//set delimiter headerfunc (self *Packet) SetHeader(header string) *Packet {    self.Header = header    self.HeaderLengh = int32(len([]byte(header)))    return self}//create default packagefunc NewDefaultPacket(data []byte) *Packet {    return &Packet{DEFAULE_HEADER, DEFAULT_HEADER_LENGTH, DEFAULT_SAVE_DATA_LENGTH, data}}//convert to net packagefunc (self *Packet) Packet() []byte {    return append(append([]byte(self.Header), self.IntToBytes(int32(len(self.Data)))...), self.Data...)}//return value is sticky datafunc (self *Packet) UnPacket(readerChannel chan []byte) []byte {    dataLen := int32(len(self.Data))    var i int32    for i = 0; i < dataLen; i++ {        //Termiate for loop when the remaining data is insufficient .        if dataLen < 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            if dataLen < i+self.HeaderLengh+self.SaveDataLength+actualDataLen {                break            }            //Get a packet            packageData := self.Data[saveDataLenBeginIndex+self.SaveDataLength : saveDataLenBeginIndex+self.SaveDataLength+actualDataLen]            //send pacakge data to reader channel            readerChannel <- packageData            //get next package index            i += self.HeaderLengh + self.SaveDataLength + actualDataLen - 1        }    }    //Reach the end    if i >= dataLen {        return []byte{}    }    //Returns the remaining data    return self.Data[i:]}func (self *Packet) IntToBytes(i int32) []byte {    byteBuffer := bytes.NewBuffer([]byte{})    binary.Write(byteBuffer, binary.BigEndian, i)    return byteBuffer.Bytes()}func (self *Packet) BytesToInt(data []byte) int32 {    var val int32    byteBuffer := bytes.NewBuffer(data)    binary.Read(byteBuffer, binary.BigEndian, &val)    return val}

Client實現虛擬碼代碼如下:

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

Server實現虛擬碼代碼如下:

  //Declare a pipe for receiving unpacked data    readerChannel := make(chan []byte, 1024)    //Store truncated data    remainBuffer := make([]byte, 0)    //read unpackage data from buffered channel    go func(reader chan []byte) {        for {            packageData := <-reader            //....balabala....        }    }(readerChannel)  remainBuffer =   NewDefaultPacket(append(remainBuffer,recvData)).UnPacket(readerChannel)
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.