Повторное использование сокетов и файловых дескрипторов (или их отсутствие) - PullRequest
2 голосов
/ 05 августа 2009

Я получаю сообщение об ошибке «Слишком много открытых файлов» после вызова сокета в приведенном ниже коде сервера. Этот код вызывается многократно, и это происходит только после того, как server_SD получает значение 1022. Поэтому я предполагаю, что я достигаю предела 1024, как это запрещено «ulimit -n». Что я не понимаю, так это то, что я закрываю сокет, что должно сделать fd многоразовым, но этого, похоже, не происходит.

Примечания: Используя linux, да и клиент также закрыт, нет, я не являюсь пользователем root, поэтому перемещение ограничений не вариант, у меня должно быть максимум 20 (или около того) сокетов, открытых одновременно. В течение всей жизни моей программы я ожидал бы открыть и закрыть около 1000000 сокетов (следовательно, нужно многократно использовать очень сильно).

  server_SD = socket (AF_INET, SOCK_STREAM, 0);  
  bind (server_SD, (struct sockaddr *) &server_address, server_len)  
  listen (server_SD,1)  
  client_SD = accept (server_SD, (struct sockaddr *)&client_address, &client_len)  
  // read, write etc...   
  shutdown (server_SD, 2);  
  close (server_SD)

Кто-нибудь знает, как гарантировать закрытие и повторное использование?

Спасибо.

Ответы [ 6 ]

2 голосов
/ 05 августа 2009

Запустите вашу программу под valgrind с опцией --track-fds=yes:

valgrind --track-fds=yes myserver

Вам также может понадобиться --trace-children=yes, если ваша программа использует оболочку или она помещается в фоновом режиме.

Если он не завершает свою работу самостоятельно, прервите его или завершите процесс с помощью «kill pid » ( not -9) после накопления какого-либо утечка файла дескрипторы. При выходе valgrind покажет дескрипторы файлов, которые все еще открыты, и трассировка стека, соответствующая тому, где они были созданы.

Запуск вашей программы под strace для регистрации всех системных вызовов также может быть полезен. Еще одна полезная команда - /usr/sbin/lsof -p pid , чтобы отобразить все используемые в настоящее время файловые дескрипторы и то, для чего они используются.

2 голосов
/ 05 августа 2009

Из вашего описания похоже, что вы открываете сокет сервера для каждого accept(2). Это не обязательно. Создайте сокет сервера один раз, bind(2) it, listen(2), затем вызовите accept(2) в нем в цикле (или еще лучше - передайте его poll(2))

Редактировать 0:

Кстати, shutdown(2) на прослушивающем сокете совершенно бессмысленен, он предназначен только для подключенных сокетов.

0 голосов
/ 05 августа 2009

Перед закрытием сервера необходимо закрыть клиент (в обратном порядке к моему коду выше!) Спасибо всем, кто предложил предложения!

0 голосов
/ 05 августа 2009

Похоже, у вас может быть проблема "TIME_WAIT". IIRC, TIME_WAIT - это один из состояний, в котором может находиться сокет TCP, и он вводится, когда обе стороны закрыли соединение, но система некоторое время удерживает сокет, чтобы избежать принятия отложенных сообщений в качестве полезной нагрузки для последующих соединений.

Возможно, вы посмотрите на это (внизу страницы 99 и вверху 100). А может быть, что другой вопрос .

0 голосов
/ 05 августа 2009

Вы используете fork()? если это так, ваши дети могут наследовать открытые файловые дескрипторы. Если это так, то вы должны заставить ребенка закрыть все fd, которые ему не принадлежат.

0 голосов
/ 05 августа 2009

Возможно, ваша проблема в том, что вы не указываете флаг SO_REUSEADDR?

Из справочной страницы :

SO_REUSEADDR Указывает, что правила, используемые при проверке адресов, предоставленных в вызове bind (2), должны разрешать повторное использование локальных адресов. Для сокетов PF_INET это означает, что сокет может связываться, кроме случаев, когда есть активный сокет прослушивания, связанный с адресом Когда прослушивающий сокет связан с INADDR_ANY с определенным портом, то невозможно привязать к этому порту какой-либо локальный адрес.

...