新聞中心
?gtcp?提供了許多方便的原生操作連接數(shù)據(jù)的方法,但是在絕大多數(shù)的應(yīng)用場(chǎng)景中,開發(fā)者需要自己設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu),并進(jìn)行封包/解包處理,由于?TCP?消息協(xié)議是沒有消息邊界保護(hù)的,因此復(fù)雜的網(wǎng)絡(luò)通信環(huán)境中很容易出現(xiàn)粘包的情況。因此?gtcp?也提供了簡(jiǎn)單的數(shù)據(jù)協(xié)議,方便開發(fā)者進(jìn)行消息包交互,開發(fā)者不再需要擔(dān)心消息包的處理細(xì)節(jié),包括封包/解包處理,這一切復(fù)雜的邏輯?gtcp?已經(jīng)幫你處理好了。

簡(jiǎn)單協(xié)議
?gtcp?模塊提供了簡(jiǎn)單輕量級(jí)數(shù)據(jù)交互協(xié)議,效率非常高,協(xié)議格式如下:
數(shù)據(jù)長(zhǎng)度(16bit)|數(shù)據(jù)字段(變長(zhǎng))- 數(shù)據(jù)長(zhǎng)度:默認(rèn)為16位(2字節(jié)),用于標(biāo)識(shí)該消息體的數(shù)據(jù)長(zhǎng)度,單位為字節(jié),不包含自身的2字節(jié);
- 數(shù)據(jù)字段:變長(zhǎng),根據(jù)數(shù)據(jù)長(zhǎng)度可以知道,數(shù)據(jù)最大長(zhǎng)度不能超過?
0xFFFF = 65535 bytes = 64 KB?;
簡(jiǎn)單協(xié)議由?gtcp?封裝實(shí)現(xiàn),如果開發(fā)者客戶端和服務(wù)端如果都使用?gtcp?模塊來通信則無需關(guān)心協(xié)議實(shí)現(xiàn),專注數(shù)據(jù)字段封裝/解析實(shí)現(xiàn)即可。如果涉及和其他開發(fā)語言對(duì)接,則需要按照該協(xié)議實(shí)現(xiàn)對(duì)接即可,由于簡(jiǎn)單協(xié)議非常簡(jiǎn)單輕量級(jí),因此對(duì)接成本很低。
數(shù)據(jù)字段也可以為空,即沒有任何長(zhǎng)度。
操作方法
https://pkg.GO.dev/github.com/gogf/gf/v2/net/gtcp
type Conn
func (c *Conn) SendPkg(data []byte, option ...PkgOption) error
func (c *Conn) SendPkgWithTimeout(data []byte, timeout time.Duration, option ...PkgOption) error
func (c *Conn) SendRecvPkg(data []byte, option ...PkgOption) ([]byte, error)
func (c *Conn) SendRecvPkgWithTimeout(data []byte, timeout time.Duration, option ...PkgOption) ([]byte, error)
func (c *Conn) RecvPkg(option ...PkgOption) (result []byte, err error)
func (c *Conn) RecvPkgWithTimeout(timeout time.Duration, option ...PkgOption) ([]byte, error)可以看到,消息包方法命名是在原有的基本連接操作方法中加上了?Pkg?關(guān)鍵詞便于區(qū)分。
其中,請(qǐng)求參數(shù)中的?PkgOption?數(shù)據(jù)結(jié)構(gòu)如下,用于定義消息包接收策略:
// 數(shù)據(jù)讀取選項(xiàng)
type PkgOption struct {
HeaderSize int // 自定義頭大小(默認(rèn)為2字節(jié),最大不能超過4字節(jié))
MaxDataSize int // (byte)數(shù)據(jù)讀取的最大包大小,默認(rèn)最大不能超過2字節(jié)(65535 byte)
Retry Retry // 失敗重試策略
}使用示例
示例1,基本使用
package main
import (
"fmt"
"github.com/gogf/gf/v2/net/gtcp"
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/util/gconv"
"time"
)
func main() {
// Server
go gtcp.NewServer("127.0.0.1:8999", func(conn *gtcp.Conn) {
defer conn.Close()
for {
data, err := conn.RecvPkg()
if err != nil {
fmt.Println(err)
break
}
fmt.Println("receive:", data)
}
}).Run()
time.Sleep(time.Second)
// Client
conn, err := gtcp.NewConn("127.0.0.1:8999")
if err != nil {
panic(err)
}
defer conn.Close()
for i := 0; i < 10000; i++ {
if err := conn.SendPkg([]byte(gconv.String(i))); err != nil {
glog.Error(err)
}
time.Sleep(1*time.Second)
}
}這個(gè)示例比較簡(jiǎn)單,執(zhí)行后,輸出結(jié)果為:
receive: [48]
receive: [49]
receive: [50]
receive: [51]
...示例2,自定義數(shù)據(jù)結(jié)構(gòu)
大多數(shù)場(chǎng)景下,我們需要對(duì)發(fā)送的消息能自定義數(shù)據(jù)結(jié)構(gòu),開發(fā)者可以利用數(shù)據(jù)字段傳遞任意的消息內(nèi)容實(shí)現(xiàn)。
以下是一個(gè)簡(jiǎn)單的自定義數(shù)據(jù)結(jié)構(gòu)的示例,用于客戶端上報(bào)當(dāng)前主機(jī)節(jié)點(diǎn)的內(nèi)存及CPU使用情況,示例代碼位于:https://github.com/gogf/gf/v2/tree/master/.example/net/gtcp/pkg_operations/monitor
在該示例中,數(shù)據(jù)字段使用了?JSON?數(shù)據(jù)格式進(jìn)行自定義,便于數(shù)據(jù)的編碼/解碼。
實(shí)際場(chǎng)景中,開發(fā)者往往需要自定義包解析協(xié)議,或者采用較通用的?protobuf?二進(jìn)制包封裝/解析協(xié)議。
- ?
types/types.go?
數(shù)據(jù)結(jié)構(gòu)定義。
package types
import "github.com/gogf/gf/v2/frame/g"
type NodeInfo struct {
Cpu float32 // CPU百分比(%)
Host string // 主機(jī)名稱
Ip g.Map // IP地址信息(可能多個(gè))
MemUsed int // 內(nèi)存使用(byte)
MemTotal int // 內(nèi)存總量(byte)
Time int // 上報(bào)時(shí)間(時(shí)間戳)
}- ?
gtcp_monitor_server.go?
服務(wù)端。
package main
import (
"encoding/json"
"github.com/gogf/gf/v2/net/gtcp"
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/.example/net/gtcp/pkg_operations/monitor/types"
)
func main() {
// 服務(wù)端,接收客戶端數(shù)據(jù)并格式化為指定數(shù)據(jù)結(jié)構(gòu),打印
gtcp.NewServer("127.0.0.1:8999", func(conn *gtcp.Conn) {
defer conn.Close()
for {
data, err := conn.RecvPkg()
if err != nil {
if err.Error() == "EOF" {
glog.Println("client closed")
}
break
}
info := &types.NodeInfo{}
if err := json.Unmarshal(data, info); err != nil {
glog.Errorf("invalid package structure: %s", err.Error())
} else {
glog.Println(info)
conn.SendPkg([]byte("ok"))
}
}
}).Run()
}- ?
gtcp_monitor_client.go?
客戶端。
package main
import (
"encoding/json"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/gtcp"
"github.com/gogf/gf/v2/os/glog"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/.example/net/gtcp/pkg_operations/monitor/types"
)
func main() {
// 數(shù)據(jù)上報(bào)客戶端
conn, err := gtcp.NewConn("127.0.0.1:8999")
if err != nil {
panic(err)
}
defer conn.Close()
// 使用JSON格式化數(shù)據(jù)字段
info, err := json.Marshal(types.NodeInfo{
Cpu : float32(66.66),
Host : "localhost",
Ip : g.Map {
"etho" : "192.168.1.100",
"eth1" : "114.114.10.11",
},
MemUsed : 15560320,
MemTotal : 16333788,
Time : int(gtime.Timestamp()),
})
if err != nil {
panic(err)
}
// 使用 SendRecvPkg 發(fā)送消息包并接受返回
if result, err := conn.SendRecvPkg(info); err != nil {
if err.Error() == "EOF" {
glog.Println("server closed")
}
} else {
glog.Println(string(result))
}
}- 執(zhí)行后
客戶端輸出結(jié)果為:
2019-05-03 13:33:25.710 ok服務(wù)端輸出結(jié)果為:
2019-05-03 13:33:25.710 &{66.66 localhost map[eth1:114.114.10.11 etho:192.168.1.100] 15560320 16333788 1556861605}
2019-05-03 13:33:25.710 client closed 文章標(biāo)題:創(chuàng)新互聯(lián)GoFrame教程:GoFrame 連接對(duì)象-消息包處理
當(dāng)前網(wǎng)址:http://www.5511xx.com/article/ccesiio.html


咨詢
建站咨詢
