showliuzp 2025-10-14 11:12 采纳率: 84.3%
浏览 9

golang tcp协议解析为什么byte不需要转换


//客户端:
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",":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)
    }
}


//服务端接收socket
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)
        buf := bytes.NewReader(body_buf)
        err = binary.Read(buf,binary.BigEndian,&(*obj).FunsName)
        fmt.Printf("body_buf:%+v,主体:%+v,err:%+v\n",body_buf,(*obj).FunsName,err)
        }
}


我在通过binary.Read以大端的形式读取buf到rpc_detail.FunsName,但(*obj).FunsName的打印为空,body_buf打印结果为:[103 101 116 95 100 97 116 97 95 100 101 116 97 105 108 45 48],为什么binary.Reads对(*obj).FunsName转换不成功,如果改为(*obj).FunsName = body_buf,不通过binary.Read进行读取,然后通过string((*obj).FunsName)强转就可以,难道读取的[]byte不需要按统一的大端或小端处理么
  • 写回答

4条回答 默认 最新

  • 紫雾凌寒 新星创作者: 人工智能技术领域 2025-10-14 11:13
    关注

    让 “紫雾凌寒” 来帮你回答,本回答结合 DeepSeek

    如果还有问题,请评论或点击头像私信,如果有帮助到您, 请关注、采纳、点赞

    1. 关键点分析
      - 问题在于Go语言中TCP协议解析时byte类型为何不需要进行显式转换。这涉及到Go语言对数据类型在网络数据处理中的理解和使用方式。
      - 特别是在处理网络字节流时,如何准确地将接收到的字节数据解析为不同的数据类型,以及byte类型在这个过程中的角色和处理方式。
    2. 解决方案
      - 在Go语言中,encoding/binary包提供了将基本数据类型与字节序列进行转换的功能。例如,要将一个整数转换为字节序列,可以使用binary.BigEndian.PutUint32函数。
      - 假设我们有一个结构体Message,包含一个整数id和一个字符串data,要将其转换为字节流发送:
    package main
    
    import (
        "bytes"
        "encoding/binary"
        "fmt"
    )
    
    type Message struct {
        ID   uint32
        Data string
    }
    
    func (m Message) ToBytes() []byte {
        var buf bytes.Buffer
        err := binary.Write(&buf, binary.BigEndian, m.ID)
        if err!= nil {
            panic(err)
        }
        dataLen := uint32(len(m.Data))
        err = binary.Write(&buf, binary.BigEndian, dataLen)
        if err!= nil {
            panic(err)
        }
        buf.WriteString(m.Data)
        return buf.Bytes()
    }
    
    • 要从字节流中解析出Message结构体:
    func FromBytes(data []byte) Message {
        var id uint32
        var dataLen uint32
        reader := bytes.NewReader(data)
        err := binary.Read(reader, binary.BigEndian, &id)
        if err!= nil {
            panic(err)
        }
        err = binary.Read(reader, binary.BigEndian, &dataLen)
        if err!= nil {
            panic(err)
        }
        buf := make([]byte, dataLen)
        _, err = reader.Read(buf)
        if err!= nil {
            panic(err)
        }
        return Message{
            ID:   id,
            Data: string(buf),
        }
    }
    
    • 在这个过程中,binary.Writebinary.Read函数会自动处理字节序(这里是大端序),并且不需要显式地将byte类型进行转换。它们会根据数据类型的大小和字节序规则,正确地读写字节。例如,binary.Write会将uint32类型的id按照大端序写入字节流,binary.Read会从字节流中按照大端序读取uint32类型的数据到id变量中。
      3. 多种方案的优缺点
    • 方案优点
      • 使用encoding/binary包是Go语言标准库提供的方式,代码简洁、高效且具有良好的兼容性。它对不同数据类型的处理是基于Go语言的类型系统,能够准确地处理各种基本数据类型和自定义结构体。
      • 字节序的处理非常方便,通过指定binary.BigEndianbinary.LittleEndian可以轻松切换字节序,适应不同的网络协议要求。
    • 方案缺点
      • 对于复杂的数据结构,可能需要编写较多的辅助代码来处理结构体的嵌套和数据长度的管理。例如,如果结构体中有多层嵌套,可能需要在ToBytesFromBytes函数中多次调用binary.Writebinary.Read
        4. 总结:在Go语言的TCP协议解析中,encoding/binary包提供了一种方便且高效的方式来处理数据类型与字节流之间的转换,不需要显式地将byte类型进行转换。通过合理使用该包中的函数,可以准确地解析和构建网络数据,并且能够灵活地处理字节序等问题。

    需要注意的是,你提供的代码片段type rpc_det不完整,上述示例是基于一个完整的结构体解析需求给出的。如果rpc_det有更复杂的结构,处理方式可能会有所不同,但基本原理是类似的,都是利用encoding/binary包来处理数据与字节流的转换。

    希望以上解答对您有所帮助。如果您有任何疑问,欢迎在评论区提出。

    评论

报告相同问题?

问题事件

  • 创建了问题 10月14日