可以将protobuf元帅消息发送到已分配的字节数组而无需复制吗?

时间:2019-01-15 17:08:25

标签: go protocol-buffers ipc packet

我正在通过自定义数据包实现客户端服务器通信。 我正在使用Go net.conn。它可以拨打tcp / unix方案,这非常方便。我使用protocol buffer定义我的消息。

我定义了一个包含lengthbuffer的数据包

type Packet struct {
    length uint32
    buffer []byte
}

API函数如下:
func(api *API) Send(m *proto.Message) error
func(api *API) Receive(p *Packet) error

send函数为例,它接收一个protobuf消息,并将其封送到Packet中。并将其写入net.conn

以下是“发送”功能的简化版本:

func(api *API) Send(m *proto.Message) error {
    bytes, err := proto.Marshal(m)
    if err != nil {
        return err
    }
    buffer := api.packet[:length]
    copy(buffer, bytes)
    _, err := api.conn.Write(buffer)
    if err != nil {
        return err
    }
    return nil
}

我正在将bytes复制到buffer中。因为Go协议缓冲区API仅提供
 func Marshal(pb Message) ([]byte, error)

在协议缓冲区C ++中,它提供了 bool SerializeToArray(void * data, int size) const,它正在序列化消息并将其存储在给定的字节数组中。 但是我在Go协议缓冲区API中找不到相同的东西。

如果我想直接将序列化结果存储在给定的字节数组中,是否可以避免复制?

3 个答案:

答案 0 :(得分:0)

不清楚您要问什么。注意,原型Marshal()函数完全可以满足您的需求:它将消息序列化为字节片(字节数组可能意味着什么)

查看以下任一帮助:

func(api *API) Send(m *proto.Message) error {
    p := Packet{}
    p.buffer, err := proto.Marshal(m)
    if err != nil {
        return err
    }
    _, err := api.conn.Write(p.buffer)
    if err != nil {
        return err
    }
    return nil
}

func(api *API) Send(m *proto.Message) error {
    buffer := api.packet[:length]
    buffer, err := proto.Marshal(m)
    if err != nil {
        return err
    }
    _, err := api.conn.Write(buffer)
    if err != nil {
        return err
    }
    return nil
}

答案 1 :(得分:0)

似乎您可以使Packet.buffer成为proto.Buffer

type Packet struct {
    length uint32
    buffer proto.Buffer
}
...
var packet Packet
packet.length = YouLength
packet.buffer = proto.NewBuffer(make([]byte, YouLength))
//Then you can Marshall in Packet directly and it  may be reused.
err := packet.Marshal(message)

答案 2 :(得分:0)

您正在从gogo/protobuf寻找MarshalTo方法,这是protobuf的另一种实现,与原始协议兼容。

当您将要填充的缓冲区传递给它时,可以通过多个封送调用重复使用同一缓冲区。显然,缓冲区应该足够大。

func MarshalTo([]byte, m) error