国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > Golang > 正文

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

2020-04-01 19:00:41
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

前言

一般所謂的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é)議

go,tcp,粘包,go語(yǔ)言,tcp粘包處理

粘包處理原理:通過(guò)請(qǐng)求頭中數(shù)據(jù)包大小,將客戶(hù)端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ù)器端需要將客戶(hù)端3次發(fā)送數(shù)據(jù)進(jìn)行粘包處理

代碼示例

package serverimport ( "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")/* 客戶(hù)端結(jié)構(gòu)體 */type Client struct { DeviceID string  //客戶(hù)端連接的唯標(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è)客戶(hù)端數(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  //客戶(hù)端連接的唯標(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ì)VEVB武林網(wǎng)的支持。


發(fā)表評(píng)論 共有條評(píng)論
用戶(hù)名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 天全县| 红河县| 师宗县| 英超| 常熟市| 祁门县| 明水县| 齐河县| 青冈县| 武定县| 泰来县| 维西| 资中县| 潼关县| 莱西市| 安塞县| 秦皇岛市| 当涂县| 康乐县| 阜阳市| 吉隆县| 锡林浩特市| 霍城县| 天峻县| 盈江县| 东方市| 邛崃市| 仪陇县| 奈曼旗| 会泽县| 宜州市| 溧水县| 永昌县| 天全县| 福海县| 滁州市| 诸暨市| 定南县| 巩义市| 海丰县| 祁门县|