主頁 > 知識庫 > GO語言如何手動(dòng)處理TCP粘包詳解

GO語言如何手動(dòng)處理TCP粘包詳解

熱門標(biāo)簽:地方門戶網(wǎng)站 服務(wù)外包 網(wǎng)站排名優(yōu)化 呼叫中心市場需求 鐵路電話系統(tǒng) 百度競價(jià)排名 AI電銷 Linux服務(wù)器

前言

一般所謂的TCP粘包是在一次接收數(shù)據(jù)不能完全地體現(xiàn)一個(gè)完整的消息數(shù)據(jù)。TCP通訊為何存在粘包呢?主要原因是TCP是以流的方式來處理數(shù)據(jù),再加上網(wǎng)絡(luò)上MTU的往往小于在應(yīng)用處理的消息數(shù)據(jù),所以就會(huì)引發(fā)一次接收的數(shù)據(jù)無法滿足消息的需要,導(dǎo)致粘包的存在。處理粘包的唯一方法就是制定應(yīng)用層的數(shù)據(jù)通訊協(xié)議,通過協(xié)議來規(guī)范現(xiàn)有接收的數(shù)據(jù)是否滿足消息數(shù)據(jù)的需要。在應(yīng)用中處理粘包的基礎(chǔ)方法主要有兩種分別是以4節(jié)字描述消息大小或以結(jié)束符,實(shí)際上也有兩者相結(jié)合的如HTTP,redis的通訊協(xié)議等。

應(yīng)用場景

大部分TCP通訊場景下,使用自定義通訊協(xié)議

粘包處理原理:通過請求頭中數(shù)據(jù)包大小,將客戶端N次發(fā)送的數(shù)據(jù)緩沖到一個(gè)數(shù)據(jù)包中

例如:

請求頭占3個(gè)字節(jié)(指令頭1字節(jié)、數(shù)據(jù)包長度2字節(jié)),版本占1個(gè)字節(jié),指令占2個(gè)字節(jié)

協(xié)議規(guī)定一個(gè)數(shù)據(jù)包最大是512字節(jié),請求頭中數(shù)據(jù)包記錄是1300字節(jié),完整的數(shù)據(jù)包是1307個(gè)字節(jié),此時(shí)服務(wù)器端需要將客戶端3次發(fā)送數(shù)據(jù)進(jìn)行粘包處理

代碼示例

package server
import (
 "net"
 "bufio"
 "ftj-data-synchro/protocol"
 "golang.org/x/text/transform"
 "golang.org/x/text/encoding/simplifiedchinese"
 "io/ioutil"
 "bytes"
 "ftj-data-synchro/logic"
 "fmt"
 "strconv"
)
/*
 客戶端結(jié)構(gòu)體
 */
type Client struct {
 DeviceID string  //客戶端連接的唯標(biāo)志
 Conn  net.Conn  //連接
 reader *bufio.Reader //讀取
 writer *bufio.Writer //輸出
 Data  []byte  //接收數(shù)據(jù)
}
func NewClient(conn *net.TCPConn) *Client {
 reader := bufio.NewReaderSize(conn, 10240)
 writer := bufio.NewWriter(conn)
 c := Client{Conn:conn, reader:reader, writer:writer}
 return c
}
/**
 數(shù)據(jù)讀?。ㄕ嘲幚恚?
 */
func (this *Client)read() {
 for {
  var data []byte
  var err error
  //讀取指令頭 返回輸入流的前4個(gè)字節(jié),不會(huì)移動(dòng)讀取位置
  data, err = this.reader.Peek(4)
  if len(data) == 0 || err != nil {
   continue
  }
  //返回緩沖中現(xiàn)有的可讀取的字節(jié)數(shù)
  var byteSize = this.reader.Buffered()
  fmt.Printf("讀取字節(jié)長度:%d\n", byteSize)
  //生成一個(gè)字節(jié)數(shù)組,大小為緩沖中可讀字節(jié)數(shù)
  data = make([]byte, byteSize)
  //讀取緩沖中的數(shù)據(jù)
  this.reader.Read(data)
  fmt.Printf("讀取字節(jié):%d\n", data)
  //保存到新的緩沖區(qū)
  for _, v := range data {
   this.Data = append(this.Data, v)
  }
  if len(this.Data)  4 {
   //數(shù)據(jù)包緩沖區(qū)清空
   this.Data = []byte{}
   fmt.Printf("非法數(shù)據(jù),無指令頭...\n")
   continue
  }
  data, err = protocol.HexBytesToBytes(this.Data[:4])
  instructHead, _ := strconv.ParseUint(string(data), 16, 16)
  //指令頭效驗(yàn)
  if uint16(instructHead) != 42330 {
   fmt.Printf("非法數(shù)據(jù)\n")
   //數(shù)據(jù)包緩沖區(qū)清空
   this.Data = []byte{}
   continue
  }
  data = this.Data[:protocol.HEADER_SIZE]
  var p = protocol.Decode(data)
  fmt.Printf("消息體長度:%d\n", p.Len)
  var bodyLength = len(this.Data)  
  /**
   判斷數(shù)據(jù)包緩沖區(qū)的大小是否小于協(xié)議請求頭中數(shù)據(jù)包大小
   如果小于,等待讀取下一個(gè)客戶端數(shù)據(jù)包,否則對數(shù)據(jù)包解碼進(jìn)行業(yè)務(wù)邏輯處理
   */
  if int(p.Len) > len(this.Data) - protocol.HEADER_SIZE {
   fmt.Printf("body體長度:%d,讀取的body體長度:%d\n", p.Len, bodyLength)
   continue
  }
  fmt.Printf("實(shí)際處理字節(jié):%v\n", this.Data)
  p = protocol.Decode(this.Data)
  //邏輯處理
  go this.logicHandler(p)
  //數(shù)據(jù)包緩沖區(qū)清空
  this.Data = []byte{}
 }
}

待優(yōu)化部分:

type Client struct {
 DeviceID string  //客戶端連接的唯標(biāo)志
 Conn  net.Conn  //連接
 reader *bufio.Reader //讀取
 writer *bufio.Writer //輸出
 Data  []byte  //接收數(shù)據(jù)
}

結(jié)構(gòu)體中Data屬性可考慮使用bytes.Buffer實(shí)現(xiàn)。

Golang標(biāo)準(zhǔn)庫文檔:https://studygolang.com/pkgdoc

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。

您可能感興趣的文章:
  • Go語言服務(wù)器開發(fā)之簡易TCP客戶端與服務(wù)端實(shí)現(xiàn)方法
  • GO語言實(shí)現(xiàn)簡單TCP服務(wù)的方法
  • golang之tcp自動(dòng)重連實(shí)現(xiàn)方法
  • 利用Golang實(shí)現(xiàn)TCP連接的雙向拷貝詳解
  • 6行代碼快速解決golang TCP粘包問題
  • Go語言中TCP/IP網(wǎng)絡(luò)編程的深入講解

標(biāo)簽:湖南 崇左 黃山 銅川 蘭州 衡水 湘潭 仙桃

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《GO語言如何手動(dòng)處理TCP粘包詳解》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266