showliuzp 2025-10-13 22:37 采纳率: 84.3%
浏览 6
已结题

golang tcp协议解析


//client.go
//发送数据
package main

import (
    "bytes"
    "encoding/binary"
    "fmt"
    "net"
)

type rpc_detail struct{
    Version [2]byte //版本号
    FunsLen uint32  //函数名长度
    FunsName []byte
}


func main() {
    obj := new(rpc_detail)
    conn ,err := net.Dial("tcp","54.151.231.22:33122")
    data := "get_data_detail-%d"

    for i:=0;i < 1;i++{
        send_data := fmt.Sprintf(data,i)

        (*obj).Version[0]   = 'v'
        (*obj).Version[1]   = '1'
        (*obj).FunsLen      = uint32(len(send_data))
        (*obj).FunsName     = []byte(send_data)

        fmt.Printf("结构体:%+v\n",*obj)
        buf := new(bytes.Buffer)
        binary.Write(buf,binary.BigEndian,obj.Version)
        binary.Write(buf,binary.BigEndian,obj.FunsLen)
        binary.Write(buf,binary.BigEndian,obj.FunsName)

        conn.Write(buf.Bytes())
        fmt.Printf("输出:%+v,err:%+v\n",buf.Bytes(),err)
    }
}


//服务端接收数据:
package main

import (
        "fmt"
        "net"
        "bytes"
        "encoding/binary"
        "os"
        "io"
)

func main() {
        listener, err := net.Listen("tcp", ":33122")
        if err != nil {
                fmt.Println("Error listening:", err.Error())
                os.Exit(1)
        }
        defer listener.Close()

        fmt.Println("Server started, listening on port 33122")

        for {
                conn, err := listener.Accept()
                if err != nil {
                        fmt.Println("Error accepting: ", err.Error())
                        continue
                }

                go handleConnection(conn)
        }
}

type rpc_detail struct{
    Version     [2]byte //版本号
    FunsLen     uint32  //函数名长度
    FunsName    []byte
}

func unpack(b []byte)(obj *rpc_detail){
    buf := bytes.NewReader(b)
    
    obj = new(rpc_detail)
    err := binary.Read(buf,binary.BigEndian,&obj.Version)
    fmt.Printf("错误:%+v,版本:%+c%+c\n",err,(*obj).Version[0],(*obj).Version[1])

    err = binary.Read(buf,binary.BigEndian,&(*obj).FunsLen)
    fmt.Printf("错误:%+v,函数名长度:%+v\n",err,(*obj).FunsLen)
    
    return
}


func handleConnection(conn net.Conn) {
        defer conn.Close()

    data := make([]byte,6)
    //dataBuffer := bytes.NewReader(binaryData)
        for {
                _, err := io.ReadFull(conn, data)
                if err != nil {
                        if err == io.EOF {
                                fmt.Println("Client disconnected")
                                return
                        } else {
                                fmt.Println("Error reading from client:", err.Error())
                                return
                        }
                }

        obj := unpack(data)
        body_len := obj.FunsLen
        body_buf := make([]byte,body_len)
                _, err = io.ReadFull(conn, body_buf)
        body_data := bytes.NewReader(body_buf)
        err = binary.Read(body_data,binary.BigEndian,&(*obj).FunsName)
        fmt.Printf("主体:%+v,err:%+v\n",(*obj).FunsName,err)
        }
}

我遇到的问题是FunsName打印是空数据,但是读取到body_buf里了:body_buf:[103 101 116 95 100 97 116 97 95 100 101 116 97 105 108 45 48],主体:[],err:<nil>
  • 写回答

