赞
踩
一、数据包的数据结构 (所有字段采用大端序)
帧头 | 帧长度(头至尾) | 帧类型 | 帧数据 | 帧尾 |
1字节 | 4字节 | 2字节 | 1024字节 | 1字节 |
byte | int | short | string | byte |
0xC8 | 0xC9 |
二、Server端 实现代码
1、main.go
- func main() {
-
- logconfig.InitLogger()//初始化日志库,日志库实现可以查阅:
- https://blog.csdn.net/banzhuantuqiang/article/details/131403454
-
-
- //开启tcp server
- logconfig.SugarLogger.Info("server端启动中...")
- ip := "0.0.0.0"
- port := 10302
-
-
- go func() {
-
- server.Start(ip, port)
- }()
-
-
- logconfig.SugarLogger.Info("server端启动完成")
-
- for {
- logconfig.SugarLogger.Info("server端主进程活跃")
- time.Sleep(5 * time.Second)
- }
-
- }

2、server.go
- //启动TCP服务端
- func Start(ip string, port int) (bool, error) {
- var ClinetConn net.Conn = nil
- //1:启用监听
- listener, err := net.Listen("tcp", ip+":"+strconv.Itoa(port))
- //连接失败处理
- if err != nil {
- logconfig.SugarLogger.Infof("启动服务失败,err:%v", err)
- return false, err
- }
-
- //程序退出时释放端口
- defer func() {
- listener.Close()
- logconfig.SugarLogger.Error("服务的退出")
- }()
-
-
- 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())
-
- //启动一个goroutine处理客户端连接
- go process(coon, reader)
- }
- }
-
- func process(coon net.Conn, reader *bufio.Reader) {
- defer func() {
- coon.Close()
- logconfig.SugarLogger.Infof("客户连接断开,%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 解析数据和回应消息,参照下面response.go
- //这里另起一个线程去解析数据,一般是json数据
- }
- }

3、protocol.go
- const HEAD byte = 0xC8
- const TAIL byte = 0xC9
-
-
- func Encode(message Msg) ([]byte, error) {
- var pkg = new(bytes.Buffer)
- // 写入消息头
- err := binary.Write(pkg, binary.BigEndian, message.Head)
- if err != nil {
- return nil, err
- }
- // 写入长度
- err = binary.Write(pkg, binary.BigEndian, message.Length)
- if err != nil {
- return nil, err
- }
- // 写入类型
- 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
- }
-
-
- // 解码(这里考虑了粘包和分包问题)
- func Decode(reader *bufio.Reader, conn net.Conn) (Msg, error) {
- //|帧头 |帧长度(头至尾) |帧类型 |帧数据 |帧尾
- //|1字节 |4字节 |2字节 |1024字节 |1字节
- //|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("读到错误字节:%v 错误的连接地址:%s", headbyte, conn.RemoteAddr().String())
- }
- }
- // 读消息长度,不移动位置
- lengthByte, _ := reader.Peek(4)
- lengthBuff := bytes.NewBuffer(lengthByte)
- var length int32
- //将长度buff转换为 int32,大端序
- err := binary.Read(lengthBuff, binary.BigEndian, &length)
- if err != nil {
- return Msg{Type: 1}, err
- }
- //如果消息体超过 4096(默认长度)
- 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...)
- //更新可读长度
- readableLength = readableLength - int32(len(slice))
- }
- // buffer返回缓冲中现有的可读的字节数,2+length+1表示帧类型+数据长度+帧尾
- } else if length < 4096 && int32(reader.Buffered()) < length-1 {
- //退回已读取的帧头
- reader.UnreadByte()
- return Msg{Type: 1}, errors.New("数据长度不足")
- } else {
- // 读取剩余帧内容
- 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("帧尾错误,丢弃已读取的字节")
- }
- 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)//这里根据定义的消息体解析成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("结束消息处理,tpc连接:%s,回复结果:%s", coon.RemoteAddr().String(), string(encode))
- }
5、result.go
- type Result struct {
- DataType string `json:"data_type"`
- // 消息标识
- CallBackKey string `json:"call_back_key"`
- // 状态码
- Status string `json:"status"`
- // 返回描述
- Message string `json:"message"`
- // 消息体
- Data interface{} `json:"data"`
- }
三、Client端 实现代码
- 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("连接服务端失败,err:", err)
- return
- }
- conn.SetReadDeadline(time.Now().Add(100 * time.Second))
-
-
-
- strJosn:="66666"//这里写自己的json字符串
- //Type 0x01 0x02.....
- message := Msg{HEAD, 8 + int32(len(strJosn)), 0x10, string(strJosn), TAIL} // 板卡ROM重置状态查询
-
- b, err := Encode(message)
- if err != nil {
- fmt.Println("Encode失败,err:", err)
- }
- _, error := conn.Write(b)
- if error != nil {
- fmt.Println("发送失败,err:", error)
- return
- }
- fmt.Println("发送成功...,msg:", b)
- fmt.Println("发送成功...,msg:", message)
- parseServerResponseMesage(conn)
-
- }
-
- //服务端返回消息
- func parseServerResponseMesage(coon net.Conn) {
- for {
- dataByte := make([]byte, 4096)
- n, _ := coon.Read(dataByte)
- bytes := dataByte[0:n]
- fmt.Println("收到服务端消息:", 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)
- // 写入消息头
- err := binary.Write(pkg, binary.BigEndian, message.Head)
- if err != nil {
- return nil, err
- }
- // 写入长度
- err = binary.Write(pkg, binary.BigEndian, message.Length)
- if err != nil {
- return nil, err
- }
- // 写入类型
- 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
- }
-
-

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。