message.h
message.cc
server.cc
client.cc
В этом примере мы создаем простой протокол связи и используем его для реализации сервера и клиента. Сервер принимает сообщение от клиента, преобразует его в верхний регистр и отправляет обратно.
Сообщение протокола состоит из 4-байтового заголовка и тела сообщения. Заголовок представляет собой целое число в сетевой последовательности, указывающее на размер тела. Формат запросов и ответов одинаков.
Для использования пользовательского протокола требуется предоставление методов сериализации и десериализации. Эти методы являются виртуальными функциями класса ProtocolMessage
. Также рекомендуется реализовать перемещающие конструкторы и оператор присваивания для удобства использования.
В ProtocolMessage.h представлены следующие интерфейсы:
namespace protocol
{
class ProtocolMessage : public CommMessageOut, public CommMessageIn
{
private:
virtual int encode(struct iovec vectors[], int max);
/* Вы должны реализовать одно из этих 'append' функций, а первая с аргументом 'size_t *size' рекомендована. */
virtual int append(const void *buf, size_t *size);
virtual int append(const void *buf, size_t size);
...
};
}
В нашем примере сериализация и десериализация очень просты.
Заголовочный файл message.h объявляет классы request и response:
namespace protocol
{
class TutorialMessage : public ProtocolMessage
{
private:
virtual int encode(struct iovec vectors[], int max);
virtual int append(const void *buf, size_t size);
...
};
using TutorialRequest = TutorialMessage;
using TutorialResponse = TutorialMessage;
}
Классы request и response представляют один и тот же тип сообщения. Они используются через using.
Обратите внимание, что request и response должны иметь возможность создания без параметров, то есть должны существовать конструкторы без параметров или вообще отсутствовать.
Кроме того, при повторной попытке соединения объект response будет использоваться заново.
Request и Response классы позволяют нам создать сервер и клиент, работающие через наш Tutorial протокол. В предыдущих примерах мы рассматривали типы данных для HTTP протокола:
using WFHttpTask = WFNetworkTask<protocol::HttpRequest,
protocol::HttpResponse>;
using http_callback_t = std::function<void (WFHttpTask *)>;
using WFHttpServer = WFServer<protocol::HttpRequest,
protocol::HttpResponse>;
using http_process_t = std::function<void (WFTutorialTask *)>;
Аналогично, для нашего Tutorial протокола типы данных будут такими:
using WFTutorialTask = WFNetworkTask<protocol::TutorialRequest,
protocol::TutorialResponse>;
using tutorial_callback_t = std::function<void (WFTutorialTask *)>;
using WFTutorialServer = WFServer<protocol::TutorialRequest,
protocol::TutorialResponse>;
using tutorial_process_t = std::function<void (WFTutorialTask *)>;
Клиент получает данные от стандартного ввода, отправляет запрос на сервер и получает результат. Этот процесс повторяется с помощью WFRepeaterTask
, прекращая цикл, когда входные данные пользователя пусты. Кроме того, проверяем, чтобы ответы от сервера не превышали 4 КБ для обеспечения безопасности.
Основная задача клиента — создание задачи для специального протокола. Это можно найти в файле WFTaskFactory.h:
template<class REQ, class RESP>
class WFNetworkTaskFactory
{
private:
using T = WFNetworkTask<REQ, RESP>;
public:
static T *create_client_task(TransportType type,
const std::string& host,
unsigned short port,
int retry_max,
std::function<void (T *)> callback);
static T *create_client_task(TransportType type,
const std::string& url,
int retry_max,
std::function<void (T *)> callback);
static T *create_client_task(TransportType type,
const ParsedURI& uri,
int retry_max,
std::function<void (T *)> callback);
static T *create_client_task(TransportType type,
const struct sockaddr *addr,
socklen_t addrlen,
int retry_max,
std::function<void (T *)> callback);
}
Пример конфигурации:
};
В данном примере TransportType
указывает протокол транспортного уровня, доступны следующие значения: TT_TCP, TT_UDP, TT_SCTP и TT_TCP_SSL.
Различия между четырьмя интерфейсами незначительны; в нашем примере URL пока не требуется, мы используем доменное имя и порт для создания задач.
Если пользователю требуется использовать Unix Domain Protocol для доступа к серверу, следует использовать последний интерфейс, передавая sockaddr
.
Актуальный вызов кода представлен ниже. Мы производим наследование от класса WFTaskFactory
, но это наследование не обязательно.
namespace protocol;
class MyFactory : public WFTaskFactory
{
public:
static WFTutorialTask* create_tutorial_task(const std::string& host,
unsigned short port,
int retry_max,
tutorial_callback_t callback)
{
using NTF = WFNetworkTaskFactory<TutorialRequest, TutorialResponse>;
WFTutorialTask* task = NTF::create_client_task(TT_TCP, host, port,
retry_max,
std::move(callback));
task->set_keep_alive(30 * 1000);
return task;
}
};
Как видно, используется класс WFNetworkTaskFactory<TutorialRequest, TutorialResponse>
для создания клиентских задач.
Далее через метод set_keep_alive()
задачи поддерживается соединение на 30 секунд после завершения связи, в противном случае будет использоваться короткое соединение.
Остальные части клиента содержат информацию, которая уже была представлена в предыдущих примерах. Подробнее см. client.cc.
Система поддерживает встроенные протоколы, такие как http, redis, mysql, kafka и dns. Можно ли создать задачи для этих протоколов аналогичным образом? Например:
WFHttpTask* task = WFNetworkTaskFactory<protocol::HttpRequest, protocol::HttpResponse>::create_client_task(...);
Необходимо отметить, что создание HTTP-задач таким способом приведёт к потере многих функциональностей, таких как возможность использования постоянных соединений на основе заголовков или распознавание переадресации.
Аналогично, если создать MySQL-задачу таким образом, она может просто не запуститься из-за отсутствия процесса аутентификации.
Запрос к Kafka может требовать сложной взаимодействия с несколькими брокерами, что также невозможно реализовать данным способом.
Каждый из встроенных протоколов имеет более сложный процесс генерации сообщений, чем показано в этом примере. Если пользователь хочет реализовать более функциональный протокол связи, ему потребуется написать больше кода.
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Опубликовать ( 0 )