Что такое сокет: TCP-, UDP- и unix-сокеты простыми словами, и почему сайт падает с «Too many open files»
11 мин чтения
Что такое сокет: TCP-, UDP- и unix-сокеты простыми словами, и почему сайт падает с «Too many open files»

Что такое сокет: TCP-, UDP- и unix-сокеты простыми словами, и почему сайт падает с «Too many open files»

Слово «сокет» встречается в ошибках nginx, конфиге PHP-FPM и выводе ss, и каждый раз означает чуть разное. Разбираем, что такое сокет для операционной системы, чем он отличается от порта и соединения, зачем nginx и PHP-FPM общаются через файл в /run и почему под нагрузкой сервер отдаёт 502 с записью «Too many open files» в логе.

Сокет — это конечная точка обмена данными между программами, которую операционная система выдаёт процессу как файловый дескриптор. Сетевой сокет задаётся тройкой «протокол + IP-адрес + порт» (например, TCP 203.0.113.10:443), unix-сокет — путём в файловой системе (например, /run/php/php8.3-fpm.sock). Соединение — пара сокетов на двух концах, порт — только номер внутри адреса. Поскольку каждый сокет занимает дескриптор, при их исчерпании сервер получает ошибку EMFILE — «Too many open files» — и перестаёт принимать подключения.

Чем сокет отличается от порта, соединения и файлового дескриптора

Порт — число от 0 до 65535 в заголовке TCP или UDP; сокет — объект ядра, к которому порт привязан вместе с адресом и протоколом; соединение — пара сокетов «клиент — сервер». Так определяет их спецификация TCP: RFC 9293 (действующая редакция, заменившая RFC 793) называет сокетом конкатенацию интернет-адреса и порта, а соединением — пару сокетов.

Термин Что это Пример Чем отличается
Порт 16-битный номер в заголовке TCP/UDP 443 Адресная метка, сам ничего не «слушает»
Сокет Объект ядра с адресом, портом, протоколом и буферами TCP 203.0.113.10:443 На одном порту — один слушающий сокет и тысячи принятых
Соединение Пара сокетов: IP и порт с обеих сторон 198.51.100.7:51234 ↔ 203.0.113.10:443 Уникально четвёркой, поэтому на одном порту помещаются миллионы соединений
Unix-сокет Сокет AF_UNIX для процессов одной машины /run/php/php8.3-fpm.sock Нет IP и порта, адрес — путь; доступ через права на файл
WebSocket Протокол прикладного уровня поверх TCP (RFC 6455) wss://example.com/chat Не сокет ОС, а протокол внутри обычного TCP-сокета
Файловый дескриптор Номер, которым процесс ссылается на открытый файл, сокет или пайп fd 7 → socket:[184532] Сокет — один из видов дескриптора; ulimit -n считает их вместе

Подробнее о портах — в статье «Что такое порт».

Какие бывают сокеты: SOCK_STREAM, SOCK_DGRAM, unix и raw

Тип сокета задают два параметра вызова socket(): семейство адресов (AF_INET, AF_INET6, AF_UNIX) и тип передачи (SOCK_STREAM, SOCK_DGRAM, SOCK_RAW). По man 2 socket, вызов «создаёт конечную точку для обмена данными и возвращает файловый дескриптор, который на неё ссылается».

Тип Семейство + тип Свойства Где используется Пример адреса
TCP-сокет AF_INET / AF_INET6 + SOCK_STREAM Поток байтов, соединение, гарантия доставки и порядка HTTP, HTTPS, SSH, SMTP, MySQL по сети 0.0.0.0:443, [::]:22
UDP-сокет AF_INET / AF_INET6 + SOCK_DGRAM Отдельные датаграммы, без соединения и гарантий DNS, NTP, QUIC/HTTP/3, VoIP, WireGuard 0.0.0.0:53, 0.0.0.0:443 (QUIC)
Unix stream AF_UNIX + SOCK_STREAM Как TCP, но внутри одной машины, без сетевого стека nginx ↔ PHP-FPM, MySQL, Redis, Docker /run/php/php8.3-fpm.sock
Unix datagram AF_UNIX + SOCK_DGRAM Сообщения с границами, доставка надёжная (man 7 unix) syslog, journald /dev/log
Raw-сокет AF_INET + SOCK_RAW, AF_PACKET Прямой доступ к пакетам и кадрам, нужны привилегии ping (ICMP), traceroute, tcpdump, nmap Порта нет: протокол ICMP, интерфейс

Проверка порта открывает TCP-сокет, а ping работает через raw-сокет и портов не касается. Разница между TCP и UDP — в статье «TCP и UDP: в чём разница».

Как живёт TCP-соединение: socket, bind, listen, accept и connect

