Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Сети > [asio] как узнать что произошёл disconnect


Автор: borisbn 22.12.2011, 09:44
Всем привет.

Есть asio::tcp::socket. Коннекчусь им к серверу. Если сервер выключили или пропало соединение, то как узнать об этом ?
И ещё вопросик. Есть ли функция получения текущего состояния сокета - есть коннект / нет коннекта / в процессе коннекта / в процессе дисконнекта ?

Спасибо.

Автор: boostcoder 22.12.2011, 09:53
Цитата(borisbn @  22.12.2011,  09:44 Найти цитируемый пост)
Если сервер выключили или пропало соединение, то как узнать об этом ?

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

Цитата(borisbn @  22.12.2011,  09:44 Найти цитируемый пост)
Есть ли функция получения текущего состояния сокета - есть коннект / нет коннекта / в процессе коннекта / в процессе дисконнекта ?

есть http://www.boost.org/doc/libs/1_48_0/doc/html/boost_asio/reference/basic_stream_socket/is_open.html. остального нет.

Добавлено через 11 минут и 25 секунд
при начале изучения asio, очень рекомендую прочесть http://www.boost.org/doc/libs/1_48_0/doc/html/boost_asio/overview/core.html. там описываются основные принципы/концепты asio, которые необходимо знать, хотя бы для правильного формулирования вопросов.

Автор: borisbn 22.12.2011, 14:01
Цитата(boostcoder @  22.12.2011,  09:53 Найти цитируемый пост)
которая по завершению вызывает назначенный ей хендлер. вот в этом хендлере по переданному ему коду ты и узнаешь о типе ошибки

Вот я и подумал, что туда попадёт только по завершению, а про то, что при разрыве соединения попадёт туда же - не сообразил. Спасибо.

Цитата(boostcoder @  22.12.2011,  09:53 Найти цитируемый пост)
есть is_open(). остального нет.

Опять же, неправильно понял... Подумал, что эта функция говорит, была ли вызвана ф-ция секета open(), а не "есть ли в данный момент коннект". 2-е спасибо. Раз уж про open() зашла речь, не подскажешь, нужно ли вообще её вызывать ? Зачем она нужна ? В описании очень скупо сказано
Цитата
Open the socket using the specified protocol.

В бустовских примерах её никто не вызывает...

Цитата(boostcoder @  22.12.2011,  09:53 Найти цитируемый пост)
при начале изучения asio, очень рекомендую прочесть Core Concepts and Functionality

Ессно прочёл. От корки до корки, но т.к. не нашёл там ответов на свои вопросы, то и пришёл задавать их здесь.

Автор: boostcoder 22.12.2011, 14:13
Цитата(borisbn @  22.12.2011,  14:01 Найти цитируемый пост)
нужно ли вообще её вызывать ? Зачем она нужна ?

она создает внутреннюю реализацию для указанного типа IP. вообще, никогда не использовал ее)

Цитата(borisbn @  22.12.2011,  14:01 Найти цитируемый пост)
В бустовских примерах её никто не вызывает...

в моих, тоже)

Автор: mabrarov 22.12.2011, 16:19
Цитата(borisbn @ 22.12.2011,  09:44)
Если сервер выключили или пропало соединение, то как узнать об этом ?

Если нет висящей (pending, активная) операции ввода/вывода/connect на сокете, то о том, что соединение закрыто узнать трудно. Сомневаюсь, что is_open сообщит о том, что "remote peer is disconnected". Даже если на сокете есть активная операция, то о том, что кто-то порвал/выдернул сетевой шнур TCP/IP-стек сообщит не сразу (если вообще сообщит) - обычно используется application level ping/pong.
Многие вопросы отпадут, если почитать http://www.proklondike.com/books/codingproch/codingproch_snader_effective_tcp_ip.html.
Удачи.

Автор: borisbn 22.12.2011, 16:45
mabrarov, спасибо, учту.
Цитата(mabrarov @  22.12.2011,  16:19 Найти цитируемый пост)
Многие вопросы отпадут, если почитать "Эффективное программирование TCP/IP" Йона Снейдера.

Уже начал читать. Спасибо.

Автор: borisbn 23.12.2011, 09:21
Ну... Чтобы не плодить тем спрошу ещё разок тут.
Правильно ли я понимаю, что
Цитата
Asynchronous completion handlers will only be called from threads that are currently calling io_service::run()

означает, что результат чтения/записи/коннекта мне прийдёт в потоке, который вызвал io_service::run() ?
Если да, то каким образом мне передать управление (и данные) из этого потока в основной (для отображения графики, например) ? Курить boost::signal ? М.б. strand::wrap() ?
Код

