在 go 中使用来自网络的原始字节

2024-04-12

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

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

该协议是按照小端顺序的字节来定义的,因此在我的 C++ 实现中,数据包标头如下所示(我知道,它仅适用于 LE 机器):

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

Upon 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, size = 03, flags = 0100 }

我是否需要实现相反的功能,或者是否有更好的方法从字节数组转换为任意结构(反之亦然,与我的函数相反)?如果任何额外的说明会有帮助,请告诉我。


正确的方法是使用编码/二进制 http://golang.org/pkg/encoding/binary它的内部功能与您上面所写的差不多。

(操场 http://play.golang.org/p/3dhyT0EVti)

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}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 go 中使用来自网络的原始字节 的相关文章

随机推荐

  • python 原始字符串符号抛出带有尾部斜杠的错误

    我正在尝试使用原始字符串表示法在 python 中设置字符串变量的路径 但出现尾部斜杠错误 datapath r C path to my data 给我一个 扫描字符串文字时 EOL 错误 我认为原始字符串表示法应该将所有内容都包含在字符
  • 如何删除 HTML 正文中签名上方自动生成的行?

    When I Display邮件 要添加签名 签名上方添加两行空行 有没有办法删除它们 使邮件看起来更好 而不丢失签名格式 With objOutlookMsg SentOnBehalfOfName email protected cdn
  • getResource 使用 java 1.7 windows 7 在磁盘名称之前放置一个前导 /

    以下在磁盘名称之前给出了一个前导斜杠 我怎样才能避免这种情况 String pngpath getClass getResource resources image png getPath System out println pngpat
  • Restcomm gmlc 配置问题 主要地址不存在本地 SSN

    我在尝试在 Windows Server 2008 R2 上配置 Restcomm gmlc 时遇到问题 我收到这条消息 下图是HLR和Windows服务器的设置 以下是设置 GmlcManagement gmlcproperties xm
  • 如何用 Cocoa 获得准确的分贝值?

    我们正在创建一个应用程序 它可以记录周围的声音 并在声音超过指定分贝时采取必要的措施 为了实现应用程序目标 我们使用 AudioQueueObject h 中的以下方法 void getAudioLevels Float32 levels
  • innerHTML 将 CDATA 转换为注释

    我正在尝试使用 javascript 将一些 HTML 插入到页面中 并且我插入的 HTML 包含 CDATA 块 我发现 在 Firefox 和 Chrome 中 CDATA 正在转换为注释 HTML 不在我的控制之下 所以我很难避免使用
  • 在不知道编码的情况下如何读取编码头?

    如果我正在读取 HTML 文件的 XML 我是否必须读取告诉我编码的标签才能读取该文件 该标签的编码方式与文件的编码方式不一样吗 我很好奇你如何在不知道编码的情况下读取该标签 我意识到这个问题已经解决了 我只是好奇它是如何完成的 Updat
  • 在计算机上找不到 MySQL

    我最近直接从网站 mysql 5 7 9 osx10 10 x86 64 dmg 安装了MySQL 并且在尝试更改给定密码时遇到了很大的困难 返回的错误是 警告 在命令行界面上使用密码可能会导致 没有安全感 mysqladmin 连接到 l
  • Java:从 FTP 服务器访问文件

    我有一个 FTP 服务器 里面有一堆文件夹和文件 我的程序需要访问该服务器 读取所有文件并显示它们的数据 出于开发目的 我一直在使用硬盘驱动器上 src 文件夹中的文件 但现在服务器已启动并运行 我需要将软件连接到它 基本上我想要做的是获取
  • 是否可以获取 Stream 中的下一个元素?

    我正在尝试转换for loop到功能代码 我需要向前看一个值 也需要向后看一个值 是否可以使用流 以下代码是将罗马文本转换为数值 不确定带有两个 三个参数的reduce方法是否可以在这里提供帮助 int previousCharValue
  • 在 Windows 环境中使用 Gnustep 在 Objective-C 中进行编程

    我正在使用 Windows 并且想开始学习如何使用 Objective C 进行编程 我安装了 Gnustep 但无法确定如何构造和构建项目 等效 也找不到 IDE 请参阅Gnustep 入门 https stackoverflow com
  • 选择com的理由

    我想知道为什么人们会选择 Com 作为他的软件开发 技术 我的第一个想法是机器 编程语言的独立性 你的是啥呢 COM 是 Windows 上自动化和 IPC 的事实上的标准 尽管 Net 已经开始转移焦点 因此有些领域您根本没有 或没有 选
  • 在python中查找关键字后的单词[重复]

    这个问题在这里已经有答案了 我想查找出现在关键字 由我指定和搜索 之后的单词并打印出结果 我知道我应该使用正则表达式来做到这一点 我也尝试了一下 如下所示 import re s hi my name is ryan and i am ne
  • 如何从 Django 中的请求对象获取 URI?

    如何从 Django 中的请求对象获取 URI 有 request uri 吗 request META REQUEST URI or request get full path 你往往会产生大量琐碎的问题 你可以在文档 谷歌中轻松找到答案
  • NSTokenField 捕获一些 NSEvents

    我需要为 NSTokenField 和解决方案实现 Command Enter Command O 和 Esc 快捷键https stackoverflow com a 18486965 1067147 https stackoverflo
  • 如何使用 Javascript 访问网络摄像头 [重复]

    这个问题在这里已经有答案了 我用谷歌搜索了很长一段时间关于这个话题 没有任何令人满意的结果 实际上我的基本问题是 有没有可能使用 Javascript 访问网络摄像头 也许用 HTML5
  • jQuery .val() 与 .attr("value")

    我本来以为这两个是一样的 但看起来不是 我一般都用过 obj attr value 使用表单字段 但在我当前正在构建的页面上 obj attr value 不返回我在字段中输入的文本 然而 obj val does 在我构建的另一个页面上
  • 自定义验证错误的自动响应

    在 asp net core 2 1 中 当发生验证错误时 ApiController 将自动响应 400 BadRequest 如何更改 修改发送回客户端的响应 json body 有某种中间件吗 我正在使用 FluentValidati
  • 使用 Celery(RabbitMQ、Django)检索队列长度

    我在 django 项目中使用 Celery 我的代理是 RabbitMQ 我想检索队列的长度 我浏览了 Celery 的代码 但没有找到执行此操作的工具 我在 stackoverflow 上发现了这个问题 从客户端检查 RabbitMQ
  • 在 go 中使用来自网络的原始字节

    抱歉 问题很长 我最近一直在尝试使用 Go 而不是 C 来开发一个游戏服务器模拟器 我正在将其作为一个业余项目进行开发 并质疑我是否以合理的 Go 术语来实现它 正如您所料 服务器通过发送符合特定协议规范的原始数据包 TCP 与一个或多个游