Как избежать межъязыковой зависимости при сериализации / десериализации с использованием TCP?

Мне нужно создать TCP-чат с клиентами C ++ и сервером Python (уже запущен), у меня есть сообщения в классе C ++, такие как

class Message{
public:
uint64 utc_time;
uint64 token;
string content;
};

Я отправляю это от клиента к серверу, на сервере у меня есть очередь с приоритетами по utc_time и мне нужно передать другим. У меня вопрос, как это сериализовать, какой формат использовать, чтобы избежать любых межъязыковых зависимостей от размера шрифта? (может быть, в будущем будет больше метаданных, так что нужно немного общего)? Кто-нибудь может дать мне совет, какой формат использовать для сериализации (или сбрасывать только как байты)?

class Persistent:
public:
Persistent(int sz):objSize(sz){}
void write(std::ostream& out)const{out.write((char*)this, objSize);}
void read(std::istream& in){in.read((char*)this, objSize);}
private:
int objSize;
};

Я подумал о другой возможности иметь десериализатор в c ++ на сервере и вызывать из python, если это возможно. Любое элегантное решение этой проблемы?

3

Решение

Если вы действительно хотите перейти на разные языки и кроссплатформенность, не беспокоясь о том, где заканчивается сообщение, взгляните на комбинацию Google Protobuf а также ZeroMQ.

При использовании обычных сокетов вы сначала должны прочитать размер сообщения (вы должны добавить это), а затем узнать, откуда и где массив байтов является полным сообщением.

Пример использования protobuf + zmq:

message Message {
optional uint64 utc_time = 1;
required uint64 token = 2;
optional string content = 3;
}

Используйте компилятор protobuf для генерации кода C ++ (или ruby ​​/ python / etc).

Чтобы использовать его в своем коде:

#include <Message.pb.h>

Message msg;
msg.set_token(1);
msg.set_content("Hello world");

Чтобы отправить его с помощью zmq:

std::string serialized = msg.SerializeAsString();
zmq::message_t reply(serialized.size());
memcpy(reply.data(), serialized.data(), serialized.size());
zmq_socket.send(reply);

Чтобы получить его с помощью zmq:

zmq::message_t request;
zmq_socket.recv(&request); // blocking
Message recv_msg;
recv_msg.ParseFromArray(request.data(), request.size());
4

Другие решения

Использование ZeroMQ — хорошее начало, так как оно берет на себя всю транспортную работу за вас. Лучший способ сериализации зависит от вида работы, которую вы делаете. Поскольку вы работаете с приложением чата, эффективность не имеет значения, поэтому я бы использовал текстовый формат с самоописанием, который проще всего отлаживать, отслеживать, регистрировать и использовать. Что-нибудь вроде protobufs или msgpack будет дополнительной работой без ощутимой награды. Вы можете использовать XML, JSON, заголовки в стиле HTTP, пары имя = значение и т. Д.

Когда вы начинаете создавать действительно большие объемы сообщений (сотни тысяч в секунду) или очень большие (более 1 Кбайт), вы можете начать думать о различных способах уменьшения размера сообщения. Я лично рекомендую придерживаться простейшего из возможных текстовых форматов, пока у вас не возникнет проблема с производительностью, а затем переключиться на наиболее эффективный двоичный формат для случаев, когда это необходимо. Но не в приложении чата …

1

Мне нравится использовать JSON, при условии, что у вас есть хороший буферизованный интерфейс для сокета и анализатор JSON на основе потоков. Хорошая вещь в JSON заключается в том, что вам не нужно указывать длину каждого сообщения. Правильно написанный JSON-анализатор может определить, когда он достигает конца «объекта». Таким образом, ваш объект чтения может просто анализировать JSON, когда он проходит по проводам, и когда вы достигнете конца исходного объекта, верните его в систему как одно сообщение.

Если JSON является избыточным для ваших данных, всегда есть простой текст. Большая часть интернета работает с открытым текстом (POP, IMAP, HTTP, FTP и т. Д.). И это потому, что простой текст — это самая простая вещь для использования кросс-платформенного / кросс-языкового.

1
По вопросам рекламы [email protected]