boost :: asio :: ip :: tcp :: socket подключен? - PullRequest
18 голосов
/ 02 октября 2009

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

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

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

Я также протестировал функцию is_open () , но она не работает ожидаемым образом.

Ответы [ 4 ]

31 голосов
/ 03 октября 2009

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

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

Virtual TCP Conduit

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

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

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

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

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

1 голос
/ 12 октября 2018

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

Учитывая, что сервер ничего не отправил, вы либо получите 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

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

Ubuntu: 16.04
Ядро: 4.15.0-36-generic
Увеличение: 1,67

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

1 голос
/ 03 октября 2009

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

Если вы знаете, что протокол отправляется по TCP, то, вероятно, в этом протоколе есть способ сообщить вам, в порядке ли он (для HTTP это будет запрос HEAD )

0 голосов
/ 02 октября 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...