socket.async_read(... handler ); // (1)
boost::thread t( boost::bind( &boost::asio::io_service::run, &io ) ); // (2)
...
void handler(...) {
// хочу, чтобы этот код выполнялся в основном потоке приложения
// в том потоке, в котором выполнялись строки (1) и (2)
}

Спасибо.

Автор: boostcoder 23.12.2011, 10:32
Цитата(borisbn @  23.12.2011,  09:21 Найти цитируемый пост)
Правильно ли я понимаю, что ... означает, что результат чтения/записи/коннекта мне прийдёт в потоке, который вызвал io_service::run() ?

да.

Цитата(borisbn @  23.12.2011,  09:21 Найти цитируемый пост)
каким образом мне передать управление (и данные) из этого потока в основной (для отображения графики, например) ?

зависит от фреймворка, который использовался для создания гуя. для куте - http://developer.qt.nokia.com/doc/qt-4.8/qmetaobject.html#invokeMethod.

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

один из способов:
Код

io_service main_loop;
io_service work_pool;
boost::thread t(&boost::asio::io_service::run, boost::ref(work_pool));

ip::tcp::socket socket(work_pool);
socket.async_read(... handler );

...

void handler(...) {
   // хочу, чтобы этот код выполнялся в основном потоке приложения
   // в том потоке, в котором выполнялись строки (1) и (2)
   main_loop.post( ... );
}

...

main_loop.run();

Автор: borisbn 23.12.2011, 11:03
boostcoder, дело в том, что я пишу не программу, а библиотечку, и в каком она будет использована фреймворке мне не нужно знать (чисто для кути я уже сделал такую библиотечку, и "переношу" управление и данные между потоками сигнал/слотами). В частности, мне нужно, чтобы данная библиотека работала из-под дебилдера. Мой класс (строки (1) и (2) из моего псевдокода) будет создаваться в основном потоке билдера, туда же мне и нужно вернуть управление и данные.

Автор: boostcoder 23.12.2011, 11:15
Цитата(borisbn @  23.12.2011,  11:03 Найти цитируемый пост)
Мой класс (строки (1) и (2) из моего псевдокода) будет создаваться в основном потоке билдера, туда же мне и нужно вернуть управление и данные.

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

Добавлено через 2 минуты и 9 секунд
покажи желаемый пример использования твоей библиотеки.

Автор: mabrarov 23.12.2011, 12:32
Цитата(boostcoder @ 23.12.2011,  10:32)
зависит от фреймворка, который использовался для создания гуя. для куте - http://developer.qt.nokia.com/doc/qt-4.8/qmetaobject.html#invokeMethod.

Да нет же! smile
Если серьезно, то (IMHO) красивое решение для Qt я привел в asio samples - qt_echo_server. Достаточно обычной связки Qt signal + Qt slot + http://developer.qt.nokia.com/doc/qt-4.8/qt.html.

Добавлено @ 12:37
Цитата(borisbn @ 23.12.2011,  11:03)
boostcoder, дело в том, что я пишу не программу, а библиотечку, и в каком она будет использована фреймворке мне не нужно знать (чисто для кути я уже сделал такую библиотечку, и "переношу" управление и данные между потоками сигнал/слотами). В частности, мне нужно, чтобы данная библиотека работала из-под дебилдера. Мой класс (строки (1) и (2) из моего псевдокода) будет создаваться в основном потоке билдера, туда же мне и нужно вернуть управление и данные.

Делать надо так же как и в Delphi (в Qt это скрывается за Qt::QueuedConnection):
  • "Проектируешь" user defined window message так, чтобы основной поток билдера понимал его и мог из него восстановить все параметры вызова.
  • Там, где вызывается твой handler, заворачиваешь в user defined window message параметры вызова, который необходимо сделать в основном (он же GUI) потоке, и http://msdn.microsoft.com/en-us/library/windows/desktop/ms644944%28v=vs.85%29.aspx.
Очень геморно. Но на 2009 год в Delphi можно было только так.

boost::signal с передачей вызова в другой поток (что, по сути, "правильно" можно сделать только при помощи очереди) никак не поможет. Основной поток C++ Builder/Delphi крутит стандартный виндовый message loop. Так что практически единственное решение - кинуть сообщение в него. Ну а в сообщении как-то указать на сам вызов (что и с какими данными). При этом где-то внутрях Delphi можно было повеcить обработчик на Windows message (возможно, на объект класса Application - это лучше, чем на форму). 

P.S. Что за C++-программисты пошли, что не знают столь древний и почти стандартный трюк...

Автор: boostcoder 23.12.2011, 13:35
Цитата(mabrarov @  23.12.2011,  12:32 Найти цитируемый пост)
Что за C++-программисты пошли, что не знают столь древний и почти стандартный трюк

