当前位置: 首页 > 面试题库 >

使用来自网络的原始字节

鲁钱明
2023-03-14
问题内容

(对不起,还有很长的问题!)我最近一直在尝试将Go(而不是C
++)用于我作为辅助项目正在开发的游戏html" target="_blank">服务器模拟器,并质疑我是否以明智的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}


 类似资料:
  • 问题内容: 我只想从Web应用程序中使用PHP发送SMS。谁能告诉我该怎么做?我需要为此做些什么? 问题答案: 我不知道这是否适用于您,但是为了省钱我做了很多次,就是在个人资料中询问用户他的载体是什么,然后尝试与匹配。本质上,许多运营商/大多数运营商都将电子邮件地址连接到电话号码,您可以轻松地将短信发送到该号码。例如,如果您拥有ATT,并且电话号码是786-262-8344,则发送至7682628

  • 我想为Netty处理程序使用不同于套接字的输入。这可能吗?如果是这样,什么是有效的输入格式?

  • 有一个请求队列,回复队列由客户端服务器实例创建,并固定到每个实例,而不是使用临时队列。 用例需要获取入站jms消息,然后将该消息发送到异步进程。一旦从服务接收到异步回复消息,我需要获取这些结果并回复原始消息的jmsReplyTo。Jms网关在这种情况下不起作用AFAIK 我正在使用jms消息驱动的通道适配器来处理带有一系列通道和服务激活器的消息,以处理进程外调用和异步回复。我正在尝试使用Dynam

  • 我的Android项目中的raw文件夹中有各种音频文件。我的代码不起作用。当我单击共享按钮应用程序时,它崩溃了。 这是我的代码: 和Android清单添加:

  • 假设我有一个非常大的对象集合(数百万个),这些对象是根据proto-wire格式序列化的。是否可以从文件中流式处理这些项目?我尝试将对象序列化为列表 我怀疑解决方案需要我知道每个序列化项的大小,然后从流中读取该大小,并将该字节跨度传递给protobuf序列化程序进行反序列化。我想确保没有一种更简单的机制来完成这项任务,这种机制不需要了解每个对象实例中可能不同的单个项目的长度。 我的另一个想法是在流

  • Docker 允许通过外部访问容器或容器互联的方式来提供网络服务。