go protobuf tcp 粘包处理 demo
生活随笔
收集整理的這篇文章主要介紹了
go protobuf tcp 粘包处理 demo
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
1.安裝相關包
設置代理
set GOPROXY=https://goproxy.cn
安裝?golang?的proto工具包
go get -u github.com/golang/protobuf/proto
安裝?goalng?的proto編譯支持
go get -u github.com/golang/protobuf/protoc-gen-go
?
查看 protoc 版本
protoc --version?
2.創建項目
模擬微服務開發,在某個空文件夾下建立兩個文件夾
分別為?client?和?server
3.創建 proto 文件
proto 文件是微服務交互的基本
proto的語法見官方:https://developers.google.com/protocol-buffers/docs/proto3?hl=zh-cn
這里簡單寫一個示例(people.proto)
4.生成?.bp.go?文件
protoc --go_out=./ ./people.proto運行后會在spider目錄下生成?people.pb.go?文件
該文件是 server 和 client 的通信協議,勿動
5.編寫 server 端
編寫 server.go?文件
package probimport ("bufio""fmt""github.com/golang/protobuf/proto""net""os""tes/prob/model" )func Server() {//監聽listener, err := net.Listen("tcp", "localhost:6600")if err != nil {panic(err)}for {conn, err := listener.Accept()if err != nil {panic(err)}fmt.Println("new connect", conn.RemoteAddr())go readMessage(conn)} }//接收消息 func readMessage(conn net.Conn) {defer conn.Close()reader :=bufio.NewReader(conn)//讀消息for {data,err := Decode(reader)if err ==nil && len(data)>0{p := &model.People{}err := proto.Unmarshal(data, p)if err != nil {panic(err)}//buf= make([]byte, 10240,10240)//前兩個字節表示本次請求body為長度fmt.Printf("receive %s %+v \n", conn.RemoteAddr(), p)if p.Name == "stop" {os.Exit(1)}}if err!=nil{panic(err)}} }6.編寫 client 端
編寫 client.go?文件
package probimport ("fmt""github.com/golang/protobuf/proto""net""strconv""tes/prob/model""time" )func Client() {strIP := "localhost:6600"var conn net.Connvar err error//連接服務器if conn, err = net.Dial("tcp", strIP); err != nil {panic(err)}fmt.Println("connect", strIP, "success")defer conn.Close()//發送消息//sender := bufio.NewScanner(os.Stdin)for i :=0; i<10000;i++ {stSend := &model.People{Name: "lishang "+ strconv.Itoa(i),Age: *proto.Int(i),}//protobuf編碼pData, err := proto.Marshal(stSend)if err != nil {panic(err)}data,err := Encode(pData)if err==nil{conn.Write(data)fmt.Printf("%d send %d \n" ,i,uint16(len(pData)))}/*if sender.Text() == "stop" {return}*/}for{time.Sleep(time.Millisecond*10)/*stSend := &model.People{Name: "lishang "+ strconv.Itoa(22222222),Age: *proto.Int(22222222),}//protobuf編碼pData, err := proto.Marshal(stSend)if err != nil {panic(err)}b := make([]byte,2,2)binary.BigEndian.PutUint16(b,uint16(len(pData)))//發送conn.Write(append(b,pData...))*/}}7.粘包處理
編寫 protol.go?文件
package probimport ("bufio""bytes""encoding/binary" )//將數據包編碼(即加上包頭再轉為二進制) func Encode(mes []byte) ([]byte, error) {//獲取發送數據的長度,并轉換為四個字節的長度,即int32len := uint16(len(mes))//創建數據包dataPackage := new(bytes.Buffer) //使用字節緩沖區,一步步寫入性能更高//先向緩沖區寫入包頭//大小端口訣:大端:尾端在高位,小端:尾端在低位//編碼用小端寫入,解碼也要從小端讀取,要保持一致err := binary.Write(dataPackage, binary.LittleEndian, len) //往存儲空間小端寫入數據if err != nil {return nil, err}//寫入消息err = binary.Write(dataPackage, binary.LittleEndian, mes)if err != nil {return nil, err}return dataPackage.Bytes(), nil }//解碼數據包 func Decode(reader *bufio.Reader) ([]byte, error) {//讀取數據包的長度(從包頭獲取)lenByte, err := reader.Peek(2) //讀取前四個字節的數據if err!=nil{return []byte{}, err}//轉成Buffer對象,設置為從小端讀取buff := bytes.NewBuffer(lenByte)var len uint16 //讀取的數據大小,初始化為0err = binary.Read(buff, binary.LittleEndian, &len) //從小端讀取if err != nil {return []byte{}, err}//讀取消息pkg := make([]byte, int(len)+2)//Buffered返回緩沖區中現有的可讀取的字節數if reader.Buffered() < int(len)+2 { //如果讀取的包頭的數據大小和讀取到的不符合hr := 0for hr < int(len)+2 {l, err := reader.Read(pkg[hr:])if err != nil {return []byte{}, err}hr+=l}}else{_, err := reader.Read(pkg)if err != nil {return []byte{}, err}}return pkg[2:], nil}8.運行
編寫 main.go?文件
package mainimport ("fmt""os""os/signal""syscall""tes/prob" )func main(){go prob.Server()go prob.Client()c := make(chan os.Signal, 1)signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)for {s := <-cfmt.Printf("job get a signal %s", s.String())switch s {case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:fmt.Printf("job exit")returncase syscall.SIGHUP:default:return}} }
我們運行后可看到結果已經正常返回并打印
總結
以上是生活随笔為你收集整理的go protobuf tcp 粘包处理 demo的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Golang 使用Protocol Bu
- 下一篇: Go语言操作MySQL