Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Программирование под Unix/Linux > Как проверить закрыт ли сокет


Автор: Zerstroer 12.8.2015, 14:22
Здравствуйте.
Знаю, что вопрос относительно тривиальный, но обращаюсь за помощью и что бы разобраться в ситуации.

Пишу под Linux. Работаю с сокетами. Использую "чистый" C.

Создаю сокет:

Код

   int sock_descr = socket(AF_INET, SOCK_STREAM, 0);


Благополучно соединяюсь с ним, работаю, по завершению работы или при возникновении ошибок делаю его close(sock_descr).
Далее, в зависимости от ситуации, мне нужно повторно использовать sock_descr.
Либо повторно им воспользоваться, если я не делал ранее его close, либо заново сделать connect и пользоваться им.
Вопрос заключается в следующем:
как в определенном месте однозначно надежно (и по возможности просто) выяснить, что этот дескриптор сокета был ранее closed?

Решение этого вопроса гуглил, но в ходу не совсем очевидные мне методы выяснения статуса сокета. Рекомендуют использовать read/write, но у меня вызывает сомнение использование этого метода.
Адекватный ли вариант "мануально" контролировать статус сокета посредством использования рукописной структуры? 
Пример:
Код

struct MySocket{
  int socket_descriptor;
  int status; //Статус сокета. Каждый раз при вызове close устанавливать его руками.
}


Заранее спасибо.

Автор: feodorv 12.8.2015, 15:58
Цитата(Zerstroer @  12.8.2015,  14:22 Найти цитируемый пост)
Либо повторно им воспользоваться, если я не делал ранее его close, либо заново сделать connect и пользоваться им.

Довольно странный подход. Если сокет был закрыт, то его дескриптор становится свободным, а память под внутренние структуры высвобождена. Как его можно использовать? Никак. К тому же любой последующий open/fopen/socket может занять этот дескриптор, и прощай надежды...


Цитата(Zerstroer @  12.8.2015,  14:22 Найти цитируемый пост)
Адекватный ли вариант "мануально" контролировать статус сокета посредством использования рукописной структуры? 

Опять-таки, после закрытия сокета его использовать никоим образом нельзя. Поэтому очень часто пишут так:
Код
if( sock >= 0 ) close( sock );
sock = -1;
Присвоение переменной сокета значения -1 как раз и будет указывать на то, что сокет был закрыт. Дополнительного значения status здесь не требуется. В дальнейшем в коде можно просто делать проверку на положительность значения переменной сокета:
Код
if( sock >= 0 ) send( sock, ...);


Автор: Zerstroer 12.8.2015, 19:47
Перечитал свой первый пост, достаточно запутанно описал ситуацию.
По сути, в случае ошибки, мне нужно делать полное переподключение и я использую один и тот же дескриптор сокета.
Как выходить из этой ситуации?
Вручную присваивать дескриптору после close значение -1? Как в таком случае открывать этот дескриптор повторно?

Автор: Alexeis 12.8.2015, 19:50
Цитата(feodorv @  12.8.2015,  16:58 Найти цитируемый пост)
Присвоение переменной сокета значения -1 как раз и будет указывать на то, что сокет был закрыт.

  ООП наш лучший друг в слежении за ресурсами.

Автор: feodorv 12.8.2015, 20:07
Цитата(Zerstroer @  12.8.2015,  19:47 Найти цитируемый пост)
По сути, в случае ошибки

В случае какой ошибки? Ошибки бывают слишком разные. 

Цитата(Zerstroer @  12.8.2015,  19:47 Найти цитируемый пост)
мне нужно делать полное переподключение и я использую один и тот же дескриптор сокета.

Зачем? В чем причина необходимости для использования того же дескриптора сокета? Почему бы просто не закрыть сокет, а потом его по-новой открыть в случае переподключения? У Вас им удерживается свой IP:port?

Автор: Zerstroer 12.8.2015, 20:29
Alexeis, ООП использовать не могу - "чистый" Си.
feodorv, по поводу ошибок. Имеются ввиду ошибки при подключении или при обмене данными (хост недоступен, порт занят, сервер не возвращает данные). В каждом из этих случаев мне необходимо осуществить несколько попыток повторного подключения. Я принял во внимание ваши советы, пересмотрел код. Вопросы стали еще проще и еще банальнее:
1. Фактически close не изменяет значение дескриптора сокета, а лишь закрывает его. Верно? Т.е. после close я могу присвоить переменной в которой ранее хранился действующий дескриптор сокета отрицательное значение для индикации того, что сокет закрыт?
2. Дескриптор действующего сокета не может быть отрицательным. Может ли дескриптор сокета быть равным 0?

