亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Golang > 正文

6行代碼快速解決golang TCP粘包問題

2020-04-01 18:58:41
字體:
來源:轉載
供稿:網友

前言

什么是TCPgolang/177809.html">粘包問題以及為什么會產生TCP粘包,本文不加討論。本文使用golang的bufio.Scanner來實現自定義協議解包。

下面話不多說了,來一起看看詳細的介紹吧。

協議數據包定義

本文模擬一個日志服務器,該服務器接收客戶端傳到的數據包并顯示出來

type Package struct { Version  [2]byte // 協議版本,暫定V1 Length   int16 // 數據部分長度 Timestamp  int64 // 時間戳 HostnameLength int16 // 主機名長度 Hostname  []byte // 主機名 TagLength  int16 // 標簽長度 Tag   []byte // 標簽 Msg   []byte // 日志數據}

協議定義部分沒有什么好講的,根據具體的業務邏輯定義即可。

數據打包

由于TCP協議是語言無關的協議,所以直接把協議數據包結構體發送到TCP連接中也是不可能的,只能發送字節流數據,所以需要自己實現數據編碼。所幸golang提供了binary來幫助我們實現網絡字節編碼。

func (p *Package) Pack(writer io.Writer) error { var err error err = binary.Write(writer, binary.BigEndian, &p.Version) err = binary.Write(writer, binary.BigEndian, &p.Length) err = binary.Write(writer, binary.BigEndian, &p.Timestamp) err = binary.Write(writer, binary.BigEndian, &p.HostnameLength) err = binary.Write(writer, binary.BigEndian, &p.Hostname) err = binary.Write(writer, binary.BigEndian, &p.TagLength) err = binary.Write(writer, binary.BigEndian, &p.Tag) err = binary.Write(writer, binary.BigEndian, &p.Msg) return err}

Pack方法的輸出目標為io.Writer,有利于接口擴展,只要實現了該接口即可編碼數據寫入。binary.BigEndian是字節序,本文暫時不討論,有需要的讀者可以自行查找資料研究。

數據解包

解包需要將TCP數據包解析到結構體中,接下來會講為什么需要添加幾個數據無關的長度字段。

func (p *Package) Unpack(reader io.Reader) error { var err error err = binary.Read(reader, binary.BigEndian, &p.Version) err = binary.Read(reader, binary.BigEndian, &p.Length) err = binary.Read(reader, binary.BigEndian, &p.Timestamp) err = binary.Read(reader, binary.BigEndian, &p.HostnameLength) p.Hostname = make([]byte, p.HostnameLength) err = binary.Read(reader, binary.BigEndian, &p.Hostname) err = binary.Read(reader, binary.BigEndian, &p.TagLength) p.Tag = make([]byte, p.TagLength) err = binary.Read(reader, binary.BigEndian, &p.Tag) p.Msg = make([]byte, p.Length-8-2-p.HostnameLength-2-p.TagLength) err = binary.Read(reader, binary.BigEndian, &p.Msg) return err}

由于主機名、標簽這種數據是不固定長度的,所以需要兩個字節來標識數據長度,否則讀取的時候只知道一個總的數據長度是無法區分主機名、標簽名、日志數據的。

數據包的粘包問題解決

上文只是解決了編碼/解碼問題,前提是收到的數據包沒有產生粘包問題,解決粘包就是要正確分割字節流中的數據。一般有以下做法:

  • 定長分隔(每個數據包最大為該長度) 缺點是數據不足時會浪費傳輸資源
  • 特定字符分隔(如rn) 缺點是如果正文中有rn就會導致問題
  • 在數據包中添加長度字段(本文采用的)

golang提供了bufio.Scanner來解決粘包問題。

scanner := bufio.NewScanner(reader) // reader為實現了io.Reader接口的對象,如net.Connscanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) { if !atEOF && data[0] == 'V' { // 由于我們定義的數據包頭最開始為兩個字節的版本號,所以只有以V開頭的數據包才處理  if len(data) > 4 { // 如果收到的數據>4個字節(2字節版本號+2字節數據包長度)   length := int16(0)   binary.Read(bytes.NewReader(data[2:4]), binary.BigEndian, &length) // 讀取數據包第3-4字節(int16)=>數據部分長度   if int(length)+4 <= len(data) { // 如果讀取到的數據正文長度+2字節版本號+2字節數據長度不超過讀到的數據(實際上就是成功完整的解析出了一個包)    return int(length) + 4, data[:int(length)+4], nil   }  } } return})// 打印接收到的數據包for scanner.Scan() { scannedPack := new(Package) scannedPack.Unpack(bytes.NewReader(scanner.Bytes())) log.Println(scannedPack)}

本文的核心就在于scanner.Split方法,該方法用來解析TCP數據包

完整源碼

package mainimport ( "bufio" "bytes" "encoding/binary" "fmt" "io" "log" "os" "time")type Package struct { Version  [2]byte // 協議版本 Length   int16 // 數據部分長度 Timestamp  int64 // 時間戳 HostnameLength int16 // 主機名長度 Hostname  []byte // 主機名 TagLength  int16 // Tag長度 Tag   []byte // Tag Msg   []byte // 數據部分長度}func (p *Package) Pack(writer io.Writer) error { var err error err = binary.Write(writer, binary.BigEndian, &p.Version) err = binary.Write(writer, binary.BigEndian, &p.Length) err = binary.Write(writer, binary.BigEndian, &p.Timestamp) err = binary.Write(writer, binary.BigEndian, &p.HostnameLength) err = binary.Write(writer, binary.BigEndian, &p.Hostname) err = binary.Write(writer, binary.BigEndian, &p.TagLength) err = binary.Write(writer, binary.BigEndian, &p.Tag) err = binary.Write(writer, binary.BigEndian, &p.Msg) return err}func (p *Package) Unpack(reader io.Reader) error { var err error err = binary.Read(reader, binary.BigEndian, &p.Version) err = binary.Read(reader, binary.BigEndian, &p.Length) err = binary.Read(reader, binary.BigEndian, &p.Timestamp) err = binary.Read(reader, binary.BigEndian, &p.HostnameLength) p.Hostname = make([]byte, p.HostnameLength) err = binary.Read(reader, binary.BigEndian, &p.Hostname) err = binary.Read(reader, binary.BigEndian, &p.TagLength) p.Tag = make([]byte, p.TagLength) err = binary.Read(reader, binary.BigEndian, &p.Tag) p.Msg = make([]byte, p.Length-8-2-p.HostnameLength-2-p.TagLength) err = binary.Read(reader, binary.BigEndian, &p.Msg) return err}func (p *Package) String() string { return fmt.Sprintf("version:%s length:%d timestamp:%d hostname:%s tag:%s msg:%s",  p.Version,  p.Length,  p.Timestamp,  p.Hostname,  p.Tag,  p.Msg, )}func main() { hostname, err := os.Hostname() if err != nil {  log.Fatal(err) } pack := &Package{  Version:  [2]byte{'V', '1'},  Timestamp:  time.Now().Unix(),  HostnameLength: int16(len(hostname)),  Hostname:  []byte(hostname),  TagLength:  4,  Tag:   []byte("demo"),  Msg:   []byte(("現在時間是:" + time.Now().Format("2006-01-02 15:04:05"))), } pack.Length = 8 + 2 + pack.HostnameLength + 2 + pack.TagLength + int16(len(pack.Msg)) buf := new(bytes.Buffer) // 寫入四次,模擬TCP粘包效果 pack.Pack(buf) pack.Pack(buf) pack.Pack(buf) pack.Pack(buf) // scanner scanner := bufio.NewScanner(buf) scanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {  if !atEOF && data[0] == 'V' {   if len(data) > 4 {    length := int16(0)    binary.Read(bytes.NewReader(data[2:4]), binary.BigEndian, &length)    if int(length)+4 <= len(data) {     return int(length) + 4, data[:int(length)+4], nil    }   }  }  return }) for scanner.Scan() {  scannedPack := new(Package)  scannedPack.Unpack(bytes.NewReader(scanner.Bytes()))  log.Println(scannedPack) } if err := scanner.Err(); err != nil {  log.Fatal("無效數據包") }}

寫在最后

golang作為一門強大的網絡編程語言,實現自定義協議是非常重要的,實際上實現自定義協議也不是很難,以下幾個步驟:

  • 數據包編碼
  • 數據包解碼
  • 處理TCP粘包問題
  • 斷線重連(可以使用心跳實現)(非必須)

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對VEVB武林網的支持。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
91免费人成网站在线观看18| 日本不卡高字幕在线2019| 精品久久久在线观看| 亚洲成人网久久久| 国产一区二区三区在线观看视频| 国产综合色香蕉精品| 亚洲欧美一区二区三区久久| 7m第一福利500精品视频| 日本高清久久天堂| 亚洲一二在线观看| 欧美诱惑福利视频| 88xx成人精品| 精品国产拍在线观看| 久久久999精品| 91精品国产一区| 日本高清+成人网在线观看| 国产香蕉精品视频一区二区三区| 日韩免费在线看| 高清日韩电视剧大全免费播放在线观看| 色综合久久中文字幕综合网小说| 日韩欧美在线网址| 久久精品视频播放| 伊人久久久久久久久久| 国产精品露脸自拍| 国产精品久久久久77777| 欧美重口另类videos人妖| 欧美在线视频一二三| 亚洲国产精品高清久久久| 成人性生交大片免费看小说| 国产91露脸中文字幕在线| 亚洲精品在线观看www| 国产一区在线播放| 欧美专区在线播放| 亚洲精品狠狠操| 国内精品一区二区三区四区| 国产精品久久久久久久久久久久久| 亚洲区bt下载| 精品视频在线导航| 国产精品99久久久久久久久| 亚洲欧美精品在线| 一本色道久久88综合日韩精品| 欧美中文字幕视频在线观看| 国产成+人+综合+亚洲欧美丁香花| 91产国在线观看动作片喷水| 午夜精品久久久久久久男人的天堂| 日韩亚洲欧美成人| 国外日韩电影在线观看| 久久精品亚洲94久久精品| 亚洲嫩模很污视频| 久久免费成人精品视频| 青草青草久热精品视频在线观看| 国产精品视频1区| 欧美香蕉大胸在线视频观看| 国语自产精品视频在线看抢先版图片| 久久人人97超碰精品888| 久久久久久伊人| 久久中文字幕在线| 午夜精品一区二区三区在线视| 欧美色图在线视频| 欧美在线免费看| 久久国产精品久久久久久| 91精品国产沙发| 日韩性生活视频| 欧美午夜精品伦理| 日韩中文字幕在线视频播放| 91在线无精精品一区二区| 亚洲国内精品在线| 国产精品尤物福利片在线观看| 国产专区精品视频| 亚洲在线视频福利| 久久亚洲影音av资源网| 日韩欧美国产免费播放| 日韩毛片中文字幕| 日韩免费视频在线观看| 亚洲欧美制服综合另类| 久久高清视频免费| 久久久久久久久综合| 97国产suv精品一区二区62| 亚洲精品按摩视频| 亚洲国产高清自拍| 日本一本a高清免费不卡| 中文字幕亚洲欧美日韩在线不卡| 91久久夜色精品国产网站| 国产中文欧美精品| 性欧美xxxx视频在线观看| 日韩欧美在线第一页| 综合网中文字幕| 久久高清视频免费| 国产女精品视频网站免费| 国产一区私人高清影院| 久久久精品美女| 欧美美最猛性xxxxxx| 亚洲国产成人爱av在线播放| 精品国产区一区二区三区在线观看| 日韩欧美aⅴ综合网站发布| 疯狂做受xxxx高潮欧美日本| 亚洲天天在线日亚洲洲精| 日韩精品中文字幕有码专区| 国产精品久久97| 久久精品影视伊人网| 成人自拍性视频| 日产精品99久久久久久| 成人免费午夜电影| 97在线视频免费观看| 国产精品久久精品| 日韩国产欧美精品一区二区三区| 久久av中文字幕| 亚洲精品免费一区二区三区| 亚洲精品www| 在线免费观看羞羞视频一区二区| 日韩综合视频在线观看| 欧美成人午夜激情在线| 日韩在线观看免费高清完整版| 一区二区三区日韩在线| 久久男人的天堂| 国产91精品视频在线观看| 久久久久久网址| 欧美性猛交xxxx久久久| 日韩经典中文字幕| 欧美在线播放视频| 欧美极品少妇全裸体| 日韩欧美第一页| 久久久久国产精品免费网站| 亚洲精品久久久久久久久久久久| 亚洲国产精品久久久| 爽爽爽爽爽爽爽成人免费观看| 大胆欧美人体视频| 在线观看成人黄色| 国产视频综合在线| 91久久精品国产91久久性色| 久久综合五月天| 26uuu亚洲伊人春色| 亚洲精品久久久一区二区三区| 亚洲性线免费观看视频成熟| 欧美午夜激情小视频| 亚洲国产精品999| 51色欧美片视频在线观看| 欧美裸体xxxxx| 91色中文字幕| 欧美另类69精品久久久久9999| 色先锋资源久久综合5566| 九九九热精品免费视频观看网站| 久久久久久999| 国产91在线播放九色快色| 久久久久久久一区二区| 国产成人综合亚洲| 97视频com| 日韩在线欧美在线| 精品视频一区在线视频| 亚洲国产精品久久精品怡红院| 久久久中精品2020中文| 国产精品99久久久久久白浆小说| 色偷偷噜噜噜亚洲男人| 成人免费福利在线| 久久精品99久久久香蕉| 亚洲激情中文字幕| 日韩成人激情影院| 国产第一区电影| 97国产在线视频| 国模私拍一区二区三区| 91免费观看网站| 亚洲女在线观看| 久久噜噜噜精品国产亚洲综合| 亚洲精品国产精品久久清纯直播|