一、數(shù)據(jù)包的數(shù)據(jù)結(jié)構(gòu) (所有字段采用大端序)
幀頭 |
幀長(zhǎng)度(頭至尾) |
幀類(lèi)型 |
幀數(shù)據(jù) |
幀尾 |
1字節(jié) |
4字節(jié) |
2字節(jié) |
1024字節(jié) |
1字節(jié) |
byte |
int |
short |
string |
byte |
0xC8 |
0xC9 |
二、Server端?實(shí)現(xiàn)代碼
1、main.go
func main() {
logconfig.InitLogger()//初始化日志庫(kù),日志庫(kù)實(shí)現(xiàn)可以查閱:
https://blog.csdn.net/banzhuantuqiang/article/details/131403454
//開(kāi)啟tcp server
logconfig.SugarLogger.Info("server端啟動(dòng)中...")
ip := "0.0.0.0"
port := 10302
go func() {
server.Start(ip, port)
}()
logconfig.SugarLogger.Info("server端啟動(dòng)完成")
for {
logconfig.SugarLogger.Info("server端主進(jìn)程活躍")
time.Sleep(5 * time.Second)
}
}
2、server.go
//啟動(dòng)TCP服務(wù)端
func Start(ip string, port int) (bool, error) {
var ClinetConn net.Conn = nil
//1:?jiǎn)⒂帽O(jiān)聽(tīng)
listener, err := net.Listen("tcp", ip+":"+strconv.Itoa(port))
//連接失敗處理
if err != nil {
logconfig.SugarLogger.Infof("啟動(dòng)服務(wù)失敗,err:%v", err)
return false, err
}
//程序退出時(shí)釋放端口
defer func() {
listener.Close()
logconfig.SugarLogger.Error("服務(wù)的退出")
}()
for {
coon, err := listener.Accept() //2.建立連接
//判斷是代理連接還是ssh連接
reader := bufio.NewReader(coon)
if ClinetConn != nil {
ClinetConn.Close()
}
ClinetConn = coon
if err != nil {
logconfig.SugarLogger.Errorf("接收客戶連接失敗,err:%v", err)
continue
}
logconfig.SugarLogger.Infof("接收客戶連接成功,%s", coon.RemoteAddr().String())
//啟動(dòng)一個(gè)goroutine處理客戶端連接
go process(coon, reader)
}
}
func process(coon net.Conn, reader *bufio.Reader) {
defer func() {
coon.Close()
logconfig.SugarLogger.Infof("客戶連接斷開(kāi),%s", coon.RemoteAddr().String())
}()
for {
msg, err := protocol.Decode(reader, coon)
if err != nil {
logconfig.SugarLogger.Infof("decode失敗,err:%v", err)
if msg.Type == 1 {
continue
} else {
logconfig.SugarLogger.Errorf("客戶端連接處理異常......")
return
}
}
logconfig.SugarLogger.Infof("收到客戶端%s的消息:%v", coon.RemoteAddr().String(), msg)
//TODO 解析數(shù)據(jù)和回應(yīng)消息,參照下面response.go
//這里另起一個(gè)線程去解析數(shù)據(jù),一般是json數(shù)據(jù)
}
}
3、protocol.go
const HEAD byte = 0xC8
const TAIL byte = 0xC9
func Encode(message Msg) ([]byte, error) {
var pkg = new(bytes.Buffer)
// 寫(xiě)入消息頭
err := binary.Write(pkg, binary.BigEndian, message.Head)
if err != nil {
return nil, err
}
// 寫(xiě)入長(zhǎng)度
err = binary.Write(pkg, binary.BigEndian, message.Length)
if err != nil {
return nil, err
}
// 寫(xiě)入類(lèi)型
err = binary.Write(pkg, binary.BigEndian, message.Type)
if err != nil {
return nil, err
}
err = binary.Write(pkg, binary.BigEndian, []byte(message.Data))
if err != nil {
return nil, err
}
err = binary.Write(pkg, binary.BigEndian, message.Tail)
if err != nil {
return nil, err
}
return pkg.Bytes(), nil
}
// 解碼(這里考慮了粘包和分包問(wèn)題)
func Decode(reader *bufio.Reader, conn net.Conn) (Msg, error) {
//|幀頭 |幀長(zhǎng)度(頭至尾) |幀類(lèi)型 |幀數(shù)據(jù) |幀尾
//|1字節(jié) |4字節(jié) |2字節(jié) |1024字節(jié) |1字節(jié)
//|byte |int |short |string |byte
//|0xC8 | | | |0xC9
for {
headbyte, headError := reader.ReadByte()
if headError != nil {
return Msg{}, headError
} else if headbyte == HEAD {
//已讀取到正確頭
break
} else {
logconfig.SugarLogger.Infof("讀到錯(cuò)誤字節(jié):%v 錯(cuò)誤的連接地址:%s", headbyte, conn.RemoteAddr().String())
}
}
// 讀消息長(zhǎng)度,不移動(dòng)位置
lengthByte, _ := reader.Peek(4)
lengthBuff := bytes.NewBuffer(lengthByte)
var length int32
//將長(zhǎng)度buff轉(zhuǎn)換為 int32,大端序
err := binary.Read(lengthBuff, binary.BigEndian, &length)
if err != nil {
return Msg{Type: 1}, err
}
//如果消息體超過(guò) 4096(默認(rèn)長(zhǎng)度)
var pack []byte
if length > 4096 {
pack = make([]byte, 0, int(length-1))
readableLength := length - 1
for {
if readableLength < 4096 {
slice := make([]byte, readableLength)
_, err = reader.Read(slice)
pack = append(pack, slice...)
break
}
slice := make([]byte, int32(reader.Buffered()))
_, err = reader.Read(slice)
pack = append(pack, slice...)
//更新可讀長(zhǎng)度
readableLength = readableLength - int32(len(slice))
}
// buffer返回緩沖中現(xiàn)有的可讀的字節(jié)數(shù),2+length+1表示幀類(lèi)型+數(shù)據(jù)長(zhǎng)度+幀尾
} else if length < 4096 && int32(reader.Buffered()) < length-1 {
//退回已讀取的幀頭
reader.UnreadByte()
return Msg{Type: 1}, errors.New("數(shù)據(jù)長(zhǎng)度不足")
} else {
// 讀取剩余幀內(nèi)容
pack = make([]byte, int(length-1))
_, err = reader.Read(pack)
if err != nil {
return Msg{Type: 1}, err
}
}
typeBuff := bytes.NewBuffer(pack[4:6])
var msgType int16
msgTypeErr := binary.Read(typeBuff, binary.BigEndian, &msgType)
if msgTypeErr != nil {
return Msg{Type: 1}, msgTypeErr
}
data := string(pack[6 : len(pack)-1])
tail := pack[len(pack)-1]
if tail != TAIL {
reader.UnreadByte()
return Msg{Type: 1}, errors.New("幀尾錯(cuò)誤,丟棄已讀取的字節(jié)")
}
msg := Msg{Head: HEAD, Length: length, Type: msgType, Data: data, Tail: TAIL}
return msg, nil
}
type Msg struct {
Head byte
Length int32
Type int16
Data string
Tail byte
}
4、response.go
func tcpReturn(coon net.Conn, result *dto.Result, msgType int16) {
marshal, _ := json.Marshal(result)//這里根據(jù)定義的消息體解析成json字符串
msg := protocol.Msg{protocol.HEAD, 8 + int32(len(marshal)), msgType, string(marshal), protocol.TAIL}
encode, _ := protocol.Encode(msg)
coon.Write(encode)
logconfig.SugarLogger.Infof("結(jié)束消息處理,tpc連接:%s,回復(fù)結(jié)果:%s", coon.RemoteAddr().String(), string(encode))
}
5、result.go文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-730753.html
type Result struct {
DataType string `json:"data_type"`
// 消息標(biāo)識(shí)
CallBackKey string `json:"call_back_key"`
// 狀態(tài)碼
Status string `json:"status"`
// 返回描述
Message string `json:"message"`
// 消息體
Data interface{} `json:"data"`
}
三、Client端?實(shí)現(xiàn)代碼文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-730753.html
package main
import (
"bytes"
"encoding/binary"
"fmt"
"net"
"time"
)
const HEAD byte = 0xC8
const TAIL byte = 0xC9
func main() {
conn, err := net.Dial("tcp", "127.0.0.1:10301")
if err != nil {
fmt.Println("連接服務(wù)端失敗,err:", err)
return
}
conn.SetReadDeadline(time.Now().Add(100 * time.Second))
strJosn:="66666"http://這里寫(xiě)自己的json字符串
//Type 0x01 0x02.....
message := Msg{HEAD, 8 + int32(len(strJosn)), 0x10, string(strJosn), TAIL} // 板卡ROM重置狀態(tài)查詢
b, err := Encode(message)
if err != nil {
fmt.Println("Encode失敗,err:", err)
}
_, error := conn.Write(b)
if error != nil {
fmt.Println("發(fā)送失敗,err:", error)
return
}
fmt.Println("發(fā)送成功...,msg:", b)
fmt.Println("發(fā)送成功...,msg:", message)
parseServerResponseMesage(conn)
}
//服務(wù)端返回消息
func parseServerResponseMesage(coon net.Conn) {
for {
dataByte := make([]byte, 4096)
n, _ := coon.Read(dataByte)
bytes := dataByte[0:n]
fmt.Println("收到服務(wù)端消息:", string(bytes))
}
}
type Msg struct {
Head byte
Length int32
Type int16
Data string
Tail byte
}
func Encode(message Msg) ([]byte, error) {
var pkg = new(bytes.Buffer)
// 寫(xiě)入消息頭
err := binary.Write(pkg, binary.BigEndian, message.Head)
if err != nil {
return nil, err
}
// 寫(xiě)入長(zhǎng)度
err = binary.Write(pkg, binary.BigEndian, message.Length)
if err != nil {
return nil, err
}
// 寫(xiě)入類(lèi)型
err = binary.Write(pkg, binary.BigEndian, message.Type)
if err != nil {
return nil, err
}
err = binary.Write(pkg, binary.BigEndian, []byte(message.Data))
if err != nil {
return nil, err
}
err = binary.Write(pkg, binary.BigEndian, message.Tail)
if err != nil {
return nil, err
}
return pkg.Bytes(), nil
}
到了這里,關(guān)于Golang實(shí)現(xiàn)之TCP長(zhǎng)連接-------服務(wù)端和客戶端的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!