Как закрыть сокет?
От: Corvin Украина  
Дата: 02.10.02 12:35
Оценка:
Такой проблем: есть клиент и сервер. На клиенте открывается окошко куда ждут ввод, но если связь с сервером обрывается это окошко надо закрыть. Для проверки наличия связи на клиенте вызываю recv. И если получается ошибка, окошко закрываем. Все работает если на сервере программу закрыли, но возникла необходимость это окошко закрыть с сервера из программы. А тут не получается... Делал shutdown, closesocket, — все равно, — клиент не понимает, что связь оборвалась. Неужто винда еще что-то с сокетом делает когда прога закрывается?
Re: Как закрыть сокет?
От: Gosha Украина  
Дата: 02.10.02 19:20
Оценка:
Здравствуйте Corvin, Вы писали:

C>Такой проблем: есть клиент и сервер. На клиенте открывается окошко куда ждут ввод, но если связь с сервером обрывается это окошко надо закрыть. Для проверки наличия связи на клиенте вызываю recv.

Проверить сокет на возможность записи в него данных можно с помощью функции select (см. аргумент writefds)
C>И если получается ошибка, окошко закрываем. Все работает если на сервере программу закрыли, но возникла необходимость это окошко закрыть с сервера из программы. А тут не получается... Делал shutdown, closesocket, — все равно, — клиент не понимает, что связь оборвалась. Неужто винда еще что-то с сокетом делает когда прога закрывается?
Ничего не понял, кто, "из кого" и кого закрывает По-яснее выражайся.
Но это вообщем-то не важно. Проблема видимо в твоей проверке recv-ом и в том, что у recv-а есть таймаут на получение данных. Видимо, пока этот таймаут не проходит — клиент висит. Так что select.
Re[2]: Как закрыть сокет?
От: NavuhodonosoR Россия  
Дата: 03.10.02 05:39
Оценка: 1 (1)
Здравствуйте Gosha, Вы писали:

G>Здравствуйте Corvin, Вы писали:


C>>Такой проблем: есть клиент и сервер. На клиенте открывается окошко куда ждут ввод, но если связь с сервером обрывается это окошко надо закрыть. Для проверки наличия связи на клиенте вызываю recv.

G>Проверить сокет на возможность записи в него данных можно с помощью функции select (см. аргумент writefds)
C>>И если получается ошибка, окошко закрываем.

Неправильно. Сокет закрыт <=> recv возвращает 0.

G>Но это вообщем-то не важно. Проблема видимо в твоей проверке recv-ом и в том, что у recv-а есть таймаут на получение данных. Видимо, пока этот таймаут не проходит — клиент висит. Так что select.


Не пойдет. select через readfds скажет, что из сокета можно прочитать либо что сокет закрыт. При этом для проверки "закрыт ли сокет?" по любому придется вызывать recv.
Re[3]: Как закрыть сокет?
От: Gosha Украина  
Дата: 03.10.02 16:41
Оценка: -1
Здравствуйте NavuhodonosoR, Вы писали:

C>>>Такой проблем: есть клиент и сервер. На клиенте открывается окошко куда ждут ввод, но если связь с сервером обрывается это окошко надо закрыть. Для проверки наличия связи на клиенте вызываю recv.

G>>Проверить сокет на возможность записи в него данных можно с помощью функции select (см. аргумент writefds)
C>>>И если получается ошибка, окошко закрываем.

NR>Неправильно. Сокет закрыт <=> recv возвращает 0.

Кто бы спорил! Только вот, в таком случае по сокету гоняются левые, никому не нужные (в данной постановке), данные. Потому как если recv не вернул 0 и не вернул SOCKET_ERROR, то он должен вернуть количество принятых байт, а их для этого надо послать. Делать это только для проверки "живости" сокета, я считаю, лишне.

G>>Но это вообщем-то не важно. Проблема видимо в твоей проверке recv-ом и в том, что у recv-а есть таймаут на получение данных. Видимо, пока этот таймаут не проходит — клиент висит. Так что select.


NR>Не пойдет. select через readfds скажет, что из сокета можно прочитать либо что сокет закрыт. При этом для проверки "закрыт ли сокет?" по любому придется вызывать recv.


