#multithreading #boost-asio #blocking
#многопоточность #boost-asio #блокировка
Вопрос:
Я пытаюсь изучить boost ::asio для программирования сокетов / сетей. Я пытаюсь отправить некоторые простые данные с клиента на сервер.
Позвольте мне сказать, прежде всего, что я намеренно использую синхронный блокирующий код, в отличие от асинхронного неблокирующего кода, потому что в дополнение к этому я использую многопоточность (с библиотекой pthreads).
Клиент успешно вызывает boost::asio::write() . Я зашел так далеко, что не только попытался перехватить любые исключения, вызванные boost ::asio::write() , но также проверил значение boost ::system ::error_code , которое выдает сообщение «Операция успешно завершена» или что-то в этом роде.
Мой код read() выглядит следующим образом:
#define MAX_MESSAGE_SIZE 10000 // in bytes
void* receivedData = malloc(MAX_MESSAGE_SIZE);
try
{
boost::asio::read(*sock, boost::asio::buffer(receivedData, MAX_MESSAGE_SIZE));
}
catch (std::exception amp;e)
{
std::cout << "Exception thrown by boost::read() in receiveMessage(): " << e.what() << "n";
delete receivedData;
receivedData = NULL;
return false;
}
Несмотря на то, что write() успешно выполняется на другом конце, и клиент и сервер соглашаются, что на данный момент установлено соединение, read() никогда не возвращается. Единственный случай, когда он возвращается для меня, — это если я вручную закрываю клиентское приложение, и в этот момент (как и следовало ожидать) вызов read() выдает исключение, указывающее, что клиент принудительно закрыл соединение.
Имеет ли это какое-либо отношение к io_service.run() ? В моем поиске в Google для отладки я наткнулся на некоторые упоминания run() , и то, что я неявно понял из этих сообщений, заключается в том, что run() обрабатывает «работу», что, как я понимаю, означает, что он выполняет фактическую отправку и получение, и что write() и read()это просто средство постановки в очередь отправки и проверки того, какие пакеты уже были отправлены и, так сказать, «одобрены» io_service.run() . Пожалуйста, поправьте меня, если я ошибаюсь, поскольку в документации Boost говорится немного больше, чем «Запустить цикл обработки событий io_service».
Я следую руководству boost::asio ( http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/tutorial/tutdaytime1.html ) в котором абсолютно не упоминается run(), так что, может быть, я здесь совершенно не на том пути, и run() вообще не нужен?
В любом случае, только что я внес еще одно изменение в свой код, чтобы посмотреть, изменится ли что-нибудь (этого не произошло): как на моем клиенте, так и на сервере я настроил поток для следующей функции (для повторного вызова io_service.run() в течение всего времени работы приложения, чтобы увидеть, не выполняется ли этоэто то, что вызывало проблему):
void* workerThread(void* nothing)
{
while(1)
{
io_service.run();
Sleep(10); // just keeping my CPU from overheating in this infinite loop
}
}
Но, как указано выше, это никак не повлияло на производительность.
Чего мне здесь не хватает? Почему read() никогда не возвращается, даже после успешного выполнения write() на другом конце?
Заранее спасибо.
Ответ №1:
Обратите внимание, что используемое вами чтение будет блокироваться до тех пор, пока буфер не будет заполнен или не возникнет ошибка, поэтому оно вернется только тогда, когда получит 10000 байт.
Рассмотрите возможность использования read_some или использования условия завершения с read вместо этого.
Комментарии:
1. Ага, вы абсолютно правы. Установка значения 1 вместо 10000 или, например, сработала. Можете ли вы подробнее рассказать о том, как используется read_some() или как я бы использовал условие завершения с помощью read() ? И если я использую их, могу ли я гарантировать, что мое полное «чтение» будет полными и точными данными, которые были отправлены в write (), или мне нужно будет добавлять к каждому сообщению байт, сообщающий получателю, сколько байтов в сообщении (и продолжать чтение и хранение данных до завершенияполучено полное сообщение)?
2. С TCP-сокетами вы не можете полагаться на сохранение границ пакетов, поэтому вам придется использовать для этого свой собственный протокол. Вероятно, самый простой способ — использовать отдельные вызовы чтения для заголовка сообщения и тела сообщения (используя transfer_exactly с размером из заголовка сообщения).
3. О, я понимаю, да, похоже, это было бы довольно просто и работало бы хорошо. Большое спасибо за вашу помощь!