Расклад в голанге

Реализую сетевой пакет в голанге. Это уже было реализовано на C++. Цель состоит в том, чтобы заставить клиент, реализованный в golang, взаимодействовать с сервером, реализованным на C++.

Они будут общаться по пакету. Структура пакета:

type Packet struct {
    length   uint32
    nameLen  uint8
    data     []byte
} // in golang

struct Packet {
    uint32_t length;
    uint8_t  nameLen;
    byte     data[];
} // in C++

Их подчеркнутая структура представляет собой массив байтов. При получении сообщения в формате байтового массива. Нам нужно перевести это в Packet.

auto p = reinterpret_cast<Packet*>(buffer); // in c++
(buffer's alignment is manually set as 64)

p := (Packet)(unsafe.Pointer(&buffer)) // in golang

Чтобы они могли общаться, их структура должна оставаться неизменной.

Возникает вопрос: Распечатав их выравнивание, я получаю следующее:

type Packet struct {
    length  uint32 // alignment 8
    nameLen uint8  // alignment 8
    data    []byte // alignment 8
}
struct Packet {
    uint32_t length;  // alignment 4
    uint8    nameLen; // alignment 4
    data     []byte;  // alignment 1
}

Они будут декодировать сообщение по-разному из-за разного выравнивания.

Не могу изменить код C++.

Q1: Есть ли способ настроить выравнивание полей структуры в golang?

Q2: Есть ли лучший способ реализовать пакет golang, чтобы избежать несоответствия выравнивания при преобразовании буфера в пакет?

Q2: Очень, очень да. Распаковывайте поля индивидуально, используя encoding/binary, вместо того, чтобы рассчитывать на распаковку всего этого сразу на разных языках и архитектурах.

Adrian 13.12.2018 22:00

Q1: Нет. Q2: Да: Занимайтесь программированием вместо магии.

Volker 13.12.2018 22:51

Спасибо вам, ребята. Распаковываю поля по одному.

Cubey Mew 24.12.2018 20:55
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
5
3
2 315
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Согласно комментарию Адриана и Волкера,

Q1: Нет

Q2: Поупражняйтесь в программировании, чтобы распаковывать поля по отдельности.

В пакете кодирование / двоичный у нас есть

func PutUVarint

Он кодирует uint64 в buf и возвращает количество записанных байтов. Почему-то в этом пакете нет общедоступной функции для кодирования uint32. Итак, я сделал нечто подобное:

func PutUint32(b []byte, v uint32) {
    _ = b[3] // early check
    b[0] = byte(v)
    b[1] = byte(v >> 8)
    b[2] = byte(v >> 16)
    b[3] = byte(v >> 24)
} // assume it's littleEndian

// to store packet length into buffer.
PutUint32(buffer, pkt.length)

Вы можете сделать то же самое, что и c / C++, но небезопасно.

Просто используйте массив байтов фиксированной ширины увеличенного размера.

С учетом сказанного вам нужно сделать свою собственную проверку границ.

package main

import (
    "unsafe"
    "runtime"
    "reflect"
    "fmt"
)

type Packet struct {
    length  uint32     // alignment 4
    nameLen uint8      // alignment 1
    data    [2048]byte // alignment 1
}

func NewPacketFromBuffer(buffer []byte) *Packet {
    sh := (*reflect.SliceHeader)(unsafe.Pointer(&buffer))
    packet := (*Packet)(unsafe.Pointer(sh.Data))
    runtime.KeepAlive(buffer) // So the buffer will not be garbage collected
    return packet
}


func main () {
   buffer := []byte{9,0,0,0,4,'d','a','t','a'};
   p := NewPacketFromBuffer(buffer)
   fmt.Printf("length : %d, nameLen : %d, name: %s\n", p.length, 
    p.nameLen, p.data[0:p.nameLen] )
}

Другие вопросы по теме