При чем тут readfds? Я говорил про writefds. В клиент-серверных отношениях рулит клиент, а сервер выполняет пассивную роль. С этой точки зрения "живость" сокета должна проверяться клиентом, и именно на возможность записи в него (в сокет), т.е. проверка способности клиента рулить.
А вообще, это дело вкуса
Re[4]: Как закрыть сокет?
От: NavuhodonosoR Россия  
Дата: 04.10.02 06:21
Оценка: 5 (1)
Здравствуйте Gosha, Вы писали:

NR>>Неправильно. Сокет закрыт <=> recv возвращает 0.


G>Кто бы спорил! Только вот, в таком случае по сокету гоняются левые, никому не нужные (в данной постановке), данные. Потому как если recv не вернул 0 и не вернул SOCKET_ERROR, то он должен вернуть количество принятых байт, а их для этого надо послать. Делать это только для проверки "живости" сокета, я считаю, лишне.


Не понял Какие еще "левые данные"? Если данные идут, то они информативны, а если данных нет, то вызов recv() на блокирующем сокете остановит задачу либо до получения данных (recv() > 0) либо до закрытия сокета (recv() == 0), а на неблокирующем — либо скажет, что данных еще нет (recv() == -1 && WSAGetLastError() == WSAEWOULDBLOCK) либо что сокет закрыт (recv() == 0)

И, кстати, если ты не споришь с утверждением "сокет закрыт <=> recv возвращает 0", то почему ты считаешь лишним "Делать это только для проверки "живости" сокета"? Или может быть было не совсем понятно, что знак "<=>" означает "необходимо и достаточно"?

NR>>Не пойдет. select через readfds скажет, что из сокета можно прочитать либо что сокет закрыт. При этом для проверки "закрыт ли сокет?" по любому придется вызывать recv.


G>При чем тут readfds? Я говорил про writefds...


Я знаю, что ты говорил про writefds. Но если сервер закроет соединение, то на клиенте этот факт отразится на значении readfds, а не writefds.

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


Не совсем понимаю, что здесь есть "живость" сокета, подозреваю что "сервер доступен и готов обрабатывать запросы". Только такая проверка называется ping'ом (не путать с одноименной программой и не ограничиваться ICMP) и делается все равно иначе.

Если же ты имеешь ввиду состояние сокета "соединение закрыто", то смотри выше.

G>А вообще, это дело вкуса


Что дело вкуса? Правильно или неправильно решать задачи?
Re[5]: Как закрыть сокет?
От: Gosha Украина  
Дата: 04.10.02 16:34
Оценка:
Здравствуйте NavuhodonosoR, Вы писали:

NR>>>Неправильно. Сокет закрыт <=> recv возвращает 0.


G>>Кто бы спорил! Только вот, в таком случае по сокету гоняются левые, никому не нужные (в данной постановке), данные. Потому как если recv не вернул 0 и не вернул SOCKET_ERROR, то он должен вернуть количество принятых байт, а их для этого надо послать. Делать это только для проверки "живости" сокета, я считаю, лишне.


NR>Не понял Какие еще "левые данные"? Если данные идут, то они информативны, а если данных нет, то вызов recv() на блокирующем сокете остановит задачу либо до получения данных....

Совершенно верно. Только остановит он поток до наступления таймаута! А потом recv вернет ошибку. Ну, разве что траффик постоянный в оба конца, хотя, все равно. Клиент (приложение), который решает что сервер мертв, если на протяжении таймаута операции получения (recv) ничего от него не получает... Если ты считаешь этот "способ" проверить наличие связи лучше select-а — ну что поделаешь, ставь мне еще один ноль А у меня тогда другой вопрос. На фига вообще select есть, а?

NR>И, кстати, если ты не споришь с утверждением "сокет закрыт <=> recv возвращает 0", то почему ты считаешь лишним "Делать это только для проверки "живости" сокета"?

Агрументы выше.

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


NR>Не совсем понимаю, что здесь есть "живость" сокета, подозреваю что "сервер доступен и готов обрабатывать запросы".

Точно так.
NR>Только такая проверка называется ping'ом (не путать с одноименной программой и не ограничиваться ICMP) и делается все равно иначе.
Спасибо, что обратил внимание на сходство, теперь я их точно не перепутаю

G>>А вообще, это дело вкуса

