2015-01-07 2 views
3

(Извините, давно вопрос!) Я недавно тестировал Go, а не C++ для эмулятора игрового сервера. Я работаю как побочный проект, и задаюсь вопросом, он реализует его в разумных условиях Go. Как и следовало ожидать, сервер связывается с одним или несколькими игровыми клиентами, отправляя необработанные пакеты (TCP), которые придерживаются конкретной спецификации протокола. Соответствующая часть выглядит примерно так:Работа с необработанными байтами из сети в go

получить заголовок -> расшифровать его -> recv байт до достижения длины заголовка -> расшифровать оставшуюся часть пакета -> отправить в обработчик -> декодировать пакет -> обращаться по мере необходимости -> отправить ответ

протокол определен в байтах в маленьком порядке Endian, так что в моем C++ реализации заголовок пакета выглядит следующим образом (я знаю, он работает только на машинах LE):

struct pkt_header { 
    uint16_t length; 
    uint16_t type; 
    uint32_t flags; 
}; 

recv() 'ing и расшифровать этот заголовок, я извлечу его DS:

// client->recv_buffer is of type u_char[1024] 
header = (pkt_header*) client->recv_buffer; 

if (client->recv_size < header->length) { 
    // Recv some more 
} 
// Decrypt and so on 

В самих могу гнездо вышеуказанной-структуры заголовка в других определениях структуры пакетов и отбрасывать те, на байт [] буферных массивов для того, чтобы непосредственно получить доступ к полям обработчиков. Из того, что я читал, выравнивание структуры (неудивительно) сложно или невозможно, и очень разочаровывается в Go.

Не зная, что еще сделать, я написал эту функцию для перехода от произвольного Struct -> [] байт:

// Serializes the fields of a struct to an array of bytes in the order in which the fields are 
// declared. Calls panic() if data is not a struct or pointer to struct. 
func StructToBytes(data interface{}) []byte { 
    val := reflect.ValueOf(data) 
    valKind := val.Kind() 
    if valKind == reflect.Ptr { 
     val = reflect.ValueOf(data).Elem() 
     valKind = val.Kind() 
    } 

    if valKind != reflect.Struct { 
     panic("data must of type struct or struct ptr, got: " + valKind.String()) 
    } 

    bytes := new(bytes.Buffer) 
    for i := 0; i < val.NumField(); i++ { 
     field := val.Field(i) 

     switch kind := field.Kind(); kind { 
     case reflect.Struct: 
      binary.Write(bytes, binary.LittleEndian, StructToBytes(field.Interface())) 
     case reflect.Array, reflect.Slice: 
      binary.Write(bytes, binary.LittleEndian, field.Interface()) 
     case reflect.Uint8: 
      binary.Write(bytes, binary.LittleEndian, uint8(field.Uint())) 
     case reflect.Uint16: 
      binary.Write(bytes, binary.LittleEndian, uint16(field.Uint())) 
     // You get the idea 
     } 
    } 
    return bytes.Bytes() 
} 

И будет делать это в обработчике:

type Header struct { 
    length uint16 
    size uint16 
    flags uint32 
} 
newHeader := new(Header) 
// Initialization, etc 
client.Conn.Write(StructToBytes(newHeader)) // ex. [C8 00 03 00 00 00 01 00] 

As a Новичок, обратная связь о том, как я могу реализовать это более эффективно, более чем приветствуется. Пока все хорошо, но теперь я столкнулся с проблемой, как сделать обратное: перейдите из [] byte-> Struct (например, [C8 00 03 00 00 01 00 00] в заголовок { length = C8, size = 03, flags = 0100}

Нужно ли просто реализовать обратное это или есть лучший способ перехода от массива байтов к произвольной структуре (или наоборот, в противоположность к моей функции)? Пожалуйста, дайте мне знать, если какая-либо дополнительная ясность была бы полезно.

ответ

4

ходу путь будет использовать encoding/binary который внутренне делает довольно много, что вы написали выше.

(playground)

package main 

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

type Header struct { 
    Length uint16 
    Size uint16 
    Flags uint32 
} 

func main() { 
    header := &Header{Length: 0xC8, Size: 3, Flags: 0x100} 
    fmt.Printf("in = %#v\n", header) 
    buf := new(bytes.Buffer) 

    err := binary.Write(buf, binary.LittleEndian, header) 
    if err != nil { 
     log.Fatalf("binary.Write failed: %v", err) 
    } 
    b := buf.Bytes() 
    fmt.Printf("wire = % x\n", b) 

    var header2 Header 
    buf2 := bytes.NewReader(b) 
    err = binary.Read(buf2, binary.LittleEndian, &header2) 
    if err != nil { 
     log.Fatalf("binary.Read failed: %v", err) 
    } 
    fmt.Printf("out = %#v\n", header2) 
} 

который печатает

in = &main.Header{Length:0xc8, Size:0x3, Flags:0x100} 
wire = c8 00 03 00 00 01 00 00 
out = main.Header{Length:0xc8, Size:0x3, Flags:0x100} 
+0

Отлично, спасибо! Я должен был спросить, прежде чем я пошел и написал свою собственную функцию. По крайней мере, это было интересно ... – drodman