使用Go中的网络处理原始字节 [英] Working with raw bytes from a network in go

查看:75
本文介绍了使用Go中的网络处理原始字节的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(对不起,很长的问题!)我最近一直在尝试将Go而不是C ++用作我作为辅助项目正在开发的游戏服务器模拟器,并质疑我是否以明智的Go术语来实现它.如您所料,服务器通过发送符合特定协议规范的原始数据包(TCP)与一个或多个游戏客户端进行通信.相关部分如下所示:

(Sorry, long question!) I've recently been trying out Go as opposed to C++ for a game server emulator I'm working on as a side project and am questioning whether I'm implementing it in sensible Go terms. As you might expect, the server communicates with one or more game clients by sending raw packets (TCP) that adhere to a particular protocol specification. The relevant part goes something like this:

接收标头->解密->接收字节,直到达到标头长度->解密数据包的其余部分->分发给处理程序->解码数据包->根据需要处理->发送响应

receive a header -> decrypt it -> recv bytes until the header length is reached -> decrypt the rest of the packet -> dispatch to a handler -> decode the packet -> handle as necessary -> send a response

协议是按照字节的顺序以小尾数顺序定义的,因此在我的C ++实现中,数据包头看起来像这样(我知道,它仅适用于LE机器):

The protocol is defined in terms of bytes in little endian order, so in my C++ implementation the packet header looks like this (I know, it only works on LE machines):

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

recv()上并解密此标头后,我将提取字段:

Upon recv()'ing and decrypting this header, I'll extract the fields:

// 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中的结构对齐(毫不奇怪)是困难/不可能的,并且强烈建议不要这样做.

In the handlers themselves I can nest the above header struct in other packet structure definitions and cast those onto the byte[] buffer arrays in order to directly access the fields. From what I've read, struct alignment (unsurprisingly) is difficult/impossible and highly discouraged in Go.

我不知道该怎么办,我编写了此函数,以便从任意Struct-> [] byte:

Not knowing what else to do, I wrote this function for going from an arbitrary 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()
}

并会在处理程序中执行此操作:

And would do this in a handler:

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] 到标题{长度= C8,大小= 03,标志= 0100}

As a Go newbie, feedback on how I might implement this more efficiently is more than welcome. So far it's worked well, but now I'm faced with the challenge of how to do the opposite: go from []byte->Struct (for example, [C8 00 03 00 00 01 00 00] to a Header { length = C8, size = 03, flags = 0100 }

我是否需要执行相反的操作,或者有更好的方法将字节数组转换为任意结构(反之亦然,与我的函数相反)?请让我知道是否进一步的说明会有所帮助.

Do I need to just implement the reverse of this or is there a better method of going from an array of bytes to an arbitrary struct (or vice versa, as opposed to my function)? Please let me know if any additional clarity would be helpful.

推荐答案

可行的方法是使用编码/binary 在内部几乎可以完成您上面所写的内容.

The go way would be to use encoding/binary which internally does pretty much what you've written above.

(游乐场)

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}

这篇关于使用Go中的网络处理原始字节的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