【问题标题】:Go conversion between struct and byte arrayGo 结构体和字节数组之间的转换
【发布时间】:2014-12-09 22:06:06
【问题描述】:

我正在用 Go 编写一个客户端 - 服务器应用程序。我想在 Go 中执行类似 C 的类型转换。

例如在围棋中

type packet struct {
    opcode uint16
    data [1024]byte
}

var pkt1 packet
...
n, raddr, err := conn.ReadFromUDP(pkt1)  // error here

我还想执行类似 C 的 memcpy(),这将允许我直接将接收到的网络字节流映射到结构。

例如上面收到的pkt1

type file_info struct {
    file_size uint32       // 4 bytes
    file_name [1020]byte
}

var file file_info
if (pkt1.opcode == WRITE) {
    memcpy(&file, pkt1.data, 1024)
}

【问题讨论】:

  • 我建议先尝试在 go 中编写它。你不会在 go 中做这样的事情。除其他外,go 没有强制转换。 uint 也不是 4 个字节。 Conn.Read 采用[]byte,它实际上是一个指向后备数组的智能大小的指针。只需在 go 中编写此内容,您就会有更好的时间。
  • 您是否已经看过 encoding/gob 包的内置二进制序列化?

标签: struct casting go memcpy


【解决方案1】:

我遇到了同样的问题,我通过使用“编码/二进制”包解决了这个问题。这是一个例子:

package main

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

func main() {
  p := fmt.Println
  b := []byte{43, 1, 0}

  myStruct := MyStruct{}
  err := binary.Read(bytes.NewBuffer(b[:]), binary.BigEndian, &myStruct)

  if err != nil {
    panic(err)
  }

  p(myStruct)
}

type MyStruct struct {
  Num uint8
  Num2 uint16
}

下面是运行示例:https://play.golang.org/p/Q3LjaAWDMh

【讨论】:

    【解决方案2】:

    感谢您的回答,我相信它们可以完美运行。但就我而言,我更感兴趣的是解析作为网络数据包接收的 []byte 缓冲区。我使用以下方法来解析缓冲区。

    var data []byte // holds the network packet received
    opcode := binary.BigEndian.Uint16(data) // this will get first 2 bytes to be interpreted as uint16 number
    raw_data := data[2:len(data)] // this will copy rest of the raw data in to raw_data byte stream
    

    从结构体构造 []byte 流时,可以使用以下方法

    type packet struct {
        opcode uint16
        blk_no uint16
        data   string
    }
    pkt := packet{opcode: 2, blk_no: 1, data: "testing"}
    var buf []byte = make([]byte, 50) // make sure the data string is less than 46 bytes
    offset := 0
    binary.BigEndian.PutUint16(buf[offset:], pkt.opcode)
    offset = offset + 2
    binary.BigEndian.PutUint16(buf[offset:], pkt.blk_no)
    offset = offset + 2
    bytes_copied := copy(buf[offset:], pkt.data)
    

    我希望这可以大致了解如何将 []byte 流转换为 struct 并将 struct 转换回 []byte 流。

    【讨论】:

      【解决方案3】:

      unsafe.Pointer 是,嗯,不安全,你实际上并不需要它。请改用encoding/binary 包:

      // Create a struct and write it.
      t := T{A: 0xEEFFEEFF, B: 3.14}
      buf := &bytes.Buffer{}
      err := binary.Write(buf, binary.BigEndian, t)
      if err != nil {
          panic(err)
      }
      fmt.Println(buf.Bytes())
      
      // Read into an empty struct.
      t = T{}
      err = binary.Read(buf, binary.BigEndian, &t)
      if err != nil {
          panic(err)
      }
      fmt.Printf("%x %f", t.A, t.B)
      

      Playground

      如您所见,它非常巧妙地处理大小和字节序。

      【讨论】:

        【解决方案4】:

        你必须使用 unsafe,uint 在 64 位系统上是 8 个字节,如果你想要 4 个字节,你必须使用 uint32。

        这是丑陋的,不安全的,你必须自己处理字节序。

        type packet struct {
            opcode uint16
            data   [1022]byte
        }
        
        type file_info struct {
            file_size uint32     // 4 bytes
            file_name [1018]byte //this struct has to fit in packet.data
        }
        
        func makeData() []byte {
            fi := file_info{file_size: 1 << 20}
            copy(fi.file_name[:], []byte("test.x64"))
            p := packet{
                opcode: 1,
                data:   *(*[1022]byte)(unsafe.Pointer(&fi)),
            }
            mem := *(*[1022]byte)(unsafe.Pointer(&p))
            return mem[:]
        }
        
        func main() {
            data := makeData()
            fmt.Println(data)
            p := (*packet)(unsafe.Pointer(&data[0]))
            if p.opcode == 1 {
                fi := (*file_info)(unsafe.Pointer(&p.data[0]))
                fmt.Println(fi.file_size, string(fi.file_name[:8]))
            }
        }
        

        play

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多