2014-10-14 29 views
6

Piszę aplikację klient - serwer w trybie Go. Chcę wykonać rzut typu C w Go.Przejść do konwersji między tablicą struktury i bajtu

E.g. Przejdź

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

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

Również chcę wykonać C-jak memcpy(), która pozwoli mi bezpośrednio odwzorowywać strumień bajtów sieć otrzymała do struct.

np. z wyżej otrzymał 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) 
} 
+2

ja polecam próbuje napisać go w pierwszy. Po prostu nie zrobiłbyś czegoś takiego w podróży. Między innymi, go nie ma castingu. 'uint' również nie jest 4 bajtami. 'Conn.Read' pobiera bajt' [], który jest efektywnie sprytnym wskaźnikiem o wielkości w tablicy. Będziesz miał o wiele lepszy czas, pisząc to w biegu. – Dustin

+1

Czy już obejrzałeś wbudowaną serializację binarną za pomocą pakietu 'encoding/gob'? – dyoo

Odpowiedz

2

Trzeba by użyć niebezpieczne, również uint jest 8 bajtów na systemach 64-bitowych, trzeba użyć uint32 jeśli chcesz 4 bajty.

Jest brzydki, niebezpieczny i sam musisz sobie poradzić z beznadziejnością.

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

13

unsafe.Pointer jest dobrze, niebezpieczna, a nie faktycznie trzeba go tutaj. Użyj encoding/binary pakiet Zamiast:

// 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

Jak widać, obsługuje rozmiary i kolejność bajtów całkiem zgrabnie.

2

Dziękuję za odpowiedzi i jestem pewien, że działają doskonale. Ale w moim przypadku bardziej interesowało mnie analizowanie bufora bajtów [] odbieranego jako pakiet sieciowy. Użyłem następującej metody do analizy bufora.

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 

Podczas konstruowania [] strumień bajtów z struct, można użyć następujących metod

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) 

Mam nadzieję, że to daje ogólne pojęcie o tym, jak konwertować [] strumień bajtów do struct i struct z powrotem do [] strumień bajtów.

1

Miałem ten sam problem i rozwiązałem go, używając pakietu "encoding/binary". Oto przykład:

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 
} 

Oto uruchomiony przykład: https://play.golang.org/p/Q3LjaAWDMh