Вопрос

Я хочу проверить состояние соединения перед выполнением операций чтения / записи.

Есть ли способ создать метод isConnect()?

Я видел это, но это кажется "уродливым".

Я проверил is_open() - открыть() тоже функционирует, но у него нет ожидаемого поведения.

Это было полезно?

Решение

Протокол TCP предназначен для обеспечения надежности в условиях жесткой сети;несмотря на то, что TCP обеспечивает то, что выглядит как постоянное сквозное соединение, все это просто ложь, каждый пакет на самом деле просто уникальная, ненадежная дейтаграмма.

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

Virtual TCP Conduit

Из—за лежащей в основе - по сути, отсутствия соединения и ненадежности — природы сети, стек будет сообщать о разорванном соединении только тогда, когда удаленный конец отправляет пакет FIN для закрытия соединения, или если он не получает ответ ACK на отправленный пакет (после тайм-аута и пары повторных попыток).

Из-за асинхронной природы asio самый простой способ получать уведомления о постепенном отключении - это иметь выдающийся async_read который вернет error::eof сразу же после закрытия соединения.Но само по себе это все еще оставляет возможность других проблем, таких как полуоткрытые соединения и неполадки в сети, оставшимися незамеченными.

Наиболее эффективный способ обойти неожиданное прерывание соединения - это использовать какой-нибудь вид поддержания активности или пинга.Эта случайная попытка передачи данных по соединению позволит оперативно обнаружить непреднамеренно разорванное соединение.

Протокол TCP на самом деле имеет встроенный механизм поддержания жизнедеятельности который может быть настроен в asio с помощью asio::tcp::socket::keep_alive.Хорошая особенность TCP keep-alive заключается в том, что он прозрачен для приложения пользовательского режима, и настраивать его нужно только тем узлам, которые заинтересованы в keep-alive.Недостатком является то, что вам нужен доступ / знания на уровне операционной системы для настройки параметров тайм-аута, к сожалению, они не доступны через простой параметр сокета и обычно имеют значения тайм-аута по умолчанию, которые довольно велики (7200 секунд в Linux).

Вероятно, наиболее распространенным методом поддержания работоспособности является реализация его на прикладном уровне, где приложение получает специальное сообщение noop или ping и ничего не делает, кроме как отвечает на щекотку.Этот метод дает вам наибольшую гибкость при реализации стратегии поддержания работоспособности.

Другие советы

TCP обещает отслеживать отброшенные пакеты - при необходимости повторяя попытки - чтобы обеспечить вам надежное соединение, по некоторому определению reliable .Конечно, TCP не может обрабатывать случаи, когда сервер выходит из строя, или у вас выпадает кабель Ethernet, или происходит что-то подобное.Кроме того, знание того, что ваше TCP-соединение установлено, не обязательно означает, что протокол, который будет использоваться через TCP-соединение, готов (например, ваш HTTP-веб-сервер или ваш FTP-сервер могут быть в некотором нерабочем состоянии).

Если вы знаете протокол, отправляемый по TCP, то, вероятно, в этом протоколе есть способ сообщить вам, все ли в порядке (для HTTP это было бы Запрос РУКОВОДИТЕЛЯ)

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

Учитывая, что сервер ничего не отправил, вы либо получите asio::error::would_block или какая-то другая ошибка.Если первое, то ваш локальный сокет еще не обнаружил разъединения.Если последнее, то ваш сокет был закрыт.

Вот пример кода:

#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/steady_timer.hpp>

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

template<class Duration>
void async_sleep(asio::io_service& ios, Duration d, asio::yield_context yield)
{
  auto timer = asio::steady_timer(ios);
  timer.expires_from_now(d);
  timer.async_wait(yield);
}

int main()
{
  asio::io_service ios;
  tcp::acceptor acceptor(ios, tcp::endpoint(tcp::v4(), 0));

  boost::asio::spawn(ios, [&](boost::asio::yield_context yield) {
    tcp::socket s(ios);
    acceptor.async_accept(s, yield);
    // Keep the socket from going out of scope for 5 seconds.
    async_sleep(ios, chrono::seconds(5), yield);
  });

  boost::asio::spawn(ios, [&](boost::asio::yield_context yield) {
    tcp::socket s(ios);
    s.async_connect(acceptor.local_endpoint(), yield);

    // This is essential to make the `read_some` function not block.
    s.non_blocking(true);

    while (true) {
      system::error_code ec;
      char c;
      // Unfortunately, this only works when the buffer has non
      // zero size (tested on Ubuntu 16.04).
      s.read_some(asio::mutable_buffer(&c, 1), ec);
      if (ec && ec != asio::error::would_block) break;
      cerr << "Socket is still connected" << endl;
      async_sleep(ios, chrono::seconds(1), yield);
    }

    cerr << "Socket is closed" << endl;
  });

  ios.run();
}

И на выходе:

Socket is still connected
Socket is still connected
Socket is still connected
Socket is still connected
Socket is still connected
Socket is closed

Проверено на:

Убунту:16.04
Ядро:4.15.0-36-общий
Повышение:1.67

Хотя я не знаю, зависит ли такое поведение от какой-либо из этих версий.

вы можете отправить фиктивный байт в сокет и посмотреть, вернет ли он ошибку.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top