Сначала вам нужно решить, какой стиль связи через сокет вы собираетесь использовать:
синхронный - означает, что все низкоуровневые операции блокируются, и обычно вам требуется поток для принятия, а затем потоки (чтение потока или io_service) для обработки каждого клиента.
асинхронный - означает, что все низкоуровневые операции не являются блокирующими, и здесь вам нужен только один поток (io_service), и вам нужно иметь возможность обрабатывать обратные вызовы, когда происходят определенные вещи (например, принимает, частичный пишет, результат чтения и т. д.)
Преимущество подхода 1 состоит в том, что код (??) намного проще, чем 2, однако я считаю, что 2 является наиболее гибким, и фактически с 2, по умолчанию у вас есть однопоточное приложение (внутренне обратные вызовы событий) выполняются в отдельном потоке к основному потоку диспетчеризации), недостатком 2, конечно, является то, что ваша задержка обработки затрагивает следующие операции чтения / записи ... Конечно, вы можете создавать многопоточные приложения с подходом 2, но не наоборот. наоборот (то есть однопоточный с 1) - следовательно, гибкость ...
Так что, в принципе, все зависит от выбора стиля ...
РЕДАКТИРОВАТЬ: обновлено для новой информации, это довольно долго, я не могу потрудиться написать код, есть много в документации Boost, я просто опишу, что происходит для ваша выгода ...
[основная тема]
- объявить экземпляр io_service
- для каждого сервера, к которому вы подключаетесь (я предполагаю, что эта информация доступна при запуске), создайте класс (скажем, ServerConnection
) и в этом классе создайте сокет tcp :: socket, используя тот же экземпляр io_service сверху и в самом конструкторе вызовите async_connect
, ПРИМЕЧАНИЕ: этот вызов является планированием запроса на подключение, а не реальной операцией подключения (это не произойдет до позже)
- как только все объекты ServerConnection
(и их соответствующие async_connects поставлены в очередь), вызовите run()
в экземпляре io_service. Теперь основной поток заблокирован, отправляя события в очередь io_service.
[asio thread] В io_service по умолчанию есть поток, в котором вызываются запланированные события, вы не управляете этим потоком, и для реализации «многопоточной» программы вы можете увеличить количество потоков, которые использует io_service. , но на данный момент придерживайтесь одного, это сделает вашу жизнь простой ...
asio будет вызывать методы в вашем классе ServerConnection в зависимости от того, какие события готовы из запланированного списка. Первое событие, которое вы поставили в очередь (до вызова run ()), было async_connect
, теперь asio перезвонит вам, когда будет установлено соединение с сервером, обычно вы реализуете метод handle_connect
, который будет вызываться (вы передаете метод к вызову async_connect
). На handle_connect
все, что вам нужно сделать, это запланировать следующий запрос - в этом случае вы хотите прочитать некоторые данные (возможно, из этого сокета), поэтому вы вызываете async_read_some
и передаете функцию, которая будет уведомлена, когда есть данные. После этого основной поток рассылки asio продолжит отправку других событий, которые готовы (это могут быть другие запросы на подключение или даже добавленные вами async_read_some
).
Допустим, вам звонят, потому что на одном из сокетов сервера есть некоторые данные, которые передаются вам через ваш обработчик для async_read_some
- вы можете затем обработать эти данные, сделать, как вам нужно, но это самый важный бит - после того, как это сделано, запланируйте следующий async_read_some
, таким образом asio будет доставлять больше данных, когда они станут доступны. ОЧЕНЬ ВАЖНОЕ ПРИМЕЧАНИЕ: если вы больше не планируете какие-либо запросы (т.е. выходите из обработчика без постановки в очередь), то в io_service не хватит событий для отправки, и run () (которую вы вызывали в главном потоке) завершится.
Теперь, что касается письма, это немного сложнее.Если все ваши записи выполняются как часть обработки данных из вызова read (то есть в потоке asio), вам не нужно беспокоиться о блокировке (если только у вашего io_service несколько потоков), иначе в вашем методе записи,добавить данные в буфер и запланировать запрос async_write_some
(с помощью обработчика write_handler, который будет вызываться при записи в буфер, частично или полностью).Когда asio обрабатывает этот запрос, он вызывает ваш обработчик после записи данных, и у вас есть возможность снова вызвать async_write_some
, если в буфере осталось больше данных или если их нет, вам не нужно беспокоиться о планировании записи,На этом этапе я упомяну одну технику, рассмотрим двойную буферизацию - я оставлю это на этом.Если у вас совершенно другой поток, который находится за пределами io_service, и вы хотите написать, вы должны вызвать метод io_service::post
и передать метод для выполнения (в вашем классе ServerConnection
) вместе с данными, io_service будетзатем вызовите этот метод, когда это возможно, и в этом методе вы можете затем буферизовать данные и при необходимости вызвать async_write_some
, если запись в данный момент не выполняется .
Теперь есть одинОЧЕНЬ важная вещь, с которой вы должны быть осторожны, вы НИКОГДА не должны планировать async_read_some
или async_write_some
, если уже выполняется , то есть, допустим, вы звонили async_read_some
насокет, пока asio не вызовет это событие, вы не должны планировать другое async_read_some
, иначе у вас будет много дерьма в ваших буферах!
Хорошей отправной точкой является сервер / клиент asio chat, которыйВы найдете в документах Boost, он показывает, как используются методы async_xxx.И имейте это в виду, все вызовы async_xxx возвращаются немедленно (в течение нескольких десятков микросекунд), поэтому блокирующих операций нет, все это происходит асинхронно.http://www.boost.org/doc/libs/1_39_0/doc/html/boost_asio/example/chat/chat_client.cpp, - это пример, на который я ссылался.
Теперь, если вы обнаружите, что производительность этого механизма слишком низкая, и вы хотите иметь многопоточность, все, что вам нужно сделать, это увеличить количество потоков, доступных для основного io_service, и реализовать соответствующую блокировку в вашемметоды чтения / записи в ServerConnection и все готово.