Сервер делает четыре вызова — socket(), bind(), listen(), accept(); клиент два — socket() и connect(). Слушающий сокет данных не передаёт: на каждое соединение accept() возвращает новый сокет и новый дескриптор.

Шаг Кто Что происходит Состояние в ss Типичная ошибка
socket() Оба Ядро создаёт объект и выдаёт дескриптор UNCONN (не показывается без -a) EMFILE — Too many open files
bind() Сервер Сокету назначаются адрес и порт (или путь для unix) UNCONN с локальным адресом EADDRINUSE — Address already in use
listen() Сервер Сокет становится слушающим, создаётся очередь backlog LISTEN, Send-Q = размер backlog Переполнение очереди: клиент ждёт или получает RST
connect() Клиент Ядро выбирает эфемерный порт, идёт рукопожатие SYN-SENT → ESTAB ECONNREFUSED, ETIMEDOUT, EADDRNOTAVAIL (порты кончились)
accept() Сервер Готовое соединение забирается из очереди, выдаётся новый дескриптор ESTAB с адресом клиента EMFILE — соединение висит в очереди
close() Оба Обмен FIN CLOSE-WAIT, TIME-WAIT Много CLOSE_WAIT — приложение не вызывает close()
# Слушающие TCP-сокеты: Send-Q у LISTEN — размер backlog
$ ss -ltn
# пример вывода
State   Recv-Q  Send-Q  Local Address:Port  Peer Address:Port
LISTEN  0       511           0.0.0.0:80         0.0.0.0:*
LISTEN  0       511           0.0.0.0:443        0.0.0.0:*
LISTEN  0       511         127.0.0.1:6379       0.0.0.0:*

# Слушающие unix-сокеты
$ ss -lxn | grep php
# пример вывода
u_str LISTEN 0 511 /run/php/php8.3-fpm.sock 21873 * 0

# Сводка: сколько соединений в TIME-WAIT
$ ss -s
# пример вывода
TCP:   3412 (estab 1189, closed 2106, orphaned 3, timewait 2094)

Recv-Q у LISTEN, подбирающийся к Send-Q, — переполнение очереди accept. Разбор состояний и ключей ss — в статье «netstat и ss: какие порты слушает сервер».

Unix-сокеты в веб-стеке: nginx, PHP-FPM, MySQL и Redis

Когда nginx и PHP-FPM работают на одной машине, они обычно общаются через unix-сокет — файл в /run, а не через TCP-порт 9000. Unix-сокет не проходит через сетевой стек: нет заголовков IP и TCP, TIME_WAIT и эфемерных портов, а доступ контролируется правами на файл. Документация PHP рекомендует unix-сокет для веб-сервера на том же хосте: открытый наружу TCP-порт FastCGI позволяет выполнить произвольный код.

# /etc/php/8.3/fpm/pool.d/www.conf — PHP-FPM слушает unix-сокет
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
listen.backlog = 511
pm.max_children = 20

# /etc/nginx/sites-enabled/example.conf — nginx подключается к нему
location ~ \.php$ {
    include fastcgi_params;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}

По man 7 unix, для подключения к stream-сокету нужно право записи на файл сокета. Поэтому три самые частые ошибки в error.log nginx — про файл, а не про сеть:

# /var/log/nginx/error.log — примеры записей
# 1. PHP-FPM не запущен или слушает другой путь
[crit] 1832#1832: *44 connect() to unix:/run/php/php8.3-fpm.sock
  failed (2: No such file or directory) while connecting to upstream
# 2. nginx работает не от пользователя из listen.owner
[crit] 1832#1832: *45 connect() to unix:/run/php/php8.3-fpm.sock
  failed (13: Permission denied) while connecting to upstream
# 3. Все воркеры заняты, очередь backlog переполнена
[error] 1832#1832: *46 connect() to unix:/run/php/php8.3-fpm.sock
  failed (11: Resource temporarily unavailable) while connecting
  to upstream
# В /var/log/php8.3-fpm.log в это же время:
WARNING: [pool www] server reached pm.max_children setting (20),
  consider raising it

Ошибка 11 (EAGAIN) — самая коварная: сокет есть, права в порядке, но все воркеры заняты, очередь listen.backlog (по умолчанию 511 на Linux) полна, и ядро отказывает nginx мгновенно. Снаружи это 502. Лечится не переходом на TCP, а увеличением pm.max_children в пределах памяти и ускорением скриптов.

