Примеры, которые вы упомянули в документации Boost.Asio, на самом деле очень хороши, чтобы увидеть, как все работает. Вы правы, что поначалу это может показаться немного сложным для понимания, особенно если вы новичок в этих понятиях. Однако я бы порекомендовал вам начать с примера с сервером чата и получить его на своей машине. Это позволит вам поближе взглянуть на вещи и начать что-то менять, чтобы узнать, как это работает. Позвольте мне рассказать вам о нескольких вещах, которые я считаю важными для начала.
Из вашего описания того, что вы хотите сделать, кажется, что сервер чата дает вам хорошую отправную точку, поскольку у него уже есть похожие кусочки, которые вам нужны. Асинхронный сервер - это то, что вам нужно, так как вы можете легко обрабатывать несколько клиентов в одном потоке. Ничего сложного с самого начала.
Упрощенный, асинхронный в данном случае означает, что ваш сервер обрабатывает очередь, берет обработчик (задачу) и выполняет его. Если в очереди ничего нет, он просто ждет, когда что-то будет помещено в очередь. В вашем случае это означает, что это может быть соединение от клиента, новое чтение сообщения от клиента или что-то в этом роде. Чтобы это работало, каждый обработчик (функция, обрабатывающая реакцию на конкретное событие) должен быть настроен.
Позвольте мне немного объяснить, используя код из примера сервера чата.
В исходном файле сервера вы видите класс chat_server
, который вызывает start_accept
в конструкторе. Здесь устанавливается обработчик принятия.
void start_accept()
{
chat_session_ptr new_session(new chat_session(io_service_, room_)); // 1
acceptor_.async_accept(new_session->socket(), // 2
boost::bind(&chat_server::handle_accept, this, new_session, // 3
boost::asio::placeholders::error)); // 4
}
Строка 1 : создается объект chat_session
, представляющий сеанс между одним клиентом и сервером. Для принятия создается сеанс (еще не подключен клиент).
Строка 2 : асинхронный прием для сокета ...
Строка 3 : ... Обязательно позвоните chat_server::handle_accept
, когда это произойдет. Сеанс передается для использования первым клиентом, который подключается.
Теперь, если мы посмотрим на handle_accept
, мы увидим, что при подключении клиента для сеанса вызывается start
(это просто запускает вещи между сервером и этим клиентом). И, наконец, новый запрос будет принят, если другие клиенты также захотят подключиться.
void handle_accept(chat_session_ptr session,
const boost::system::error_code& error)
{
if (!error)
{
session->start();
}
start_accept();
}
Это то, что вы тоже хотите иметь. Выдающийся прием для входящих соединений. И если несколько клиентов могут подключиться, всегда должен быть один из этих невыполненных, чтобы сервер мог обработать принятие.
Как сервер и клиент (ы) взаимодействуют - все в сеансе, и вы можете следовать той же схеме и изменить это, чтобы сделать то, что вы хотите. Вы упоминаете, что сервер должен смотреть на то, что отправлено и делать разные вещи. Взгляните на chat_session
и функцию start
, которая была вызвана сервером в handle_accept
.
void start()
{
room_.join(shared_from_this());
boost::asio::async_read(socket_,
boost::asio::buffer(read_msg_.data(), chat_message::header_length),
boost::bind(
&chat_session::handle_read_header, shared_from_this(),
boost::asio::placeholders::error));
}
Здесь важен звонок на boost::asio::async_read
. Это то, что вы тоже хотите. Это помещает выдающееся чтение в сокет, так что сервер может читать то, что отправляет клиент. Есть обработчик (функция), который связан с этим событием chat_session::handle_read_header
. Это будет вызываться всякий раз, когда сервер читает что-то в сокете. В этой функции-обработчике вы можете начать вводить свой конкретный код, чтобы определить, что делать, если отправлено конкретное сообщение и т. Д.
Важно знать, что при вызове этих асинхронных функций boost :: asio внутри этого вызова ничего не происходит (т. Е. Сокет не читается, если вы вызываете функцию read). Это асинхронный аспект. Вы просто регистрируете обработчик чего-то, и ваш код вызывается, когда это происходит. Следовательно, когда вызывается это чтение, оно немедленно возвращается, и вы возвращаетесь в handle_accept
для сервера (если вы будете следовать тому, как все вызывается). И если вы помните, мы также вызываем start_accept
, чтобы настроить другое асинхронное принятие. На этом этапе у вас есть два выдающихся обработчика, ожидающих подключения другого клиента или первого клиента, отправляющего что-либо. В зависимости от того, что произойдет первым, будет вызван этот конкретный обработчик.
Также важно понимать, что всякий раз, когда что-то запускается, оно будет работать непрерывно, пока не будет выполнено все, что ему нужно сделать.Другие обработчики должны ждать, даже если есть ожидающие события, которые их инициируют.
Наконец, для запуска сервера вам понадобится io_service
, который является центральным понятием в Asio.
io_service.run();
Это одна строка, которую вы видите в функции main
.Это просто говорит о том, что поток (только один в примере) должен запустить io_service, который является очередью, в которой обработчики ставятся в очередь, когда есть работа, которую нужно сделать.Когда ничего не происходит, io_service просто ждет (конечно, блокируя основной поток).
Надеюсь, это поможет вам начать то, что вы хотите сделать.Есть много вещей, которые вы можете сделать и чему поучиться.Я считаю это отличным программным обеспечением!Удачи!