LINUX.ORG.RU

Boost Asio Определение сервером факта отключения клиента

 ,


0

1

Boost Asio Определение сервером факта отключения клиента Добрый день.
Имеется код(некоторые очевидные участки скрыты)

...

void client_session(tcp::socket socket) {
    string st;
    try {
        while(true) {
            cout << "Enter st:" << endl;
            cin >> st;
            socket.write_some(buffer(st));
        }
    }
    catch (exception &e) {
        socket.close();
        cout << e.what() << endl;
    }
}
...

int main() {
    ...
    tcp::socket socket(bService);
    bAcceptor.accept(socket);
    thread th(client_session, move(socket));
    th.join();

    return 0;
}
Короче, запускаю этот «сервер»(:-))
1) Подключаюсь к нему с локального ПК или другого(в контексте данного вопроса не имеет значения) командой:
nc localhost 30055
2) На сервере получаю сообщение «Enter st:», и сразу же после его получения(со стороны клиента, закрываю nc комбинацией CRTL+C);
3) На сервере тем временем продолжает отображаться строка «Enter st:»;
4) На сервере ввожу строку «qwe123», нажимаю ВВОД и далее оператор write_some отрабатыет успешно, исключение не выпадает;
5) На сервере ввожу вторую строку «rty456», нажимаю ВВОД и далее при выполнении оператора write_some выпадает исключение(текст e.what(): «write_some: Broken pipe»);

Также приведу сокращенный лог wireshark во время выполнения данных манипуляций:
Клиент -> Сервер [SYN]
Сервер -> Клиент [SYN, ACK]
Клиент -> Сервер [ACK]
- нажимаю CRTL+C со стороны клиента
Клиент -> Сервер [FIN, ACK]
Сервер -> Клиент [ACK]
- на сервере ввожу первую сроку "qwe123" и ВВОД
Сервер -> Клиент [PSH, ACK] (в данном пакете как раз есть введенная строка "qwe123")
Клиент -> Сервер [RST]
- на сервере ввожу вторую строку "rty456" и ВВОД
- в wireshark больше ничего не добавляется, на сервере выпадает
исключение и соответственно отображается строка "write_some: Broken pipe" (e.what())
Вопросы:
1) Почему исключение не выпадает при первом операторе write_some?
2) Eсть ли какие-то параметры tcp::socket, которые влияют на данное поведение и как их использовать?

★★★★★

Последнее исправление: rumgot (всего исправлений: 4)

Ответ на: комментарий от Reset

Ладно, допустим, на локальной машине это прокатило, это просто низкоуровневый хак. А как с удаленной машиной это сделать?

aboutcard
()
Ответ на: комментарий от aboutcard

Аналогично по схеме close->FIN->select->can read->read->close. Этот механизм не работает только если клиент отваливается по причине поломки сети.

Reset ★★★★★
()
Ответ на: комментарий от Reset

По твоим словам получается, что сервер всегда отслеживает поведение клиента, и в любой момент может узнать, разрыв соединения, даже если клиент на уровне HTTP его не инициализировал. Тогда какого же хрена, серверные фреймверки не используют эту возможность?

aboutcard
()
Ответ на: комментарий от aboutcard

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

Конечно.

Тогда какого же хрена, серверные фреймверки не используют эту возможность?

С чего бы это? Еще как используют. Сейчас пользуюсь libevent/evhttp, spray/akka. Там отлично работает данный механизм.

Reset ★★★★★
()
Ответ на: комментарий от Reset

Этот механизм не работает только если клиент отваливается по причине поломки сети.

А как удаленная машина может определять вообще сломалась ли сеть, или что-то другое произошло? она ведь получает сигналы из сети, и только на основании этих сигналов может делать какие-либо выводы.

aboutcard
()
Ответ на: комментарий от aboutcard

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

Reset ★★★★★
()
Ответ на: комментарий от Reset

Ресетушка, а ты знаешь с кем ты тут полемику начал? Или решил преподать юесполезный по результатам урок анонiмусу?

anonymous
()
Ответ на: комментарий от aboutcard

Либо ты мало видел либо плохо читал документацию.

Reset ★★★★★
()
Ответ на: комментарий от anonymous

Понятия не имею, первый раз вижу этот ник. Кто это?

анонiмусу?

осознал :)

Reset ★★★★★
()
Последнее исправление: Reset (всего исправлений: 1)
Ответ на: комментарий от Reset

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

aboutcard
()
Ответ на: комментарий от Reset

Так я и написал, что нужно считать данные. К словам не придирайся, филолог.:-)

rumgot ★★★★★
() автор топика
Ответ на: комментарий от aboutcard

Ты идиот. Никакого смешивания абстракций нет. В HTTP с точки зрения пользователя соединение - совсем другая сущность, отличная от TCP соединения. Реализации клиента/сервера сервера сами разруливают транспортные соединения, с точки зрения пользователя - он отправил запрос, а как там реализация будет его отправлять/получать - его не интересует. В HTTP/1.1 просто добавили возможность отправлять несколько запросов на 1 соединении (в HTTP/1.0 1 запрос = 1 соединение) и указывать, что на этом соединении запросов больше отправлено не будет.

