亚洲综合原千岁中文字幕_国产精品99久久久久久久vr_无码人妻aⅴ一区二区三区浪潮_成人h动漫精品一区二区三

主頁 > 知識庫 > 6行代碼快速解決golang TCP粘包問題

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

熱門標簽:釘釘有地圖標注功能嗎 建造者2地圖標注 浙江高頻外呼系統(tǒng)多少錢一個月 汕頭小型外呼系統(tǒng) 濱州自動電銷機器人排名 鄭州亮點科技用的什么外呼系統(tǒng) 黃岡人工智能電銷機器人哪個好 惠州電銷防封電話卡 阿里云ai電話機器人

前言

什么是TCP粘包問題以及為什么會產(chǎn)生TCP粘包,本文不加討論。本文使用golang的bufio.Scanner來實現(xiàn)自定義協(xié)議解包。

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

協(xié)議數(shù)據(jù)包定義

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

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

協(xié)議定義部分沒有什么好講的,根據(jù)具體的業(yè)務邏輯定義即可。

數(shù)據(jù)打包

由于TCP協(xié)議是語言無關的協(xié)議,所以直接把協(xié)議數(shù)據(jù)包結構體發(fā)送到TCP連接中也是不可能的,只能發(fā)送字節(jié)流數(shù)據(jù),所以需要自己實現(xiàn)數(shù)據(jù)編碼。所幸golang提供了binary來幫助我們實現(xiàn)網(wǎng)絡字節(jié)編碼。

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,有利于接口擴展,只要實現(xiàn)了該接口即可編碼數(shù)據(jù)寫入。binary.BigEndian是字節(jié)序,本文暫時不討論,有需要的讀者可以自行查找資料研究。

數(shù)據(jù)解包

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

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
}

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

數(shù)據(jù)包的粘包問題解決

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

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

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

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

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

完整源碼

package main
import (
 "bufio"
 "bytes"
 "encoding/binary"
 "fmt"
 "io"
 "log"
 "os"
 "time"
)

type Package struct {
 Version  [2]byte // 協(xié)議版本
 Length   int16 // 數(shù)據(jù)部分長度
 Timestamp  int64 // 時間戳
 HostnameLength int16 // 主機名長度
 Hostname  []byte // 主機名
 TagLength  int16 // Tag長度
 Tag   []byte // Tag
 Msg   []byte // 數(shù)據(jù)部分長度
}

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(("現(xiàn)在時間是:" + 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("無效數(shù)據(jù)包")
 }
}

寫在最后

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

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

總結

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

您可能感興趣的文章:
  • python TCP Socket的粘包和分包的處理詳解
  • C#中TCP粘包問題的解決方法
  • 使用Netty搭建服務端和客戶端過程詳解
  • spring+netty服務器搭建的方法
  • Spring Boot實戰(zhàn)之netty-socketio實現(xiàn)簡單聊天室(給指定用戶推送消息)
  • 使用Netty解決TCP粘包和拆包問題過程詳解

標簽:瀘州 滄州 東營 昭通 泰安 阿壩 駐馬店 晉中

