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)

FIN от клиента серверу означает, что клиент больше не будет посылать данных серверу. О том, что он не будет больше принимать данных, он серверу не сообщает, поэтому сервер про это не знает. Когда ты первое сообщение посылаешь, сервер его отсылает и возвращает тебе управление. После этого он получает пакет RST и соединение помечается, как разорванное. Дальнейшие попытки что-либо делать приведут к ошибке.

Legioner ★★★★★
()
Последнее исправление: Legioner (всего исправлений: 1)

По моему, ты вообще не понимаешь, как работает сеть. Когда ты нажимаешь crtl-c, твой сервер не получает никакого сообщения, клиент не отключается для него.

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

Если подключаюсь к данному серверу с использованием такого клиента(также boost asio):

...
int main() {
...
   tcp::socket socket(service);
   socket.connect(endpoint);
   socket.write_some(buffer(some_string));
   socket.close();
...
   return 0;
}
То картина та же.
Как корректно выполнить отключение от сервера, чтобы сервер (перед первым вызовом write_some) «знал», что соединение закрыто?

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

socket.close();

ты тут закрываешь сокет на клиенте. Причем тут сервер то? Откуда он узнает, что ты разорвал соединение?

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

1) Понял. Короче нужно использовать команды на более высоком уровне.
2) А можно ли с помощью boost asio отправить пакет c флагом RST?

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

Я не знаю, что там у вас в плюсах. Я просто мимо проходил, и указал на то, что ты неверно себе представляешь процесс взаимодействия клиент/сервер:)

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

Ну спасибо и на том.
Тогда предыдущий вопрос открыт: кто знает, подскажите пожалуйста, как средствами boost asio отправить пакет с флагом RST?

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

Я что-то не совсем понял. Сервер с клиентом общается с помощью покетов, которые содержат HTTP - заголовки. Если сервер не получит connection-close, как он узнает, что соединение закрыто клиентом?

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

close отправит FIN (ровно как и shutdown(sd, SHUT_WR) ).

recv получит 0 в этом случае.

А еще почитай про KeepAlive или про кастомную организацию таймаутов.

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

Как корректно выполнить отключение от сервера, чтобы сервер (перед первым вызовом write_some) «знал», что соединение закрыто?

Можно попробовать прочитать из сокета. Если клиент успешно закрыл соединение (обрати внимание на «успешно», если у него уборщица выдернула сетевой кабель, это не «успешно»), то ты прочитаешь 0 байтов (т.е. конец файла, или как там в бусте это показано). Если твой протокол подразумевает, что после закрытие соединения со стороны клиента запись в него не положена, ты по этому факту просто закрываешь соединение со своей стороны (со стороны сервера) и всё.

Тогда предыдущий вопрос открыт: кто знает, подскажите пожалуйста, как средствами boost asio отправить пакет с флагом RST?

RST это принудительное закрытие соединения. По идее оно не должно использоваться при нормальном функционировании.

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

Я что-то не совсем понял. Сервер с клиентом общается с помощью покетов, которые содержат HTTP - заголовки. Если сервер не получит connection-close, как он узнает, что соединение закрыто клиентом?

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

При этом клиент может отвалиться на стадии трансфера ответа, тогда серверу придёт connection reset при очередной попытке записи. В принципе на реальном сайте таких reset-ов полно. Но вообще говоря это не стандартная ситуация.

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

Ну так че, ты рассказал историю, где сервер просто по дефолту всегда закрывает соединение. Это, как бы, уже другой вопрос.

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

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

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

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

Насколько я понимаю, в TCP не предусмотрена возможность закрыть сокет так, чтобы другая сторона поняла, что писать сюда больше нельзя. Т.е. возможно, с помощью пакета RST, но это вообще несколько для другого, и через API по-моему этого сделать нельзя. Т.е. или в своём протоколе поверх TCP это предусматриваешь, или как я писал выше, ну или как сейчас делаешь — пишешь в пустоту, получаешь RST и обрабатываешь ошибку.

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

Эта схема хороша, если у нас сервер типа http(т.е. запрос-ответ).
А если сервер типа чата или онлайн игры? То лучше постоянное соединение, ведь команды могут идти в обе стороны.

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

Твоя безграмотность зашкаливает любые мыслимые пределы. Я на тебя время тртить не собираюсь, для таких как ты есть википедия, а лучше с букваря начни.

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

