Здравствуйте Пишу небольшое серверное приложение, которое обслуживает несколько клиентов, с помощью WSASelect() и очереди сообщений главного GUI окна. Проблема в том, что я никак не могу поймать FD_CLOSE. Клиент — консольное приложение, которое подключается к серверу, обменивается какими-то данными и, после нажатия любой клавиши в консольном окне, завершается (тем самым разрывая соединение). Я "вовермя" получаю FD_ACCEPT и FD_READ, но FD_CLOSE приходит только после того, как клиент уже завершился и я его запустил вновь — т.е. после повторного соединения. Почему это может происходить? Разве завершение процесса — это не graceful disconnect? Клиентская система — unix (MacOS). Код (Text): SOCKET clientsAcceptingSocket = socket(PF_INET, SOCK_STREAM, 0); SOCKADDR_IN clientsAcceptingSockAddr; clientsAcceptingSockAddr.sin_addr.s_addr = htonl(INADDR_ANY); clientsAcceptingSockAddr.sin_port = htons(SERVER_ACCEPTS_CLIENTS_ON_PORT); clientsAcceptingSockAddr.sin_family = AF_INET; if(bind(clientsAcceptingSocket, (SOCKADDR*)&clientsAcceptingSockAddr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR) { //... exit(1); } listen(clientsAcceptingSocket, 0); if(WSAAsyncSelect(clientsAcceptingSocket,h_wnd,WM_SOCKET_CLIENT_TCP_EVENT, FD_ACCEPT|FD_READ|FD_CLOSE)) { //... exit(1); } В "message pump" я, при встрече сообщения WM_SOCKET_CLIENT_TCP_EVENT, вызываю эту функцию: Код (Text): void TCPSocketEvent(UINT uMsg, WPARAM wParam, LPARAM lParam) { if(WSAGETSELECTEVENT(lParam) & FD_ACCEPT) { //incoming connection cout << "FD_ACCEPT\n"; SOCKET acceptedSocket = accept(wParam, (struct sockaddr*)&acceptedSockAddr, &acceptedSockAddrLen); if(acceptedSocket!=INVALID_SOCKET) { //tcp connection successfully accepted } } if(WSAGETSELECTEVENT(lParam) & FD_CLOSE) { cout << "FD_CLOSE\n"; } if(WSAGETSELECTEVENT(lParam) & FD_READ) { cout << "FD_READ\n"; //здесь я читаю данные, и, возможно, отсылаю данные с помощью send() } Подскажите, что я не так делаю? Спасибо
Нужно явно закрывать соединение клиентом, иначе он не отправит сообщение о разрыве соединения серверу. В таком случае соединение будет висеть, пока не случится таймаут.
dinoweb Спасибо А не подскажете, какие существует способы на сервере определить что приложение-клиент "упало"?
AlwaysAlone Если вместе с клиентом не взорвался комп, то для TCP это будет "An existing connection was forcibly closed by the remote host".
_DEN_ Ezrah dinoweb Когда приложение-клиент падает, оно посылает RST вместо обмена тройным FIN - ACK+FIN - ACK. Как в Windows обработать эту ситуацию? Наипростейший способ - через WSAAsyncSelect - к сожалению не работает. FD_CLOSE приходит только для FIN-дисконнекта. Во многих системах (Mac/BSD/Linux) "приходит" нечто вроде FD_READ с 0 в качестве к-ва прочитанных байт, но только не в Windows. Как бы вы сделали?
AlwaysAlone Зачем тебе этот онанизм на голом апи? Возьми Boost.Asio - на нем твоя задача решается за 5 минут и без лишнего мусора.
Нет тут никакого мусора, на голом апи тоже все просто. AlwaysAlone, у вас ошибка в том, что вы забыли сделать WSAAsyncSelect для сокета передачи данных, acceptedSocket. FD_CLOSE приходит именно для него - ведь именно сокет передачи данных закрывается при закрытии TCP-соединения, а не clientsAcceptingSocket.
Всем спасибо за помощь Я не получал RST из-за того что приложение из-за своей специфики включало файрвол (divert TCP-траффика) и не выключало его по завершению, из-за чего TCP пакеты просто убивались. Я совершенно забыл и удивлялся почему же оно не работает, написал кучу тестов для разных систем и наконец-то удалось локализовать проблему. Извините за беспокойство Dmitry_Milk В msdn сказано что так что это наверное лишнее.. _DEN_ asio хорошая библиотека, вот только тянет она за собой целый boost. Для простенького низкоуровневого приложения для "принять 2 байта" мне хватает и встроенных средств OC Спасибо за советы