2013-03-02 3 views
12

Я написал это небольшое серверное приложение в чистом C, которое слушает входящие соединения в данном порту, очень простые вещи.Как правильно поддерживать порт прослушивания в течение длительного времени?

Это идет с обычной процедурой инициализации сокета, создать socket() затем bind() к порту, говорит сво listen() и ifinitely петли через select() ожидая входящие соединения с accept().

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

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

Ожидаемое поведение? Должен ли я проверить какие-то исключения или сделать «проверку работоспособности» в гнезде для прослушивания, если это необходимо?

Код: https://gist.github.com/Havenard/e930be035a3bee75c018 (да, я понимаю, что я использую 0 в реплике на наличие ошибок, и это плохо pratice и прочее, но это не имеет отношения к вопросу, как я объяснил в комментариях, когда я устанавливаю дескриптора файла сокета для 0 - остановить цикл и закрыть приложение).

+4

Это не нормально. Я бы заподозрил утечку ресурсов в вашем приложении, например, закончил файловые дескрипторы, потому что вы не закрываете новый сокет, возвращаемый accept(). – rhashimoto

+0

Записываются дескрипторы, и обслуживание редко используется. Обычно он не получает новых соединений в течение нескольких недель. Кроме того, это не проблема accept(). Порт действительно закрывается, он disasppears из списка netstat, и другие приложения могут привязываться к этому порту, как будто ничего не использует. – Havenard

+1

Select() приведет к сообщению о недопустимом filedescriptor/error'd. Может быть, вы закрыли его dup2() над ним? – wildplasser

ответ

7

Я хотел бы начать с очисткой его:

  • вырезать его на более мелкие, считываемые, сверяемый, проверяемые функции
  • связанного использование списков выглядит неаккуратно; его можно было бы упростить, возможно, путем введения некоторых общих функций.
  • заменить все глупые «\ x20» символьные константы на более читаемые «эквиваленты»
  • избегать манифестных магических констант, как здесь if (n_case > 0) memcpy(nick, node->nick, (n_case > 32 ? 32 : n_case));; sizeof - ваш друг.
  • не использовать нуль в качестве значения дозорного значения для неиспользуемого дескриптора файла; вместо этого используйте -1.
  • использовать беззнаковые типы для размеров и индексов; отрицательные индексы будут повреждать память, сбрасываемые неподписанные типы будут работать быстро. (failfast - ваш друг)

Это только несколько часов редактирования.

Я предполагаю, что после очистки/рефакторинга ваша «ошибка» выйдет на поверхность магически.

Сноска: Нет, я не буду делать вашу работу за вас. Не для 100 очков, не для 1000. Пожалуйста, уберите свой беспорядок.

+0

Этот старый код - всего лишь пример программы, которую я знаю, которая в конечном итоге даст проблему. Я написал другие подобные программы, которые имеют одинаковую проблему, одна из которых работает на Windows Server. Я не сомневаюсь, что это не проблема, вызывающая проблему, потому что, как вы можете видеть, она должна выйти из нее, если 'select()' возвращает <0 и если 'skt_bind' установлен на ноль с некоторым исключением. Это единственные пути, по которым он закроет сокет 'skt_bind', и в обоих из них он завершит программу, чего не происходит, что означает, что' skt_bind' все еще существует как действительный сокет, несмотря на то, что порт закрыт. – Havenard

+0

Мое сомнение действительно в том, что могло бы закрыть открытый порт без моей программы и как я должен правильно его обрабатывать. Я уже пытался имитировать пару сетевых аварийных ситуаций, таких как изменение IP-адресов или отключение сетевого кабеля, но ничто не закрывает порт и не перестает работать из-за этого. Он закрывается после долгого открытия, и из-за этого долговременного фактора мне очень трудно контролировать и проверять, как действовать. – Havenard

+1

Он не закрыт без заботы вашей программы: он уверен, что прослушивание fd закрывается вашей программой где-то в ошибке вашего дела.Удалите в printf значения fd после его создания (или найдите его с lsof), затем в gdb поместите разрыв на dup2, close и fclose, чтобы найти место в коде, где вы перестаете слушать. –

0

Похоже, ваш код должен иметь 2 последовательных ошибки, чтобы вызвать сбой.

Если у вас есть ошибка в выборе, почему бы не распечатать, почему сразу?

В строке 281, printf errno/perror, чтобы узнать, в чем проблема?

+0

Если он когда-либо достигнет этого момента, программа закончится. Это не заканчивается, оно продолжается, как будто все в порядке. – Havenard

+0

О да, и эта идея проверки дважды была взята из исходного кода Linux inetd. Он проверяет дважды! – Havenard

1

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

И еще одна проблема - использование unsigned int для fds. системные вызовы return -1 при ошибке ... и эта ошибка не будет обнаружена с, если присвоено unsigned int struct identd_node -> unsigned int handle; struct thread_node -> unsigned int skt_clnt, skt_serv;

+0

Это была моя пуля # 6. Использование 0 в качестве контрольного значения для fds действительно очень неверно. – wildplasser

+0

Не имеет значения. Его значение проверяется только на 0, если оно установлено этим значением сигналом. Нет другого способа, которым он изменит свою ценность на этом этапе. – Havenard

2

Этот ответ, главным образом, обзор кода мест, в которых вы звоните close().

Строка 330: вы закрываете розетку, но не продолжаете сразу же, как в других местах вашего кода. Это может привести к странному поведению.

Линия 928: В большинстве мест вы устанавливаете клиентский или серверный сокет на 0 после звонка на close(). После этого звонка вы этого не сделаете.

Line 1193: Тот же комментарий, как линии 928.

Line 1195: Тот же комментарий, как линии 928.

Line 1218: Тот же комментарий, как линии 928.

Line 1234: Тот же комментарий, как линии 928.

Line 1236: Тот же комментарий, как линии 928.

Когда я скомпилировал код с полными предупреждениями, я видел ряд мест, где ком piler отметил функции, объявленные для возврата значения, но значение не возвращается.

x.c:582: warning: no return statement in function returning non-void 
x.c:591: warning: no return statement in function returning non-void 
x.c:598: warning: no return statement in function returning non-void 
x.c:609: warning: no return statement in function returning non-void 
x.c:620: warning: no return statement in function returning non-void 
x.c:728: warning: no return statement in function returning non-void 
x.c:779: warning: no return statement in function returning non-void 

Есть много других проблем, как указано в других сообщениях.

Что касается отладки этой проблемы, если я подозревал, что сокет привязки закрывается раньше, я бы перехватил вызов close() своей собственной версией, которая утверждает, что дескриптор, который должен быть закрыт, не должен соответствовать привязке.

Однако, как отметил wildplasser, select() вернет ошибку о недопустимом дескрипторе, если он был закрыт.

0

Хотя система не должна вести себя так, как описано, она иногда делает это. Для серверных систем обычно вам нужно выполнить вызов healthcheck, либо извне (из сценария), либо из специального потока в вашем коде.

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

+0

Иногда это *** когда? *** Я никогда не видел такого поведения за несколько десятилетий программирования сети. – EJP

Смежные вопросы