Критерий Unix-сокет (fastcgi_pass unix:) TCP (fastcgi_pass 127.0.0.1:9000)
Накладные расходы Нет сетевого стека и рукопожатия — ниже задержка подключения Полный TCP даже через loopback; на фоне работы PHP разница мала
Контроль доступа Права на файл: listen.owner, listen.group, listen.mode listen.allowed_clients и файрвол
Эфемерные порты и TIME_WAIT Не расходуются Каждый запрос без keepalive занимает порт на 60 с в TIME_WAIT
Сеть и контейнеры Одна машина; в Docker — общий volume с сокетом Работает между хостами и контейнерами
Типичные ошибки 2: No such file · 13: Permission denied · 11: Resource temporarily unavailable 111: Connection refused · 99: Cannot assign requested address

Почему сервер пишет «Too many open files» и как поднять лимит

«Too many open files» (errno 24, EMFILE) означает, что процесс упёрся в RLIMIT_NOFILE — лимит одновременно открытых дескрипторов, а каждое соединение это дескриптор. Soft-лимит по умолчанию — 1024: systemd с версии 240 поднимает hard-лимит до 524288, но soft оставляет 1024 ради совместимости с вызовом select(), который не работает с дескрипторами ≥ 1024. Для nginx 1024 дескрипторов — примерно 500 клиентов: на каждый проксируемый запрос уходит два сокета, к клиенту и к бэкенду.

# Как это выглядит в /var/log/nginx/error.log
[alert] 28720#0: *59757 socket() failed (24: Too many open files)
  while connecting to upstream
[alert] 28720#0: *59801 accept4() failed (24: Too many open files)
[alert] 28720#0: 512 worker_connections are not enough

# Лимит оболочки (soft / hard) и реальный лимит процесса
$ ulimit -n; ulimit -Hn
1024
524288
$ cat /proc/$(pgrep -o nginx)/limits | grep 'open files'
Max open files   1024   524288   files        # пример вывода

# Дескрипторов у воркера сейчас
$ ls /proc/$(pgrep -n nginx)/fd | wc -l
1019

Поднимать лимит нужно на том слое, где он срабатывает. Правка /etc/security/limits.conf не влияет на сервисы systemd — они берут LimitNOFILE из юнита. У nginx есть своя директива worker_rlimit_nofile; worker_connections (по умолчанию 512) должен быть меньше неё.

# 1. systemd: лимит процесса
$ sudo systemctl edit nginx
[Service]
LimitNOFILE=65536
$ sudo systemctl daemon-reload && sudo systemctl restart nginx

# 2. nginx.conf: лимит воркера и число соединений
worker_rlimit_nofile 65536;
events {
    worker_connections 16384;
}

# Проверка
$ cat /proc/$(pgrep -n nginx)/limits | grep 'open files'
Max open files   65536   65536   files        # пример вывода
Слой Параметр Значение по умолчанию Как посмотреть Ошибка при упоре
Процесс RLIMIT_NOFILE: ulimit -n, LimitNOFILE soft 1024, hard 524288 (systemd ≥ 240) cat /proc/PID/limits EMFILE, 24: Too many open files
Система fs.file-max Зависит от памяти, обычно очень велик cat /proc/sys/fs/file-nr ENFILE, 23: Too many open files in system
nginx worker_rlimit_nofile, worker_connections не задан; 512 nginx -T | grep worker_ «worker_connections are not enough», 502/504
Очередь listen somaxconn; backlog в listen nginx; listen.backlog PHP-FPM 4096 с ядра 5.4 (ранее 128); 511; 511 ss -ltn (Send-Q), nstat | grep ListenOverflows Клиент ждёт или получает RST; 11: Resource temporarily unavailable
PHP-FPM pm.max_children 5 в шаблоне пула Debian php-fpm.log, pm.status_path «server reached pm.max_children setting», 502/504
Лимит — не всегда причина, иногда симптом утечки

Если число файлов в /proc/PID/fd растёт при ровной нагрузке, а в ss -tan копятся CLOSE-WAIT, приложение не закрывает сокеты — поднятый лимит лишь отодвинет падение на несколько часов.

Почему при большой нагрузке кончаются эфемерные порты

Каждое исходящее соединение занимает эфемерный порт из диапазона net.ipv4.ip_local_port_range, по умолчанию 32768–60999 — 28 232 порта на пару «адрес источника — адрес и порт назначения». Закрытое соединение ещё 60 секунд держит порт в TIME_WAIT (константа TCP_TIMEWAIT_LEN в ядре Linux, tcp_fin_timeout на неё не влияет), поэтому к одному бэкенду без keepalive можно открывать не больше ~470 соединений в секунду: 28 232 / 60. Дальше connect() возвращает EADDRNOTAVAIL — «Cannot assign requested address» (errno 99).