巨人網(wǎng)絡通訊聲明:本文標題《6行代碼快速解決golang TCP粘包問題》,本文關鍵詞  6行,代碼,快速,解決,golang,;如發(fā)現(xiàn)本文內容存在版權問題,煩請?zhí)峁┫嚓P信息告之我們,我們將及時溝通與處理。本站內容系統(tǒng)采集于網(wǎng)絡,涉及言論、版權與本站無關。
  • 相關文章
  • 下面列出與本文章《6行代碼快速解決golang TCP粘包問題》相關的同類信息!
  • 本頁收集關于6行代碼快速解決golang TCP粘包問題的相關信息資訊供網(wǎng)民參考!
  • 推薦文章
    黄色免费三级| 精品在线视频播放| 欧美日本二区| 久久精品大片| 久久久成人网| 免费一级片在线| 精品国产一区二区三区久| 久久久久久久久综合影视网| 成人影视在线播放| 日韩在线观看视频免费| 韩国毛片| 久久精品大片| 精品国产一区二区三区精东影业 | 九九免费高清在线观看视频| 一本高清在线| 国产麻豆精品高清在线播放| 日韩专区在线播放| 精品视频一区二区三区免费| 国产精品1024在线永久免费| 国产麻豆精品免费视频| 国产综合91天堂亚洲国产| 亚洲 激情| 精品视频在线看| 一本高清在线| 日韩一级黄色片| 国产一区二区精品| 精品国产亚一区二区三区| 九九九网站| 麻豆系列 在线视频| 天天色成人| 欧美大片一区| 日韩免费在线视频| 国产亚洲精品成人a在线| 欧美激情一区二区三区在线播放 | 日韩免费在线| 91麻豆爱豆果冻天美星空| 日韩免费在线| 国产一区二区精品久久91| 欧美日本二区| 亚欧乱色一区二区三区| 欧美18性精品| 免费一级片在线| 日韩专区亚洲综合久久| 亚洲第一色在线| 国产精品1024在线永久免费| 国产亚洲精品成人a在线| 国产不卡高清在线观看视频| 国产韩国精品一区二区三区| 韩国三级视频网站| 日韩av片免费播放| 欧美国产日韩精品| 欧美一区二区三区在线观看| 精品在线视频播放| 国产亚洲男人的天堂在线观看| 欧美一区二区三区在线观看| 国产麻豆精品高清在线播放| 免费一级生活片| 九九干| 黄视频网站在线看| 国产不卡福利| 日韩在线观看视频免费| 欧美一区二区三区在线观看| 日韩专区第一页| 久久国产精品永久免费网站| 久久99这里只有精品国产| 日本特黄特黄aaaaa大片| 九九热国产视频| 国产不卡在线观看视频| 国产一区二区精品久久91| 国产视频久久久久| 国产一区二区精品在线观看| 国产成+人+综合+亚洲不卡| 青青久久精品| 亚飞与亚基在线观看| 国产国语对白一级毛片| 黄视频网站在线看| 精品视频在线看| 黄视频网站免费| 九九精品久久| 精品视频在线看| 久久国产影视免费精品| 国产网站免费视频| 香蕉视频久久| 久久99中文字幕久久| 国产a毛片| 久久99这里只有精品国产| 青青青草视频在线观看| 亚欧成人乱码一区二区| 韩国毛片免费| 黄视频网站免费| 欧美另类videosbestsex久久| 国产韩国精品一区二区三区| 精品久久久久久综合网| 精品国产香蕉伊思人在线又爽又黄| 四虎影视久久| 深夜做爰性大片中文| 精品视频在线看| 国产激情一区二区三区| 欧美一级视频高清片| 999精品视频在线| 国产亚洲男人的天堂在线观看| 午夜家庭影院| 精品在线观看国产| a级毛片免费观看网站| 国产精品自拍在线观看| 青青青草视频在线观看| 精品久久久久久中文| 麻豆污视频| 日韩专区在线播放| 99久久视频| 日韩专区亚洲综合久久| 青青久久精品| 人人干人人草| 午夜家庭影院| 日韩专区亚洲综合久久| 国产一区二区福利久久| 久久精品大片| 欧美激情伊人| 欧美日本免费| 高清一级毛片一本到免费观看| 国产成人女人在线视频观看| 日本伦理片网站| a级黄色毛片免费播放视频| 欧美日本二区| 国产不卡福利| 久久精品大片| 美女免费毛片| 九九精品在线| 成人高清免费| a级毛片免费观看网站| 九九久久国产精品大片| 麻豆系列 在线视频| 成人免费观看男女羞羞视频| 国产网站免费视频| 久久久久久久久综合影视网| 国产福利免费视频| 香蕉视频三级| 国产麻豆精品高清在线播放| 久久成人亚洲| 午夜在线亚洲| 韩国三级一区| 国产一区二区精品久久91| 久久国产影视免费精品| 精品视频在线观看一区二区| 日本特黄特色aa大片免费| 日韩在线观看网站| 国产亚洲男人的天堂在线观看| 色综合久久天天综合| 精品视频在线看| 99久久视频| 午夜欧美成人久久久久久| 可以免费看毛片的网站| 久久国产一久久高清| 美女免费精品高清毛片在线视 | a级黄色毛片免费播放视频| 黄视频网站在线看| 二级片在线观看| 日韩在线观看网站| 精品久久久久久中文字幕2017| 台湾毛片| 精品久久久久久综合网| 精品在线观看国产| 欧美国产日韩一区二区三区| 国产视频一区二区在线观看| 久久久久久久男人的天堂| 可以免费看毛片的网站| 日韩男人天堂| 免费毛片基地| 日韩在线观看网站| 韩国三级香港三级日本三级| 亚洲精品中文字幕久久久久久| 精品视频一区二区| 国产美女在线一区二区三区| 成人免费观看男女羞羞视频| 精品国产香蕉伊思人在线又爽又黄| 精品国产一区二区三区精东影业 | 九九干| 日韩专区亚洲综合久久| 国产国语对白一级毛片| 国产伦久视频免费观看 视频| 日韩专区在线播放| 毛片成人永久免费视频| a级黄色毛片免费播放视频| 四虎久久精品国产| 一本高清在线| 日韩免费在线| 91麻豆精品国产片在线观看| 国产网站免费视频| 韩国三级一区| 美女免费精品高清毛片在线视 | 久久99中文字幕久久| 日韩一级黄色片| 亚欧成人乱码一区二区| 国产成+人+综合+亚洲不卡| 黄色福利| 亚洲精品久久久中文字| 成人影视在线播放| 欧美1区2区3区| 国产网站免费| 日韩在线观看网站|