C Boost Assio — запуск в другом потоке

#c #boost

#c #boost

Вопрос:

У меня есть реализация Boost Assio в моем проекте. Вот оно:

Session.h:

 #ifndef VIBRANIUM_CORE_SESSION_H
#define VIBRANIUM_CORE_SESSION_H

#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;

namespace Vibranium{
    class Session
            : public std::enable_shared_from_this<Session>
    {
    public:
        Session(tcp::socket socket)
                : socket_(std::move(socket))
        {
        }
        void start();


    private:
        void do_read();
        void do_write(std::size_t length);
        tcp::socket socket_;
        enum { max_length = 1024 };
        char data_[max_length];
    };
}
#endif //VIBRANIUM_CORE_SESSION_H
  

Session.cpp:

 #include "Session.h"

void Vibranium::Session::start() {
    do_read();
}

void Vibranium::Session::do_read() {
    auto self(shared_from_this());
    socket_.async_read_some(boost::asio::buffer(data_, max_length),
    [this, self](boost::system::error_code ec, std::size_t length)
    {
        if (!ec)
        {
            do_write(length);
        }
    });
}

void Vibranium::Session::do_write(std::size_t length) {
    auto self(shared_from_this());
    boost::asio::async_write(socket_, boost::asio::buffer(data_, length),
     [this, self](boost::system::error_code ec, std::size_t /*length*/)
     {
         if (!ec)
         {
             do_read();
         }
     });
}
  

Server.h:

 #ifndef VIBRANIUM_CORE_SERVER_H
#define VIBRANIUM_CORE_SERVER_H
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;
namespace Vibranium{
    class Server {
    public:
        Server(boost::asio::io_contextamp; io_context, short port)
                : acceptor_(io_context, tcp::endpoint(tcp::v4(), port))
        {
            do_accept();
        }

    private:
        void do_accept();
        tcp::acceptor acceptor_;
    };
}
  

Server.cpp:

 #include "Server.h"
#include "Session.h"

using namespace Vibranium;

void Server::do_accept() {
    acceptor_.async_accept([this](boost::system::error_code ec, tcp::socket socket)
    {
        if (!ec)
        {
            std::make_shared<Session>(std::move(socket))->start();
        }

        do_accept();
    });
}
  

И вот как я запускаю сервер:

 #include "Config.h"
#include "Database/MySQLConnection.h"
#include "Implementation/LoginDatabase.h"
#include "Banner.h"
#include "Server/Server.h"
#include <boost/asio.hpp>

using boost::asio::ip::tcp;
using namespace std;
using namespace Vibranium;

int main() {
    //Don't mind Logger::FatalError it's just for coloring!
    Banner::Show(Logger::Error,"AuthServer");
    Config config("AuthServer");

    MySQLConnectionInfo mySqlConnectionInfo(config, "LoginDatabaseInfo");
    LoginDatabaseConnection loginDatabaseConnection(mySqlConnectionInfo);
    loginDatabaseConnection.LoadDatabase();


    try
    {
        boost::asio::io_context io_context;
        Server s(io_context, 8080);
        io_context.run();
    }
    catch (std::exceptionamp; e)
    {
        std::cerr << "Exception: " << e.what() << "n";
    }

    std::cout << "Server Started!" << std::endl;

    return 0;
}
  

У меня есть несколько вопросов:

  1. Когда я запускаю сервер, я не вижу сообщения «Сервер запущен!». Я подозреваю, что это потому, что он блокирует мой текущий поток. Как я могу запустить TCP-сервер в другом потоке? Должно ли это быть в отдельном потоке или ?
  2. Пример, который я реализовал, просто возвращает обратно то, что отправлено на сервер. Можете ли вы предложить какой-либо пример того, как я могу различать разные сообщения о событиях? Чего я хочу добиться, так это чего-то вроде того, когда я отправил сообщение, начинающееся с 0x001, чтобы указать, что это сообщение чата, например, или когда сообщение начинается с 0x002, мой сервер знает, что клиент сообщает, что кто-то вышел из системы.
  3. Я думаю, что Boost Assio использует двоичный протокол поверх TCP. Я прав?
  4. Можете ли вы показать мне, как я могу отправлять структуры по TCP с помощью двоичного протокола?

Если вопросов слишком много, пожалуйста, рассмотрите только вопрос 1.

Ответ №1:

1. Когда я запускаю сервер, я не вижу сообщения «Сервер запущен!». Я подозреваю, что это потому, что он блокирует мой текущий поток. Как я могу запустить TCP-сервер в другом потоке? Должно ли это быть в отдельном потоке или?

 std::cout << "Server Started!" << std::endl;
  

вызывается после

 io_context.run();
  

io_context.run() будет блокировать, пока есть какие-либо текущие задачи. Вам нужно вызвать «Server Started» перед io_context.run(), возможно, в конструкторе сервера

2. Пример, который я реализовал, просто возвращает обратно то, что отправлено на сервер. Можете ли вы предложить какой-либо пример того, как я могу различать разные сообщения о событиях? Чего я хочу добиться, так это чего-то вроде того, когда я отправлял сообщение, начинающееся с 0x001, чтобы указать, что это сообщение чата, например, или когда сообщение начинается с 0x002, мой сервер знает, что клиент сообщает, что кто-то вышел из системы.

Есть много способов, которыми вы могли бы это сделать, но, например. вы могли бы просто установить поле вашего сообщения для переноса этой информации.

3. Я думаю, что Boost Assio использует двоичный протокол поверх TCP. Я прав?

В вашем случае да, потому что вы используете boost::asio::ip:: tcp, но это не обязательно. Существует также boost::asio::ip::udp для поддержки udp.

4. Можете ли вы показать мне, как я могу отправлять структуры по TCP с помощью двоичного протокола?

Вы могли бы просто реализовать метод сериализации boost для вашего struct шаблона void serialize (архив amp; ar, const unsigned int version). Смотрите пример boost. или предоставьте свою собственную сериализацию (например, в текст) и отправьте эти данные.

Если вы реализуете сериализацию boost, вы можете передать свою структуру напрямую функциям asio, в противном случае вам придется отправлять сериализованные байты.

Извините, но у меня нет времени писать весь код за вас.

Комментарии:

1. Спасибо за ваш подробный ответ. Здесь: boost.org/doc/libs/1_66_0/doc/html/boost_asio/example/cpp03 /… они говорят, что async_write это автоматически сериализует данные для меня. Итак, в этом случае мне не нужно беспокоиться о сериализации, верно? Также. Если я отправлю struct A{} с сервера, у меня должна быть точно такая же struct A{} на стороне клиента, я прав?

2. Да, но здесь библиотека сериализации boost используется неявно (путем предоставления функции сериализации в void serialize(Archive amp; ar, const unsigned int version в stock.hpp). Оператор amp; в сериализации boost выполняет и то, и другое: сериализует / десериализует, в зависимости от того, является ли входной или выходной поток.

3. @VenelinVasilev В этом примере connection объект обеспечивает сериализацию с помощью Boost. Сериализация, но это не часть Asio; это часть примера кода . Asio имеет дело только с протоколами транспортного уровня (т. Е. TCP, UDP); вам решать реализовать любой протокол прикладного уровня, который вы хотите, поверх выбранного вами транспортного протокола.

4. Спасибо тебе @StPiere. Можете ли вы привести какой — либо пример того , как сформировать и прочитать пакет заголовка , а не его тело ?

5. Также @StPiere в моем примере. TCP-сервер асинхронный, верно? Это означает, что он может выполнять множество задач параллельно в одно и то же время?