NR>Что дело вкуса? Правильно или неправильно решать задачи?
Вместо того, чтобы нулями кидатся лучше аргументы по-убедительнее найти.
Re[6]: Как закрыть сокет?
От: Andrew S Россия http://alchemy-lab.com
Дата: 05.10.02 15:54
Оценка: 5 (1)
На самом деле — NavuhodonosoR абсолютно прав (может, только не стоило 0 ставить). select после убийства удаленного конца вполне дружественно реагирует что на readfds, что на writefds. Т.е. возвращает то, что сокет готов к приему либо отправке даннных. А вот recv возвращает значение <= 0. Так что — только на recv. Кстати, по умолчанию таймаута на recv нет (INFINITE), так что все работает почти логично, а если установить — то вернет WSAETIMEDOUT, что тоже можно отловить и запустить его еще раз при необходимости.


Здравствуйте Gosha, Вы писали:

G>Здравствуйте NavuhodonosoR, Вы писали:


NR>>>>Неправильно. Сокет закрыт <=> recv возвращает 0.


G>>>Кто бы спорил! Только вот, в таком случае по сокету гоняются левые, никому не нужные (в данной постановке), данные. Потому как если recv не вернул 0 и не вернул SOCKET_ERROR, то он должен вернуть количество принятых байт, а их для этого надо послать. Делать это только для проверки "живости" сокета, я считаю, лишне.


NR>>Не понял Какие еще "левые данные"? Если данные идут, то они информативны, а если данных нет, то вызов recv() на блокирующем сокете остановит задачу либо до получения данных....

G>Совершенно верно. Только остановит он поток до наступления таймаута! А потом recv вернет ошибку. Ну, разве что траффик постоянный в оба конца, хотя, все равно. Клиент (приложение), который решает что сервер мертв, если на протяжении таймаута операции получения (recv) ничего от него не получает... Если ты считаешь этот "способ" проверить наличие связи лучше select-а — ну что поделаешь, ставь мне еще один ноль А у меня тогда другой вопрос. На фига вообще select есть, а?

NR>>И, кстати, если ты не споришь с утверждением "сокет закрыт <=> recv возвращает 0", то почему ты считаешь лишним "Делать это только для проверки "живости" сокета"?

G>Агрументы выше.

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


NR>>Не совсем понимаю, что здесь есть "живость" сокета, подозреваю что "сервер доступен и готов обрабатывать запросы".

G>Точно так.
NR>>Только такая проверка называется ping'ом (не путать с одноименной программой и не ограничиваться ICMP) и делается все равно иначе.
G>Спасибо, что обратил внимание на сходство, теперь я их точно не перепутаю

G>>>А вообще, это дело вкуса

NR>>Что дело вкуса? Правильно или неправильно решать задачи?
G>Вместо того, чтобы нулями кидатся лучше аргументы по-убедительнее найти.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re: Как закрыть сокет?
От: Smart Россия  
Дата: 05.10.02 20:22
Оценка:
Здравствуйте Corvin, Вы писали:

C>Такой проблем: есть клиент и сервер...<CRLF>

.<CRLF>


1: recv() для SOCK_STREAM в случае закрытия сокета возвращает 0 или SOCKET_ERROR.
2: не забудьте в select() (если вы вообще используете эту функцию) указать группу сокетов для чтения (у сомого такая фигня была, часа 2 копался, а всё было так просто.
3: не используйте примочки Windows Sockets. Лучше уж старые Berkeley-совместимые (хоть и не полностью), но проверенные функции.
4: если вы храните дескриптор сокета в какой-нибудь динамической структуре данных, то проследите за тем, чтобы на стадии закрытия дескриптор всё-таки существовал (не удаляйте объект раньше времени).
5: у меня какой-то баг с STLport при посылке тескта сообщения (берётся из файла и на экран выводится вроде правильно, со всеми служебными символами, а Outlook Express продолжает ждать после приёма <CRLF>.<CRLF> ), а под Dinkumware STL (VC60SP5) всё нормально.
Re[7]: Как закрыть сокет?
От: Gosha Украина  
Дата: 05.10.02 20:37
Оценка:
Здравствуйте Andrew S, Вы писали:

AS>На самом деле — NavuhodonosoR абсолютно прав (может, только не стоило 0 ставить). select после убийства удаленного конца вполне дружественно реагирует что на readfds, что на writefds. Т.е. возвращает то, что сокет готов к приему либо отправке даннных.

Так что получается, MSDN врет о том, что "Connection has been closed/reset/terminated" выяняется только через readfds? И что, select не вернет SOCKET_ERROR если закрыт (нормально закрыт) удаленный конец сокета указанного в writefds (ведь select должен вернуть если 1) сокет готов для записи — не готов, закрыт; 2) таймаут select-а вышел — допустим таймаут 0; 3) SOCKET_ERROR — произошла ошибка — по моему, возващать в этом случае больше нечего)? Если это все на практике не так, тогда я сдаюсь
AS>А вот recv возвращает значение <= 0. Так что — только на recv. Кстати, по умолчанию таймаута на recv нет (INFINITE), так что все работает почти логично, а если установить — то вернет WSAETIMEDOUT, что тоже можно отловить и запустить его еще раз при необходимости.
Я согласен, что можно (или теперь уже нужно) так. Смутило то, что в первом постинге человек написал: для проверки наличия связи на клиенте вызываю recv. Ведь recv нужен чтобы получать данные, а не проверять соединение. Но раз select ведет себя так, как Вы сказали... ну что поделаешь, пусть будет recv .
Просто, в тех приложениях, которые я писал с использованием сокетов, у меня не возникало такой необходимости как проверка закрытия (нормального закрытия) сокета (не путать с обрывом связи). То есть, либо клиент и сервер обменялить сообщениями согласно протокола и отвалились оба, либо на каком-то этапе произошла ошибка (очередную команду не может отдать клиент — send или он не может получить ответ — recv), и тогда разговор другой.
Re[2]: Как закрыть сокет?
От: Andrew S Россия http://alchemy-lab.com
Дата: 05.10.02 20:37
Оценка:
Вот, читаем MSDN:

For connection-oriented sockets, readability can also indicate that a request to close the socket has been received from the peer. If the virtual circuit was closed gracefully, and all data was received, then a recv will return immediately with zero bytes read. If the virtual circuit was reset, then a recv will complete immediately with an error code such as WSAECONNRESET.

Итак — select при закрытии сокета почти бесполезен и вернет то, что есть данные на входе. И только чтением потом мы выясним, что собственно, канала уже нет. Очевидно, код ошибки получаем WSAGetLastError или просто GetLastError.
Это собственно, не супротив постинга, а так, для ясности


Здравствуйте Smart, Вы писали:

S>Здравствуйте Corvin, Вы писали:


C>>Такой проблем: есть клиент и сервер...<CRLF>

S>.<CRLF>
S>-)

S>1: recv() для SOCK_STREAM в случае закрытия сокета возвращает 0 или SOCKET_ERROR.

S>2: не забудьте в select() (если вы вообще используете эту функцию) указать группу сокетов для чтения (у сомого такая фигня была, часа 2 копался, а всё было так просто.
S>3: не используйте примочки Windows Sockets. Лучше уж старые Berkeley-совместимые (хоть и не полностью), но проверенные функции.
S>4: если вы храните дескриптор сокета в какой-нибудь динамической структуре данных, то проследите за тем, чтобы на стадии закрытия дескриптор всё-таки существовал (не удаляйте объект раньше времени).
S>5: у меня какой-то баг с STLport при посылке тескта сообщения (берётся из файла и на экран выводится вроде правильно, со всеми служебными символами, а Outlook Express продолжает ждать после приёма <CRLF>.<CRLF> ), а под Dinkumware STL (VC60SP5) всё нормально.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[8]: Как закрыть сокет?
От: Andrew S Россия http://alchemy-lab.com
Дата: 05.10.02 20:52
Оценка: 6 (1)
Здравствуйте Gosha, Вы писали:

G>Так что получается, MSDN врет о том, что "Connection has been closed/reset/terminated" выяняется только через readfds? И что, select не вернет SOCKET_ERROR если закрыт (нормально закрыт) удаленный конец сокета указанного в writefds (ведь select должен вернуть если 1) сокет готов для записи — не готов, закрыт; 2) таймаут select-а вышел — допустим таймаут 0; 3) SOCKET_ERROR — произошла ошибка — по моему, возващать в этом случае больше нечего)? Если это все на практике не так, тогда я сдаюсь


MSDN не врет — просто повнимательнее прочитай описание к функции select.
For connection-oriented sockets, readability can also indicate that a request to close the socket has been received from the peer. If the virtual circuit was closed gracefully, and all data was received, then a recv will return immediately with zero bytes read. If the virtual circuit was reset, then a recv will complete immediately with an error code such as WSAECONNRESET.