возможно потому, что некоторые никогда не видели дельфи, и никогда не использовали winapi.

Автор: borisbn 23.12.2011, 13:50
Цитата(mabrarov @  23.12.2011,  12:32 Найти цитируемый пост)
 Что за C++-программисты пошли, что не знают столь древний и почти стандартный трюк...

Обижаешь, начальник smile
Вот мой код 2006 года (лишнее опускаю)
Код

#define NET_MESSAGE (WM_USER + 777)
NetworkBase::NetworkBase()
{
  hWnd = CreateWindow("STATIC", "", 0, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, HWND_MESSAGE, NULL, HInstance, NULL);
  SetWindowLong(hWnd, GWL_USERDATA, (LONG)this);
  prevWndProc = (MY_WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC, (LONG)netWndProc);
}
/*static*/ LRESULT CALLBACK NetworkBase::netWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  NetworkBase *net = (NetworkBase*)GetWindowLong(hWnd, GWL_USERDATA);
  if ( NET_MESSAGE == message )
  {
    char* pack = (char*)wParam;
    SNetCommPostInfo &from = *((SNetCommPostInfo*)lParam);
    net->onNetMessage(pack, from); // callback наружу. в основной поток
  }
  return CallWindowProc(net->prevWndProc, hWnd, message, wParam, lParam);
}
void NetworkBase::staticNetHandler(char *pack, SNetCommPostInfo &from, void *context)
{
  NetworkBase *net = (NetworkBase*)context;
  SendMessage(net->hWnd, NET_MESSAGE, (unsigned int)(pack), (long)(&from));
}

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

Автор: mabrarov 23.12.2011, 14:07
Цитата(borisbn @ 23.12.2011,  13:50)
просто показалось некошерно использовать WinAPI в кроссплатформенном бустовском коде.
Думал, что этот трюк можно сделать средствами буста (передача управления и данных из одного потока в другой - читай SendMessage)

Ну, если подумать за boost-оводов - в каждой ОС своя очередь для основного потока, в консольных приложениях ее вообще нет -> сделать вызов в контексте основного потока без учета всех типов приложений на всех поддерживаемых ОС нереально. Хотя вот в Qt получилось. Ну так у них даже WinMain под виндой свой, да в каждом QThread может быть свой message loop. Не по-boost-вски навязывать столько всего.

Юзаешь GUI-вую библиотеку - юзай ее правила/методы вызова в контексте GUI-вого потока. smile

Автор: borisbn 23.12.2011, 16:45
Цитата(mabrarov @  23.12.2011,  14:07 Найти цитируемый пост)
сделать вызов в контексте основного потока без учета всех типов приложений на всех поддерживаемых ОС нереально

Подозревал это, но точно не знал. Жаль.
Цитата(mabrarov @  23.12.2011,  14:07 Найти цитируемый пост)
Юзаешь GUI-вую библиотеку - юзай ее правила/методы

Я ж говорю
Цитата(borisbn @  23.12.2011,  11:03 Найти цитируемый пост)
дело в том, что я пишу не программу, а библиотечку, и в каком она будет использована фреймворке мне не нужно знать


Ладно... А то мы уже воду ступе толчём.
Спасибо ещё раз.
Тему пока не закрываю. М.б. ещё появятся вопросы по asio - буду здесь же задавать, чтоб не плодить однородных тем.

Автор: mabrarov 23.12.2011, 17:28
Цитата(borisbn @ 23.12.2011,  16:45)
Цитата(mabrarov @  23.12.2011,  14:07 Найти цитируемый пост)
Юзаешь GUI-вую библиотеку - юзай ее правила/методы

Я ж говорю
Цитата(borisbn @  23.12.2011,  11:03 Найти цитируемый пост)
дело в том, что я пишу не программу, а библиотечку, и в каком она будет использована фреймворке мне не нужно знать

В том то и дело, что, похоже, дизайн Вашей библиотеки страдает: 
  • Если Ваша библиотека в указанном месте обещает сделать что-то асинхронно и вызвать по окончании какой-то код, то она обязана уточнить, в каком потоке будет произведен этот вызов. При чем, это будет либо internal поток, либо (как в Asio) поток, подконтрольный scheduler-у Вашей библиотеки (например, за счет message loop).
  • Если же Ваша библиотека в указанном месте обещает что-то сделать синхронно, то "тупой" wait on condition variable должен иметь место.
Я бы реализовал первый вариант и дал бы кучу binding-ов/wrapper-ов, позволяющих прокинуть вызов в GUI поток в зависимости от используемого "фреймворка".

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)