anonymous
()
Ответ на: комментарий от aboutcard

правильно, нечего сказать - скатывайся в оскорбления.

leave ★★★★★
()
Ответ на: комментарий от Reset

Прости, за глупый вопрос, а что ты подразумеваешь под read-callback?
Функцию, которая читает данные из сокета?

rumgot ★★★★★
() автор топика
Ответ на: комментарий от rumgot

подозреваю, это он анонiмусу. тот только на колбеках умеет работать, потому что дальше JS не умеет. Но да, это recv/read.

anonymous
()
Ответ на: комментарий от anonymous

НЕНЕНЕ. Почему тогда в read-колбеке нужно что-то читать, если в async_read callback приходит количество уже считанного, то есть 0?

anonymous
()

Не понимаю какие ещё вопросы могли остаться. ТС, тебе же дали ссылку на примеры. Идешь по ней и видишь http://www.boost.org/doc/libs/1_59_0/doc/html/boost_asio/example/cpp03/echo/b...

      boost::system::error_code error;
      size_t length = sock->read_some(boost::asio::buffer(data), error);
      if (error == boost::asio::error::eof)
        break; // Connection closed cleanly by peer.
      else if (error)
        throw boost::system::system_error(error); // Some other error.

Какие тут ещё могут оставаться вопросы?

Другое дело, что блокирующий I/O в Boost.ASIO использовать не рекомендуется. Но там и для неблокирующего есть примеры.

asaw ★★★★★
()
Последнее исправление: asaw (всего исправлений: 2)
Ответ на: комментарий от rumgot

Видимо, он имел в виду как раз неблокирующий API, который можно использовать несколькими способами. Не обязательно там коллбэк указывать, можно попросить вернуть std::future, или использовать сопрограммы. Вот тут вот последнее есть: http://www.boost.org/doc/libs/1_59_0/doc/html/boost_asio/overview/core/spawn.... Пример с std::future есть только для UDP, но для TCP там должно быть примерно то же самое: http://www.boost.org/doc/libs/1_59_0/doc/html/boost_asio/example/cpp11/future... Классические примеры с коллбэками лежат рядом с блокирующими.

asaw ★★★★★
()
Ответ на: комментарий от pon4ik

свежезабаненного

doubleѣ опять зобанили :(((

anonymous
()
Ответ на: комментарий от rumgot

Например потому, что нет стандартной возможности программно прервать блокирующую операцию типа sock->read_some(boost::asio::buffer(data), error). Это можно сделать только платформо-зависимым способом. А если использовать неблокирующий API, то для этого можно использовать таймеры.

asaw ★★★★★
()
Ответ на: комментарий от asaw

Ну можно придумать.
Вот что думаешь про такую схему:
Также перед вызовом read_some сохраняется текущее время в какой-нибудь переменной, а в отдельном потоке проверяется данная переменная, и если она отличается от текущего время на заданную величину, то поток завершается.

rumgot ★★★★★
() автор топика
Ответ на: комментарий от rumgot

то поток завершается

Вот в этом вся загвоздка. Поток - не процесс, его в общем случае нельзя просто так взять и принудительно завершить из другого потока. Он сам должен корректно завершиться. В частном случае можно послать какой-нибудь сигнал всему процессу вроде SIGINT и этим самым вызвать возврат из функции чтения сокета, но в общем случае это не работает. В блокирующем API можно было бы специально предусмотреть таймаут, но автор ASIO этого по каким-то своим религиозным причинам просто НЕ ХОЧЕТ. Утверждает, что это неправильно и не соответствует философии его библиотеки.

asaw ★★★★★
()
Ответ на: комментарий от asaw

А если переменная потока - динамическая. То при при ее удалении оператором delete, поток завершится? Т.е.:

thread th1 = new thread(f1);
...
... 
А потом в контролирующем потоке:
delete th1;

rumgot ★★★★★
() автор топика
Ответ на: комментарий от rumgot

А если переменная потока - динамическая. То при при ее удалении оператором delete

Нет никакой разницы как вызывается деструктор - посредством delete или при выходе из области видимости. Поэтому то что у тебя - просто пример плохого кода (из-за неоправданного использования new и delete). Это во-первых. Во-вторых смотри документацию деструктора std::thread: http://en.cppreference.com/w/cpp/thread/thread/~thread То есть правильное использование работающего thread такое: сначала делаешь join(), а потом уже уничтожаешь объект thread. Поэтому твой фокус не пройдет.

Кстати, join() лучше делать из того же потока, из которого ты создавал поток.

asaw ★★★★★
()
Последнее исправление: asaw (всего исправлений: 2)
Ответ на: комментарий от aboutcard

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

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.