一、数据包的数据结构 (所有字段采用大端序)
帧头 | 帧长度(头至尾) | 帧类型 | 帧数据 | 帧尾 |
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 }