5条回答 默认 最新

  • 宝码香车 优质创作者: JavaScript技术领域 2025-10-13 22:39
    关注

    让【宝码香车】来帮您解答,本回答参考DeepSeek编写,并整理提供。

    如果答案让您满意,请采纳、点赞、关注,非常感谢!
    .
    每日一句正能量,让它带给您好心情:每一个清晨,都是一个新的开始,充满无限可能。
    .

    根据你的代码分析,问题出现在服务端解析数据时。主要问题有两个:

    问题分析

    1. 固定读取6字节导致数据不完整:服务端每次固定读取6字节,但实际数据可能超过这个长度
    2. FunsName字段解析错误:在unpack函数中只解析了Version和FunsLen,但FunsName的解析逻辑有问题

    解决方案

    修改服务端代码:

    package main
    
    
    import (
        "fmt"
        "net"
        "bytes"
        "encoding/binary"
        "os"
        "io"
    )
    
    
    func main() {
        listener, err := net.Listen("tcp", ":33122")
        if err != nil {
            fmt.Println("Error listening:", err.Error())
            os.Exit(1)
        }
        defer listener.Close()
    
    
        fmt.Println("Server started, listening on port 33122")
    
    
        for {
            conn, err := listener.Accept()
            if err != nil {
                fmt.Println("Error accepting: ", err.Error())
                continue
            }
    
    
            go handleConnection(conn)
        }
    }
    
    
    type rpc_detail struct {
        Version  [2]byte //版本号
        FunsLen  uint32  //函数名长度
        FunsName []byte
    }
    
    
    func unpackHeader(data []byte) (*rpc_detail, error) {
        if len(data) < 6 { // Version(2字节) + FunsLen(4字节)
            return nil, fmt.Errorf("header data too short")
        }
        
        obj := &rpc_detail{}
        buf := bytes.NewReader(data)
        
        // 读取版本号
        err := binary.Read(buf, binary.BigEndian, &obj.Version)
        if err != nil {
            return nil, err
        }
        
        // 读取函数名长度
        err = binary.Read(buf, binary.BigEndian, &obj.FunsLen)
        if err != nil {
            return nil, err
        }
        
        return obj, nil
    }
    
    
    func handleConnection(conn net.Conn) {
        defer conn.Close()
    
    
        for {
            // 先读取固定长度的头部数据 (2字节Version + 4字节FunsLen)
            header := make([]byte, 6)
            _, err := io.ReadFull(conn, header)
            if err != nil {
                if err == io.EOF {
                    fmt.Println("Client disconnected")
                    return
                } else {
                    fmt.Println("Error reading header:", err.Error())
                    return
                }
            }
    
    
            // 解析头部
            obj, err := unpackHeader(header)
            if err != nil {
                fmt.Println("Error unpacking header:", err.Error())
                return
            }
    
    
            fmt.Printf("版本:%c%c, 函数名长度:%d\n", obj.Version[0], obj.Version[1], obj.FunsLen)
    
    
            // 根据长度读取函数名数据
            if obj.FunsLen > 0 {
                obj.FunsName = make([]byte, obj.FunsLen)
                _, err = io.ReadFull(conn, obj.FunsName)
                if err != nil {
                    fmt.Println("Error reading body:", err.Error())
                    return
                }
                
                fmt.Printf("函数名:%s\n", string(obj.FunsName))
            }
        }
    }
    

    主要修改点

    1. 分离头部解析:将头部解析单独放在unpackHeader函数中
    2. 正确读取流程
    • 先读取固定6字节头部(Version 2字节 + FunsLen 4字节)
    • 解析头部获取FunsLen
    • 根据FunsLen读取相应长度的函数名数据
    1. 直接赋值:直接将读取到的字节数据赋值给obj.FunsName,不需要再次使用binary.Read

    客户端代码优化建议

    客户端代码也可以稍作优化:

    // 在客户端main函数中,发送数据后可以添加读取服务器响应的逻辑
    // 在conn.Write(buf.Bytes())后添加:
    
    
    // 读取服务器响应
    response := make([]byte, 1024)
    n, err := conn.Read(response)
    if err != nil {
        fmt.Printf("读取响应错误: %v\n", err)
    } else {
        fmt.Printf("服务器响应: %s\n", string(response[:n]))
    }
    

    这样修改后,服务端应该能正确解析出函数名数据。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(4条)

报告相同问题?

问题事件

  • 系统已结题 10月22日
  • 已采纳回答 10月14日
  • 创建了问题 10月13日