К сожелению — эта ошибка (с select), на которую попадаются все, кто начинает работать с сокетами, не избежал ее в свое время и я. На практике select, как и следует из MSND, возвращает количество "сработавших" сокетов, к коим относятся и сокеты, которые назодятся в readfds и у которых потерян удаленный конец, независимо, корректно там это сделано (socketclose, shutdown) или нет (например, просто вывалилось приложение с ошибкой). А далее — recv возвращает значение <=0 (на самом деле чаще всего 0), и далее надо выясянить код ошибки.


AS>>А вот recv возвращает значение <= 0. Так что — только на recv. Кстати, по умолчанию таймаута на recv нет (INFINITE), так что все работает почти логично, а если установить — то вернет WSAETIMEDOUT, что тоже можно отловить и запустить его еще раз при необходимости.

G>Я согласен, что можно (или теперь уже нужно) так. Смутило то, что в первом постинге человек написал: для проверки наличия связи на клиенте вызываю recv. Ведь recv нужен чтобы получать данные, а не проверять соединение. Но раз select ведет себя так, как Вы сказали... ну что поделаешь, пусть будет recv .
G>Просто, в тех приложениях, которые я писал с использованием сокетов, у меня не возникало такой необходимости как проверка закрытия (нормального закрытия) сокета (не путать с обрывом связи). То есть, либо клиент и сервер обменялить сообщениями согласно протокола и отвалились оба, либо на каком-то этапе произошла ошибка (очередную команду не может отдать клиент — send или он не может получить ответ — recv), и тогда разговор другой.
http://www.rusyaz.ru/pr — стараемся писАть по-русски
Re[9]: Как закрыть сокет?
От: Gosha Украина  
Дата: 05.10.02 21:39
Оценка:
Здравствуйте Andrew S, Вы писали:

Да, Вы и NavuhodonosoR оказались совершенно правы (если кого обидел — не хотел). Я тут не поленился налабать тестовый пример (обработка ошибок для краткости опущена), может народу пригодится — мимо граблей пройти

// _select_test.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <windows.h>
#include <winsock2.h>

#define PORT 10

DWORD WINAPI ClientThreadFunc(LPVOID lparam){

    LPHOSTENT phostent;
    SOCKADDR_IN remoteAddr;
    phostent=gethostbyname("localhost");
    SOCKET data_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    memset(&remoteAddr,0,sizeof(remoteAddr)); 
    memcpy(&(remoteAddr.sin_addr),phostent->h_addr,phostent->h_length); 
    remoteAddr.sin_family = phostent->h_addrtype; 
    remoteAddr.sin_port = htons(PORT);
    if (!connect(data_socket,(PSOCKADDR)&remoteAddr,sizeof(remoteAddr)))
        printf("connect() - OK\n");

    FD_SET set;
    FD_ZERO(&set);
    FD_SET(data_socket,&set);

    while (true){
        //int res=select(0,&set,NULL,NULL,NULL); // 1
        //1 не возвращает пока не закроется сокет на сервере 
        //(при условии, конечно,что читать из него нечего)
        int res=select(0,NULL,&set,NULL,NULL); // 2        
        //2 возвращает 1 не смотря на то, что сокет на сервере закрыт :(
        //что просто возмутительно :) Ведь на самом деле,
        //я уже ничего не смогу записать в этот сокет - 
        //так вернула бы 0 или SOCKET_ERROR! Хотя, если учесть,
        //что сокетов в наборе может быть много... может так и лучше.
        //а 0 зарезервирован для time limit expired.
        printf("select() result: %d\n",res);
        Sleep(1000);
    }
    return 0;
}

int main(int argc, char* argv[])
{
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2,2),& wsaData);

    SOCKADDR_IN localAddr;
    SOCKET listen_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    memset(&localAddr,0,sizeof(SOCKADDR_IN));
    localAddr.sin_family = AF_INET; 
    localAddr.sin_port = htons (PORT); 
    localAddr.sin_addr.s_addr = ADDR_ANY;
    bind(listen_socket,(SOCKADDR *)&localAddr,sizeof(SOCKADDR_IN));
    listen(listen_socket,1);

    DWORD ThreadId;
    HANDLE hThread=CreateThread(NULL,NULL,
        (LPTHREAD_START_ROUTINE)ClientThreadFunc,NULL,NULL,&ThreadId);
    CloseHandle(hThread);

    SOCKET data_socket=accept(listen_socket,NULL,NULL);
    closesocket(listen_socket);
    Sleep(5000);
    closesocket(data_socket);
    printf("server side socket closed\n");
    Sleep(5000);

    WSACleanup();    

    return 0;
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.