佳木斯湛栽影视文化发展公司

主頁(yè) > 知識(shí)庫(kù) > GO語(yǔ)言如何手動(dòng)處理TCP粘包詳解

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

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

前言

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

應(yīng)用場(chǎng)景

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

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

例如:

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

協(xié)議規(guī)定一個(gè)數(shù)據(jù)包最大是512字節(jié),請(qǐng)求頭中數(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é)長(zhǎng)度:%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ù),無(wú)指令頭...\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("消息體長(zhǎng)度:%d\n", p.Len)
  var bodyLength = len(this.Data)  
  /**
   判斷數(shù)據(jù)包緩沖區(qū)的大小是否小于協(xié)議請(qǐng)求頭中數(shù)據(jù)包大小
   如果小于,等待讀取下一個(gè)客戶端數(shù)據(jù)包,否則對(duì)數(shù)據(jù)包解碼進(jìn)行業(yè)務(wù)邏輯處理
   */
  if int(p.Len) > len(this.Data) - protocol.HEADER_SIZE {
   fmt.Printf("body體長(zhǎng)度:%d,讀取的body體長(zhǎng)度:%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)庫(kù)文檔:https://studygolang.com/pkgdoc

總結(jié)

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

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

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

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

    • 400-1100-266
    淮阳县| 林甸县| 赫章县| 昭苏县| 江山市| 灵璧县| 清水河县| 兴义市| 北安市| 广南县| 汝南县| 景宁| 松阳县| 锦屏县| 余干县| 泽普县| 鞍山市| 宁远县| 宁乡县| 沙坪坝区| 洪江市| 万宁市| 龙川县| 江川县| 临泽县| 宁南县| 水城县| 荥阳市| 定州市| 保定市| 大关县| 永康市| 榆林市| 吴忠市| 汤原县| 红河县| 平邑县| 客服| 沙雅县| 元朗区| 大埔区|