Estoy tratando de encontrar el mejor enfoque para transferir algunos datos a través de la red. Esto es lo que espero lograr:
La aplicación ejecuta y calcula algunos datos:
int w = 5;
float x = 4.736;
std::string y = "Some String.";
std::vector<int> z;
z.push_back(1);
z.push_back(2);
z.push_back(3);
Luego la ponemos en un recipiente binario:
BinaryContainer Data;
Data.Write(w);
Data.Write(x);
Data.Write(y);
Data.Write(z);
Luego lo transferimos a través de la red:
SendData(Data.c_str());
Y léelo en el otro lado:
BinaryContainer ReceivedData(IncomingData);
int w = ReceivedData.Read();
float x = ReceivedData.Read();
std::string y = ReceivedData.Read();
std::vector<int> z = ReceivedData.Read();
El ejemplo anterior describe cómo debería funcionar la funcionalidad básica desde una perspectiva de alto nivel. He examinado muchas bibliotecas de serialización diferentes y ninguna parece encajar del todo bien. Me inclino por aprender a escribir la funcionalidad yo mismo.
Endianness no importa. La arquitectura que lee y escribe datos nunca será diferente. Solo necesitamos almacenar datos binarios dentro del contenedor. La aplicación de lectura y la aplicación de escritura son exclusivamente responsables de leer los datos en el mismo orden en que fueron escritos. Solo se necesitan escribir tipos básicos, no clases arbitrarias completas o punteros a las cosas. Lo más importante en general es que la velocidad a la que esto ocurre debe ser de máxima prioridad porque una vez que se formulan los datos, debemos escribirlos en el contenedor, transferirlos a través de la red y leerlos en el otro extremo lo más rápido posible.
La transmisión de red se está realizando actualmente utilizando la API WinSock RIO de bajo nivel y ya estamos trasladando los datos de la aplicación al cable lo más rápido posible. La latencia de transmisión a través del cable siempre será una tasa mucho más alta y variable. El punto en el que serializamos nuestros datos antes de la transmisión es el siguiente paso en la cadena para garantizar que estamos desperdiciando el menor tiempo posible antes de enviar nuestros datos al cable.
Los nuevos paquetes se recibirán muy rápidamente y, como tal, la capacidad de preasignar recursos sería beneficiosa. Por ejemplo:
Serializer DataHandler;
...
void NewIncomingPacket(const char* Data)
{
DataHandler.Reset();
DataHandler.Load(Data);
int x = DataHandler.Read();
float y = DataHandler.Read();
...
}
Estoy buscando aportes de expertos de la comunidad en qué dirección ir aquí.
2 respuestas
Si no le importa el endianness y solo desea serializar tipos triviales, una simple memoria será la más rápida y segura. Simplemente ingrese / salga del búfer al serializar / deserializar.
#include <iostream>
#include <vector>
#include <cstring>
#include <cstdint>
#include <type_traits>
#include <cstddef>
template <std::size_t CapacityV>
struct BinaryContainer
{
BinaryContainer() :
m_write(0),
m_read(0)
{
}
template <typename T>
void write(const std::vector<T>& vec)
{
static_assert(std::is_trivial_v<T>);
// TODO: check if access is valid
const std::size_t bytes = vec.size() * sizeof(T);
std::memcpy(m_buffer + m_write, vec.data(), bytes);
m_write += bytes;
}
template <typename T>
void write(T value)
{
static_assert(std::is_trivial_v<T>);
// TODO: check if access is valid
const std::size_t bytes = sizeof(T);
std::memcpy(m_buffer + m_write, &value, bytes);
m_write += bytes;
}
template <typename T>
std::vector<T> read(std::size_t count)
{
static_assert(std::is_trivial_v<T>);
// TODO: check if access is valid
std::vector<T> result;
result.resize(count);
const std::size_t bytes = count * sizeof(T);
std::memcpy(result.data(), m_buffer + m_read, bytes);
m_read += bytes;
return result;
}
template <typename T>
T read()
{
static_assert(std::is_trivial_v<T>);
// TODO: check if access is valid
T result;
const std::size_t bytes = sizeof(T);
std::memcpy(&result, m_buffer + m_read, bytes);
m_read += bytes;
return result;
}
const char* data() const
{
return m_buffer;
}
std::size_t size() const
{
return m_write;
}
private:
std::size_t m_write;
std::size_t m_read;
char m_buffer[CapacityV]; // or a dynamically sized equivalent
};
int main()
{
BinaryContainer<1024> cont;
{
std::vector<std::uint32_t> values = {1, 2, 3, 4, 5};
// probably want to make serializing size part of the vector serializer
cont.write(values.size());
cont.write(values);
}
{
auto size = cont.read<std::vector<std::uint32_t>::size_type>();
auto values = cont.read<std::uint32_t>(size);
for (auto val : values) std::cout << val << ' ';
}
}
Demostración: http://coliru.stacked-crooked.com/a/4d176a41666dbad1
He escrito en serio, una biblioteca de C ++ rápida de solo encabezado que debería hacer lo que quieras :-)
Proporciona un serializador y un deserializador.
Los datos serializados son portables en diferentes arquitecturas y endianness. Sin dependencias externas.
seriously::Packer<1024> packer; // a 1024 byte serialization buffer
int32_t value1 = 83656;
bool value2 = true;
int16_t value3 = -2345;
std::string value4("only an example");
double value5 = -6.736;
std::vector<int64_t> value6;
value6.push_back(42);
value6.push_back(11);
value6.push_back(93);
packer << value1 << value2 << value3 << value4 << value5 << value6;
std::cout << "packed size: " << packer.size() << std::endl;
// packer.data() contains the serialized data
int32_t restored1;
bool restored2;
int16_t restored3;
std::string restored4;
double restored5 = -6.736;
std::vector<int64_t> restored6;
packer >> restored1 >> restored2 >> restored3 >> restored4 >> restored5 >> restored6;
std::cout << "unpacked: " << restored1 << " " << (restored2 ? "t" : "f") << " " << restored3 << " " << restored4 << " " << restored5 << std::endl;
std::vector<int64_t>::const_iterator it;
for (it = restored6.begin(); it != restored6.end(); it++) {
std::cout << *it << std::endl;
}
Nuevas preguntas
c++
C ++ es un lenguaje de programación de propósito general. Originalmente fue diseñado como una extensión de C y tiene una sintaxis similar, pero ahora es un lenguaje completamente diferente. Utilice esta etiqueta para preguntas sobre el código (a ser) compilado con un compilador C ++. Utilice una etiqueta de versión específica para preguntas relacionadas con una revisión estándar específica [C ++ 11], [C ++ 14], [C ++ 17], [C ++ 20] o [C ++ 23], etc. .