(对不起,还有很长的问题!)我最近一直在尝试将Go(而不是C ++)用于我作为辅助项目正在开发的游戏服务器模拟器,并质疑我是否以明智的Go术语来实现它。如您所料,服务器通过发送符合特定协议规范的原始数据包(TCP)与一个或多个游戏客户端进行通信。相关部分如下所示:
接收标头->解密->接收字节,直到达到标头长度->解密其余数据包->分派给处理程序->解码数据包->根据需要处理->发送响应
该协议是按照字节的形式以小端顺序排列的,因此在我的C ++实现中,数据包头看起来像这样(我知道,它仅适用于LE机器):
struct pkt_header { uint16_t length; uint16_t type; uint32_t flags; };
在 recv() 并解密此标头后,我将提取字段:
// 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
在处理程序本身中,我可以将上述标头结构嵌套在其他数据包结构定义中,并将其转换为 byte [] 缓冲区数组,以便直接访问字段。根据我的阅读,Go中的结构对齐(毫不奇怪)是困难/不可能的,并且强烈建议不要这样做。
我不知道该怎么办,我编写了此函数,以从任意Struct-> [] byte开始:
// 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] 转到标头{length = C8,大小= 03,标志= 0100}
我是否需要执行相反的操作,或者有更好的方法将字节数组转换为任意结构(反之亦然,与我的函数相反)?请让我知道是否进一步的说明会有所帮助。
可行的方法是使用编码/二进制,它内部完成了您上面编写的大部分工作。
(游乐场)
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}