(Извините, давно вопрос!) Я недавно тестировал Go, а не C++ для эмулятора игрового сервера. Я работаю как побочный проект, и задаюсь вопросом, он реализует его в разумных условиях Go. Как и следовало ожидать, сервер связывается с одним или несколькими игровыми клиентами, отправляя необработанные пакеты (TCP), которые придерживаются конкретной спецификации протокола. Соответствующая часть выглядит примерно так:Работа с необработанными байтами из сети в go
получить заголовок -> расшифровать его -> recv байт до достижения длины заголовка -> расшифровать оставшуюся часть пакета -> отправить в обработчик -> декодировать пакет -> обращаться по мере необходимости -> отправить ответ
протокол определен в байтах в маленьком порядке Endian, так что в моем C++ реализации заголовок пакета выглядит следующим образом (я знаю, он работает только на машинах LE):
struct pkt_header {
uint16_t length;
uint16_t type;
uint32_t flags;
};
recv() 'ing и расшифровать этот заголовок, я извлечу его DS:
// 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
В самих могу гнездо вышеуказанной-структуры заголовка в других определениях структуры пакетов и отбрасывать те, на байт [] буферных массивов для того, чтобы непосредственно получить доступ к полям обработчиков. Из того, что я читал, выравнивание структуры (неудивительно) сложно или невозможно, и очень разочаровывается в Go.
Не зная, что еще сделать, я написал эту функцию для перехода от произвольного Struct -> [] байт:
// 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]
As a Новичок, обратная связь о том, как я могу реализовать это более эффективно, более чем приветствуется. Пока все хорошо, но теперь я столкнулся с проблемой, как сделать обратное: перейдите из [] byte-> Struct (например, [C8 00 03 00 00 01 00 00] в заголовок { length = C8, size = 03, flags = 0100}
Нужно ли просто реализовать обратное это или есть лучший способ перехода от массива байтов к произвольной структуре (или наоборот, в противоположность к моей функции)? Пожалуйста, дайте мне знать, если какая-либо дополнительная ясность была бы полезно.
Отлично, спасибо! Я должен был спросить, прежде чем я пошел и написал свою собственную функцию. По крайней мере, это было интересно ... – drodman