Невозможно обнаружить C ++ синхронное повышение :: asio :: ip :: tcp :: сокет закрывается

я использую boost::asio установить синхронное соединение через сокет TCP с приложением TCP-сервера node.js, работающим на том же компьютере. Я использую Embarcadero RAD studio XE4 в 64-разрядном приложении для Windows, в котором используется встроенная расширенная версия Embarcadero 1.50.

Все работает хорошо, кроме случаев, когда TCP-сервер node.js выключается. Когда это происходит, мое клиентское приложение C ++ не обнаруживает разъединение, когда я читаю из сокета. Тем не менее, он обнаруживает отключение, когда я пишу в сокет.

Мой текущий код был написан после того, как я попытался разобраться в документации по бусту и множестве ответов здесь, на SO. Часть кода для чтения выглядит следующим образом (я упустил проверку кода ошибки на компактность)

boost::system::error_code ec;
boost::asio::io_service io_service;
boost::asio::ip::tcp::socket s(io_service);
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"),m_port);

ec = s.connect(ep,ec);
std::size_t bytes_available = s.available(ec);
std::vector<unsigned char> data(bytes_available,0);

size_t read_len = boost::asio::read(s,  boost::asio::buffer(data),boost::asio::transfer_at_least(bytes_available),ec);

if ((boost::asio::error::eof == ec) || (boost::asio::error::connection_reset == ec))
{
// disconnection
break;
}

Это не очень хорошая система, поскольку она работает в своем собственном потоке внутри цикла и постоянно опрашивает данные, пока программа не завершит работу. Обычно я не буду выполнять read() на сокете, когда там нет данных, но я делаю это в этом случае, так как вся документация приводит меня к мысли, что разъединение сокета обнаруживается только при выполнении чтения или записи в сокет. Проблема в том, что приведенный выше код просто никогда не обнаруживает разрыв соединения, когда приложение node.js завершает работу. Если я пишу, я обнаруживаю его (часть записи использует те же ошибки boost :: asio :: error`, что и чтение для обнаружения), но не при чтении.

Очевидно, что я не могу выполнить чтение для байтов, которые больше, чем это доступно, или мой поток заблокируется, и я не смогу выполнить запись позже в цикле моего потока.

Я пропускаю другой конкретный код ошибки наддува для обнаружения состояния ошибки? Или это проблема именно с нулевой длиной чтения. Если это так, есть ли другие варианты, доступные для меня?

В настоящее время я получаю сервер node.js для записи определенного сообщения, когда оно поступает в сокет, когда оно выключает то, что я обнаруживаю, а затем сам закрываю клиентский конец. Тем не менее, это немного хак, и я бы предпочел чистый способ обнаружить разрыв, если это возможно.

0

Решение

В общем, Boost.Asio’s read() функции возвращаются когда:

  • Буфер заполнен.
  • Полное условие было выполнено.
  • Произошла ошибка.

Не указано, в каком порядке проверяются эти условия. Однако в прошлый раз, когда я смотрел на реализацию, Boost.Asio рассматривал операции, которые читают нулевые байты в потоке, как неактивные перед попыткой чтения из сокета. Следовательно, ошибка конца файла не будет наблюдаться, поскольку буфер 0 размера считается заполненным.

Хотя использование асинхронных операций, вероятно, обеспечит лучшие результаты и масштабируемость, обнаружение разъединения неблокирующим способом все же может быть выполнено с синхронными операциями. По умолчанию синхронные операции блокируются, но это поведение можно контролировать с помощью
socket::non_blocking() функция. В документации говорится:

Если trueсинхронные операции сокета завершатся с boost::asio::error::would_block если они не могут выполнить запрошенную операцию немедленно. Если false, синхронные операции будут блокироваться до завершения.

Следовательно, если синхронные операции настроены на отсутствие блокировки, и операция чтения пытается прочитать минимум 1 байт, то можно наблюдать отключение неблокирующим образом.


Вот полный пример, демонстрирующий неблокирующую синхронность read() операция, которая обнаруживает разрыв. Чтобы ограничить печать сообщений, я решил выполнить спящий режим, когда операция будет заблокирована (т. Е. Есть соединение, но нет данных, доступных для чтения).

#include <algorithm>
#include <iostream>
#include <vector>
#include <boost/asio.hpp>
#include <boost/thread.hpp>

int main(int argc, char* argv[])
{
if (argc != 2)
{
std::cerr << "Usage: <port>\n";
return 1;
}

// Create socket and connet to local port.
namespace ip = boost::asio::ip;
boost::asio::io_service io_service;
ip::tcp::socket socket(io_service);
socket.connect(ip::tcp::endpoint(
ip::address::from_string("127.0.0.1"), std::atoi(argv[1])));

// By setting the socket to non-blocking, synchronous operations will
// fail with boost::asio::error::would_block if they cannot immediately
// perform the requested operation.
socket.non_blocking(true);

// Synchronously read data.
std::vector<char> data;
boost::system::error_code ec;
for (;;)
{
// Resize the buffer based on the amount of bytes available to be read.
// Guarantee that the buffer is at least 1 byte, as Boost.Asio treats
// zero byte read operations as no-ops.
data.resize(std::max<std::size_t>(1, socket.available(ec)));

// Read all available data.
std::size_t bytes_transferred =
boost::asio::read(socket, boost::asio::buffer(data), ec);

// If no data is available, then continue to next iteration.
if (bytes_transferred == 0 && ec == boost::asio::error::would_block)
{
std::cout << "no data available" << std::endl;
boost::this_thread::sleep_for(boost::chrono::seconds(3));
continue;
}

std::cout << "Read: " << bytes_transferred << " -- ";
if (bytes_transferred)
{
std::cout.write(&data[0], bytes_transferred);
std::cout << " -- ";
}
std::cout << ec.message() << std::endl;

// On error, such as a disconnect, exit the loop.
if (ec && ec != boost::asio::error::would_block)
{
break;
}
}
}

Следующий вывод был получен путем подключения примера программы к серверу, который написал «test», «more testing», а затем закрыл соединение:

no data available
Read: 4 -- test -- Success
no data available
Read: 12 -- more testing -- Success
Read: 0 -- End of file
5

Другие решения

Других решений пока нет …