Параметр По умолчанию (Linux, сентябрь 2026) Что делает Что менять
net.ipv4.ip_local_port_range 32768 60999 Диапазон портов для исходящих TCP и UDP Расширить до 1024 65535, если на этих портах нет сервисов
TIME_WAIT 60 с, не настраивается Закрывшая первой сторона держит четвёрку, чтобы опоздавшие сегменты не попали в новое соединение Не бороться, а сокращать число коротких соединений: keepalive к upstream
net.ipv4.tcp_tw_reuse 2 — только для loopback Разрешает connect() занять порт в TIME_WAIT через 1 с при включённых TCP timestamps 1 на клиентах и прокси с большим числом исходящих соединений
net.ipv4.tcp_tw_recycle Удалён в ядре 4.12 Ломал клиентов за NAT Убрать из старых sysctl.conf
# Текущие значения
$ sysctl net.ipv4.ip_local_port_range net.ipv4.tcp_tw_reuse
net.ipv4.ip_local_port_range = 32768   60999
net.ipv4.tcp_tw_reuse = 2

# К какому адресу назначения скопились TIME-WAIT
$ ss -tan state time-wait | awk 'NR>1 {print $4}' | sort \
    | uniq -c | sort -rn | head -3
# пример вывода
  18342 127.0.0.1:9000
   2071 10.0.0.5:3306
    514 10.0.0.7:6379

# Лечение в nginx: keepalive к бэкенду
upstream backend {
    server 127.0.0.1:9000;
    keepalive 32;
}

18 тысяч TIME-WAIT к 127.0.0.1:9000 — nginx открывает новое TCP-соединение к PHP-FPM на каждый запрос. Unix-сокет убирает проблему полностью, keepalive к upstream — почти полностью. На Windows динамический диапазон — 49152–65535 (netsh int ipv4 show dynamicport tcp), а исчерпание видно как WSAENOBUFS 10055 или WSAEADDRINUSE 10048 из Winsock — реализации того же API Berkeley sockets с приставкой WSA у кодов ошибок.

Как исчерпание сокетов выглядит снаружи и как его поймать

Снаружи ни одна из этих проблем не называется своим именем: посетитель видит 502 Bad Gateway, 504 Gateway Timeout или бесконечную загрузку, а при переполненном backlog — SYN без ответа. Разовый curl почти всегда проходит: лимиты срабатывают на пиках, через минуту TIME_WAIT рассасывается и всё «само чинится».

Такие сбои ловят регулярными проверками. HTTP-проверка запишет момент, когда сервер начал отдавать 502 и 504, а проверка TCP-порта отделит «порт не отвечает» (упал nginx, переполнен backlog) от «порт открыт, но приложение отдаёт ошибку» (PHP-FPM, лимит дескрипторов). Сопоставьте время инцидента с error.log — и станет видно, какой лимит сработал.

Пик нагрузки не пройдёт незамеченным

Пруфен проверяет сайт и порты с интервалом от 2 минут из 20 точек мониторинга — 17 в России, а также Минск, Астана и Испания. Как только проверка перестаёт проходить, приходит уведомление в Email, Telegram или Макс, и второе — когда сайт восстановился; если ресурс так и не поднялся, подключается голосовой звонок. Время обнаружения равно интервалу проверки. Бесплатный план включает 5 проверок — хватит на главную, API и порт базы данных.

Частые вопросы

Сокет и порт — одно и то же?

Нет. Порт — номер от 0 до 65535, часть адреса. Сокет — объект операционной системы с адресом, портом и протоколом, через который процесс читает и пишет данные. На порту 443 у сервера один слушающий сокет и по одному на каждое соединение; «занят ли порт» означает «есть ли на нём слушающий сокет».

Что быстрее для PHP-FPM — TCP или unix-сокет?

Unix-сокет: нет сетевого стека, рукопожатия и эфемерных портов. На одном запросе разница на фоне работы PHP невелика, но при тысячах запросов в секунду TCP через loopback упирается в TIME_WAIT, а unix — нет. TCP нужен, когда nginx и PHP-FPM живут на разных машинах или в контейнерах без общего volume.

Почему при большой нагрузке кончаются порты, если их 65 535?

Исходящим соединениям Linux по умолчанию выдаёт только 32768–60999, и каждое закрытое соединение держит порт 60 секунд в TIME_WAIT: к одному адресу без keepalive выходит около 470 новых соединений в секунду. Лечение — keepalive к бэкендам, unix-сокеты внутри машины, расширение ip_local_port_range и tcp_tw_reuse = 1 на прокси.

WebSocket — это сокет?

Нет, это протокол прикладного уровня (RFC 6455): начинается как HTTP-запрос с заголовком Upgrade и дальше использует то же TCP-соединение для двустороннего обмена. Для операционной системы это обычный TCP-сокет с теми же лимитами дескрипторов: 10 000 подключённых браузеров — 10 000 сокетов у процесса.

Была ли статья полезна?

Начните мониторинг прямо сейчас

Бесплатный тариф — до 5 проверок. 150 ₽ на баланс при регистрации.

Создать аккаунт