В Linux автоматически происходит тайм-аут входящих соединений сразу после их принятия.

Вопрос или проблема

У нас есть выделенный сервер, который работает нормально и принимает входящие подключения. Он также корректно отправляет и получает данные.

Он отлично работает в течение нескольких часов, но после этого все принятые подключения автоматически истекают, как только сокет принят.

Чем больше он принимает сокетов, тем скорее он переходит в состояние, при котором все новые подключения истекают.

Проблема не в клиенте и не в установленном нами времени таймаута, потому что это происходит сразу после принятия в функции read().

Когда мы перезагружаем сервер, все начинает снова работать прекрасно.

Я думаю, что проблема связана с Linux. Я не очень хорошо знаю Linux, я знаю, что в Linux есть конфигурации, которые ограничивают мой процесс.

Может кто-нибудь порекомендовать материалы для чтения о конфигурациях Linux для TCP-сокетов?

Спасибо.

Не могли бы вы предоставить больше деталей? Какой сервис принимает подключения? Какой дистрибутив используется (пожалуйста, предоставьте вывод команды uname -a)? У вас есть какие-либо данные в журналах сервиса?

Вы можете проверить системные журналы с помощью следующих команд: systemctl status <service>, чтобы увидеть статус сервиса и несколько строк журналов.

И journalctl -x –lines 100 -u <service>, чтобы увидеть последние 100 строк журналов сервиса.

Также команда dmesg или файлы, такие как /var/log/syslog и /var/log/kern.log в случае систем, подобных Debian, могут содержать дополнительную информацию. Или /var/log/messages в случае систем, подобных RHEL.

Проблема также может быть связана с количеством открытых файлов или конфигурацией сервиса (например, пределом соединений в конфигурации базы данных или максимальным количеством потоков в Tomcat). Обычно современные системы не нуждаются в изменении настроек по умолчанию. Но некоторые старые системы могут не открыть новые подключения, если лимит превышен. https://linux.die.net/man/5/limits.conf

Настройки ядра, связанные с сетевыми соединениями: https://docs.kernel.org/networking/ip-sysctl.html

Параметры могут быть настроены для всех интерфейсов или для каждого интерфейса с использованием соответствующего пути. Настройки могут быть изменены на лету двумя способами (например, отключение IPv6):

  1. echo 1 > /proc/sys/net/ipv6/conf/all/disable_ipv6 – отключает IPv6 для всех интерфейсов до перезагрузки
  2. sysctl -w net.ipv6.conf.ens160.disable_ipv6=0 >> /etc/sysctl.d/sysctl.conf – отключает IPv6 для интерфейса ens160 и добавляет соответствующую строку в конец /etc/sysctl.d/sysctl.conf. В этом случае настройки будут применены при следующей перезагрузке.

Полезные команды, которые могут помочь: lsof для отображения открытых файлов (соединение также считается файлом) и ss или netstat.

Например, lsof -i:8090 отобразит все соединения с портом 8090. А lsof -p 2345 отобразит все файлы и соединения, открытые процессом с PID 2345.

Обратите внимание, что перед внесением любых изменений в параметры ядра лучше узнать истинную причину.

После месяцев борьбы с этой проблемой я нашел корень проблемы.

Я закрыл fd 0, который является стандартным вводом, и новые входящие сокеты захватывали fd 0, в то время как в моем коде я ожидал, что любой действительный fd больше 0, в противном случае это означает, что он уже закрыт или должен быть исключен из действительных соединений (и я действительно закрываю этот fd, чтобы убедиться 😀 перед исключением).

И это приводило к тому, что fd 0 всегда был закрыт после его захвата.

Это была моя ошибка.

Ответ или решение

Проблема, с которой вы столкнулись, связана с неправильной обработкой дескрипторов файлов в вашем коде. Как вы сами выяснили, закрытие дескриптора 0 (stdin) приводит к тому, что новые входящие сокеты могут перетаскивать этот дескриптор и вы не получаете ожидаемого поведения.

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

1. Правильное управление дескрипторами файлов

Дескрипторы файлов (FD) – это абстракции, используемые для доступа к ресурсам системы, таким как сокеты, файлы и устройства. Ваша программа должна учитывать, что FD 0, 1 и 2 зарезервированы для stdin, stdout и stderr соответственно. Закрытие FD 0 означает, что новые входящие сокеты могут занимать этот дескриптор, что вызывает непредсказуемое поведение.

Рекомендации:

  • Не закрывайте FD 0, если это не предусмотрено вашей логикой.
  • Используйте более высокие дескрипторы для соединений, чтобы избежать подобных конфликтов.

2. Обработка ошибок

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

3. Настройки лимитов

Проверьте лимиты системы, такие как максимальное количество открытых файлов. Используйте команду ulimit -n, чтобы просмотреть текущий лимит открытых файлов и, при необходимости, установите более высокий лимит в файле конфигурации /etc/security/limits.conf.

# Пример для увеличения лимита открытых файлов
* soft nofile 4096
* hard nofile 8192

После внесения изменений перезагрузите систему или заново войдите в сессию, чтобы изменения вступили в силу.

4. Мониторинг и диагностика

  • Используйте lsof, ss или netstat для мониторинга открытых соединений и их состояния. Например, команда lsof -i :<port> поможет вам увидеть все подключения к определенному порту.
  • Регулярно проверяйте системные логи с помощью команд journalctl -xe или dmesg, чтобы выявить возможные проблемы, связанные с сетевыми соединениями.

5. Чтение и изучение

Для более глубокого понимания настройки TCP-сокетов в Linux, рассмотрите следующие материалы:

Заключение

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

Оцените материал
Добавить комментарий

Капча загружается...