Я все еще жду пример http-заголовка, служащего для «отключения от сервера». Давай же. Посади меня в лужу.

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

Во, оказывается еще такие есть. Ну держи.

HTTP/1.1 defines the «close» connection option for the sender to signal that the connection will be closed after completion of the response. For example, Connection: close

in either the request or the response header fields indicates that the connection SHOULD NOT be considered `persistent' (section 8.1) after the current request/response is complete.

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

Ты иди, потусуйся где нибудь в другом месте. поудаляй там че нибудь, в песочнице поиграй, для своего ума что-нибудь выбери, зачем с дядями большими отираться, надоедать им?

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

То есть если клиент разорвет соединение до того как ты успел вычитать весь запрос, то ты будешь его дожидаться? Молодец.

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

То есть если клиент разорвет соединение до того как ты успел вычитать весь запрос, то ты будешь его дожидаться?

Чего? Ты что сейчас под разрывом соединения подразумеваешь? Для сервера разрыв соединения означает прямое сообщение клиента об этом, с точки зрения сети никакого другого разрыва нет.

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

Чего? Ты что сейчас под разрывом соединения подразумеваешь?

Клиент сделал close(socket) или его вообще прибили и операционка закрыла сокет.

Для сервера разрыв соединения означает прямое сообщение клиента об этом, с точки зрения сети никакого другого разрыва нет.

Ога, и в tcp для этого уже предусмотрены механизмы.

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

Клиент сделал close(socket) или его вообще прибили и операционка закрыла сокет.

И что? Сервер об этом ничего не знает, и никакой возможности узнать нет. Это совершенно другой уровень абстракции.

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

Ложь. Как узнать об этом я написал выше. Советую почитать матчасть, например Cтивенс «Разработка сетевых приложений».

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

Если ты в read-callback'е попробуешь почитать сокет, который уже закрыт, то read вернет 0.

ты про это что ли? А при чем тут клиент? Что сервер клиентский сокет что-ли читает, а, дядя?

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

https://github.com/jasonish/libevent-examples/blob/master/echo-server/libeven...

$ ./a.out
Accepted connection from 127.0.0.1
Client disconnected.

$ telnet localhost 5555
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
5555
5555
5555
5555
^C^]

Еще раз повторю. Иди кури матчасть, хватит дураком себя выставлять.

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

Это ты что сейчас показал? Пытаешься тумана навести, чтобы никто не понял что ты облажался? К чему это все? Как это касается темы?

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

Этот клоун еще тут?

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

2. Connection: close означает всего-лишь, что соединение не является persistent, в HTTP/1.0 это стандартное поведение и заголовка в принципе нет. В HTTP/1.1 этот заголовок используется, чтобы показать что клиент либо не намерен больше посылать запросы используя текущее соединение на транспортном уровне, либо чтобы сообщить, что он вообще не поддерживает persistent connection.

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

Это пример, который демонстрирует определение закрытия соединения. В бусте делается аналогично, лениво самому писать, а сходу готовое не нашел.

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

Короче подведем итог:
Чтобы средствами boost asio определить факт отключения клиента(будь то обрыв соединения или нормальное закрытие сокета), нужно(на выбор):
1) Попробовать считать данные из сокета и смотреть при этом на длину считанных данных, либо на возникшее в процессе данной процедуры исключение; На основании этого делать заключение.
2) Попробовать записать данные в сокет, возможно пару раз, при этом также смотреть на возникшие исключения; На основании этого делать заключение.

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

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

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

который демонстрирует определение закрытия соединения

определение закрытия соединения кем? Сервером? То есть ты хочешь сказать, что сервер каким то образом отслеживает поведение клиента?

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

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

1) Попробовать считать данные из сокета и смотреть при этом на длину считанных данных, либо на возникшее в процессе данной процедуры исключение; На основании этого делать заключение.

Именно «пробовать» не надо. При закрытие коннекшена select/poll/epoll скажут, что сокет можно читать, boost вызовет твой колбек, где стоит read.

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

А какое отношение имеет HTTP к серверу?

То отношение, что это протокол взаимодействия сервера с клиентом.

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

В 1.54 в asio был баг со сбросом соединения. Но в 1.55 его вроде как починили.

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

То есть ты хочешь сказать, что сервер каким то образом отслеживает поведение клиента?

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

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

Я тебе дал ссылку на конкретную строчку кода. Выхлоп этой строчки есть в консоли.

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