2015-01-07 62 views
3

(對不起,很長的問題!)我最近一直在嘗試Go而不是C++作爲遊戲服務器模擬器,我正在作爲一個側面項目工作,並且質疑我在明智的Go條款中實施它。正如您所預料的那樣,服務器通過發送符合特定協議規範的原始數據包(TCP)與一個或多個遊戲客戶端進行通信。相關部分是這樣的:使用來自網絡中的原始字節go

接收頭 - >解密 - > recv的字節,直到頭長度達到 - >解密報文的其餘部分 - >發送到處理程序 - >解碼包 - >根據需要處理 - >發送響應

該協議是在小尾序字節來定義的,所以在我的C++實現數據包報頭看起來像這樣(我知道,它僅適用於LE機):

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

recv()''並解密此標頭,我將提取該標記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中很困難/不可能,並且極其灰心。

不知道自己能做什麼,我寫了這個功能從任意結構去 - > []字節:

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

由於一個Go新手,關於如何更高效地實現這一點的反饋非常值得歡迎。到目前爲止,它運行良好,但現在我面臨着如何做相反的挑戰:從[] byte-> Struct(例如,[C8 00 03 00 00 01 00 00])轉到Header {長度= C8,大小= 03,標誌= 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