Автор: feodorv 12.8.2015, 21:03
Цитата(Zerstroer @  12.8.2015,  20:29 Найти цитируемый пост)
1. Фактически close не изменяет значение дескриптора сокета, а лишь закрывает его. Верно? Т.е. после close я могу присвоить переменной в которой ранее хранился действующий дескриптор сокета отрицательное значение для индикации того, что сокет закрыт?

По сути верно, по форме я бы иначе сказал. Дело в том, что переменная сокета хранит дескриптор сокета, и при close данный дескриптор высвобождается и более ничему не соответствует (ни файлу, ни сокету), хотя переменная сокета продолжает хранить значение этого уже освобождённого дескриптора. Чтобы она этого не делала, и для индикации закрытости сокета, стОит присвоить переменной сокета отрицательное значение (обычно используют -1).


Цитата(Zerstroer @  12.8.2015,  20:29 Найти цитируемый пост)
Дескриптор действующего сокета не может быть отрицательным.

Не может. Более того, отрицательное значение, возвращаемое вызовом socket(), сигнализирует об ошибке. 


Цитата(Zerstroer @  12.8.2015,  20:29 Найти цитируемый пост)
Может ли дескриптор сокета быть равным 0? 

Вообще, значения дескрипторов 0, 1 и 2 отведены под stdin, stdout и stderr. Если их последовательно закрыть, а затем сделать вызов socket(), то вполне возможно получить нулевое значение дескриптора сокета. По крайней мере, так было в Юниксах лет 10 назад, к сожалению, меня с тех пор от них оторвали, я не в курсе последних тенденций в этом направлении. Но учитывая значительный консерватизм в разработке Юниксов, не думаю, что здесь что-то изменилось. Можно, кстати, поставить эксперимент. Но в любом случае по стандарту нужно быть готовым к тому, что значение дескриптора сокета может быть нулём)))

Автор: Zerstroer 12.8.2015, 21:14
feodorv, большое спасибо! Вы все объяснили до очевидного понятно.
Ответы на все сформулированные вопросы я получил. Тему закрою если не возникнет дополнительных вопросов при исправлении взаимодействия с сокетами в коде.

Автор: Zerstroer 13.8.2015, 12:20
Помечаю вопрос как решенный.
Воспользовался советами feodorv.

Автор: svlary 17.8.2015, 14:33
Цитата(Zerstroer @  12.8.2015,  21:14 Найти цитируемый пост)
до очевидного понятно.


Там есть одна заморочка, с которой я столкнулся на практике...  Описываю ситуацию
  • Ваш сервер установил ТСР соединение с клиентом
  • Идёт приём данных от клиента
  • Что-то случилось на сервере и он закрывает все сокеты и пытается перестартоваться
  • Но приём данных не завешён и может быть завершён - неизвестно когда. ОС не освобождает сетевой адрес этого соединения
  • При повторном открытии сокета и его биндинге возникает конфликт

 Было у меня это очень давно и потому "помню туманно" smile
Но там как-то надо использовать опцию сокета SO_REUSEBLE_ADDR.  Как-то так она пишется...

Автор: Alexeis 17.8.2015, 15:50
Цитата(svlary @  17.8.2015,  15:33 Найти цитируемый пост)
Но приём данных не завешён и может быть завершён - неизвестно когда. ОС не освобождает сетевой адрес этого соединения

  ОС закрывает соединение при возникновении любой неисправимой ошибки. Но это не имеет отношения к теме обсуждения. Автор спрашивал как проверить закрыт или открыт сокет, а не соединение. Сокет может быть открытым как при открытом так и при закрытом соединениях. Есть еще вариант проверки при помощи функции recv . После ожидания таймаута открытый сокет с открытым соединением вернет EAGAIN или EWOULDBLOCK. Закрытый сокет или в состоянии ошибки вернет код ошибки моментально. Но этот способ страдает таймаутом, так что не эффективен.

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