netstat и ss: какие порты слушает сервер и что означают состояния TCP
7 мин чтения
netstat и ss: какие порты слушает сервер и что означают состояния TCP

netstat и ss: какие порты слушает сервер и что означают состояния TCP

Снаружи порт закрыт, изнутри сервис вроде запущен. Или наоборот: сайт отвечает, но раз в час намертво зависает на минуту. Обе ситуации разбираются одной командой — если знать, куда смотреть. Разбираем ss и netstat: ключи, фильтры, одиннадцать состояний TCP и две колонки, которые видит только ss.

ss и netstat — утилиты, которые показывают сокеты операционной системы: какие порты она слушает, с кем установлены соединения и в каком те состоянии. Это взгляд изнутри сервера, в отличие от сканирования снаружи: ss видит и процесс-владелец сокета, и очереди, которых снаружи не видно вовсе.

Почему netstat может не найтись на сервере

netstat входит в пакет net-tools, последний релиз которого — 2.10 от января 2021 года. За пять с лишним лет ни одного обновления: пакет живёт только на патчах дистрибутивов. На смену пришёл iproute2, который активно развивается.

Старая команда Замена Что показывает
netstat ss Сокеты
netstat -r ip route Таблица маршрутов
netstat -i ip -s link Статистика по интерфейсам
netstat -g ip maddr Multicast-группы
netstat -s nstat Счётчики протоколов, точнее
ifconfig ip addr Адреса интерфейсов
route ip route Маршруты
arp ip neigh ARP-таблица

Где netstat уже не стоит по умолчанию: RHEL начиная с седьмой версии, Debian и Ubuntu (пакет с приоритетом optional против important у iproute2), Fedora, Arch, Alpine и все контейнерные образы. В докер-контейнере вы почти гарантированно не найдёте ни того, ни другого — ставить придётся руками.

Главная команда, которую нужно помнить

sudo ss -tulpn      # что слушает сервер: TCP, UDP, процессы, без резолва
sudo netstat -tulpn # то же на старых системах

Мнемоника одинаковая: tcp, udp, listening, process, numeric. Без sudo колонка с процессами будет пустой для чужих сокетов — ядро не покажет владельца непривилегированному пользователю.

Netid State  Recv-Q Send-Q Local Address:Port  Peer Address:Port Process
tcp   LISTEN 0      4096         0.0.0.0:80         0.0.0.0:*     users:(("nginx",pid=812,fd=6))
tcp   LISTEN 0      511        127.0.0.1:9000       0.0.0.0:*     users:(("php-fpm",pid=901,fd=8))
tcp   LISTEN 0      70         127.0.0.1:33060      0.0.0.0:*     users:(("mysqld",pid=744,fd=21))
udp   UNCONN 0      0      127.0.0.53%lo:53         0.0.0.0:*     users:(("systemd-resolve",pid=611,fd=13))
Читаем адрес, а не только порт

0.0.0.0:80 — сервис слушает на всех интерфейсах, доступен снаружи. 127.0.0.1:9000 — только с самой машины: снаружи такой порт будет выглядеть закрытым, и это чаще всего правильно. Самая частая причина «порт закрыт, хотя сервис запущен» — именно привязка к localhost вместо внешнего адреса.

Ключи ss

Ключ Что делает
-t / -u TCP / UDP
-l Только слушающие. Без него они скрыты
-a Все: и слушающие, и установленные
-n Не резолвить имена — быстрее и понятнее
-p Показать процессы-владельцы
-i Внутренности TCP: rtt, cwnd, retrans, алгоритм контроля перегрузки
-m Память сокета — ищем утечки буферов
-e Расширенно: uid, inode, cookie
-o Таймеры: keepalive, timewait, persist
-s Сводка по счётчикам, без перечисления сокетов
-4 / -6 Только IPv4 / только IPv6
-x Unix-сокеты
-H Без строки заголовка. Обязательно для скриптов
-K Принудительно закрыть подходящие под фильтр сокеты
-N имя Заглянуть в сетевой namespace контейнера

Фильтры ss: главное преимущество перед netstat

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

# По состоянию
ss -t state established
ss -t state time-wait
ss -t state close-wait          # диагностика утечки дескрипторов
ss -t state syn-sent            # висящие исходящие соединения

# По адресу и подсети
ss -t dst 10.0.0.5
ss -t dst 10.0.0.0/8
ss -t dst 10.0.0.5:443
ss -t src 192.168.1.10

# По портам
ss -t sport = :22
ss -tn 'sport > :1024'
ss -t '( dport = :443 or sport = :443 )'

# Комбинация
ss -tn state established '( dport = :80 or dport = :443 )' dst 10.0.0.0/8

# Топ IP-адресов, с которых идут соединения
ss -tn state established -H | awk '{print $5}' | cut -d: -f1 \
  | sort | uniq -c | sort -rn | head -20

# Сводка по состояниям одной командой
ss -tan | awk 'NR>1{c[$1]++} END{for (s in c) print c[s], s}' | sort -rn

# Аварийно оборвать соединения с конкретным адресом
sudo ss -K dst 10.0.0.5 dport = :443

Почему ss быстрее: netstat читает и разбирает текстовые файлы /proc/net/tcp, которые ядро генерирует построчно, а с ключом -p ещё и обходит все дескрипторы всех процессов, чтобы сопоставить inode сокета с владельцем. ss использует netlink-интерфейс sock_diag: двоичный формат, запрос только нужных полей и фильтрация на стороне ядра.

Одиннадцать состояний TCP и что означает их скопление

Состояние Смысл Если их много
LISTEN Сокет ждёт входящих Норма. Тревожно, когда ожидаемого LISTEN нет
SYN-SENT Мы послали SYN, ответа нет Исходящие не устанавливаются: файрвол дропает, внешняя зависимость лежит
SYN-RECV Пришёл SYN, финального ACK нет SYN-флуд или асимметричная маршрутизация
ESTAB Рабочее соединение Норма. Много без трафика — нет таймаутов или утечка
FIN-WAIT-1 Мы послали FIN, ACK не пришёл Пир молчит или связь оборвалась
FIN-WAIT-2 Ждём FIN от пира Пир не закрывает свою сторону. Таймер 60 секунд
CLOSE-WAIT Пир закрылся, наше приложение — нет Всегда баг приложения. Таймаута нет
LAST-ACK Ждём финальный ACK Обычно единицы. Накопление — пир исчез
CLOSING Одновременное закрытие с обеих сторон Редкость, массово не встречается
TIME-WAIT Закрывший активно держит четвёрку 60 секунд Обычно норма, а не проблема
CLOSED Сокета нет В выводе практически не появляется
Гора CLOSE-WAIT — это утечка дескрипторов, и лечится она только в коде

У состояния CLOSE-WAIT нет таймаута: оно живёт, пока процесс не закроет дескриптор или не умрёт. Причины всегда одинаковые — незакрытые соединения в пуле, забытый close, необработанное исключение, зависший воркер. Финал предсказуем: «too many open files» и отказ сервиса. Никакой sysctl это не чинит. Диагностика: ss -tanp state close-wait, затем ls /proc/PID/fd | wc -l против cat /proc/PID/limits.

Про TIME-WAIT отдельно, потому что вокруг него много устаревших советов. В Linux длительность жёстко зашита в ядро — 60 секунд, и sysctl её не меняет. Десятки тысяч таких сокетов на busy-прокси — ожидаемая картина, а не авария. Реальная проблема возникает только при исчерпании эфемерных портов к одному и тому же адресу с портом; симптом — ошибка EADDRNOTAVAIL. Лечится это пулом соединений и keep-alive, расширением net.ipv4.ip_local_port_range и параметром tcp_tw_reuse=1. А вот tcp_tw_recycle, который до сих пор советуют в старых статьях, удалён из ядра ещё в версии 4.12 — он ломал клиентов за NAT.

Recv-Q и Send-Q: две колонки, которые читают неправильно

У этих колонок разный смысл для слушающего и для установленного сокета. Путаница здесь стоит часов диагностики.

Состояние Recv-Q Send-Q
LISTEN Сколько установленных соединений ждут accept Максимальный размер этой очереди
ESTABLISHED Байты приняты ядром, но не прочитаны приложением Байты отданы в сокет, но не подтверждены пиром
ss -ltn

State  Recv-Q Send-Q  Local Address:Port
LISTEN 0      4096          0.0.0.0:80      <- очередь пуста, всё хорошо
LISTEN 129    128           0.0.0.0:8080    <- очередь переполнена, соединения теряются
Что видим Диагноз Что делать
LISTEN, Recv-Q ползёт к Send-Q Приложение не успевает принимать соединения Больше воркеров, поднять backlog и somaxconn
LISTEN, Send-Q = 128 при somaxconn 4096 Приложение само попросило маленькую очередь Править конфиг сервиса, sysctl не поможет
ESTAB, большой Recv-Q Приложение не читает из сокета Искать залипший поток или медленного потребителя
ESTAB, большой Send-Q Сеть или пир не принимают Смотреть ss -tin: retrans, cwnd, rtt
Переполнение очереди видно только в ss

netstat для слушающего сокета печатает в Send-Q бессмысленный ноль и никогда не показывает максимальный размер очереди. Проверить, не теряются ли соединения на входе, можно только через ss -ltn. Подтвердить догадку помогут счётчики: nstat -az | grep -E 'ListenOverflows|ListenDrops'.

Разбор трёх типовых ситуаций

Порт снаружи закрыт, сервис запущен. Смотрим адрес привязки: если 127.0.0.1:8080, сервис принципиально недоступен снаружи, и дело не в файрволе. Если 0.0.0.0:8080 — тогда уже файрвол или группа безопасности у хостера.

Сайт зависает под нагрузкой. Смотрим ss -ltn: если Recv-Q растёт — приложению не хватает воркеров, соединения ждут в очереди и отваливаются по таймауту у клиента, при этом в логах веб-сервера может не быть вообще ничего.

Сервис умирает раз в неделю. Смотрим ss -tan state close-wait | wc -l. Если счёт идёт на сотни и растёт день ото дня — утечка дескрипторов, и падение случится, когда упрётся в лимит.

Что делать на Windows и macOS

Утилиты ss там нет и не будет: она опирается на netlink, которого нет за пределами Linux.

# Windows
netstat -ano                     # все сокеты с PID
netstat -ano | findstr :443
tasklist /FI "PID eq 1234"       # сопоставить PID с процессом

# PowerShell, объектный вариант
Get-NetTCPConnection -State Listen |
  Select LocalAddress,LocalPort,OwningProcess,
         @{n='Process';e={(Get-Process -Id $_.OwningProcess).ProcessName}}

# macOS: netstat есть, но BSD-версии, PID не показывает
sudo lsof -i -P -n               # реальный аналог ss -tulpn
sudo lsof -i :443
netstat -an                      # -p здесь означает протокол, а не процесс

Взгляд изнутри не заменяет взгляда снаружи

ss показывает, что сокет открыт и слушает. Он не показывает, доходит ли до него трафик из интернета: между вашим сервисом и пользователем могут стоять файрвол, группа безопасности хостера, NAT и провайдерская фильтрация. Порт может слушать идеально и быть недоступен всей стране.

Проверить это можно только снаружи, причём из нескольких мест: проверка TCP-порта устанавливает соединение с указанным портом с внешних точек и показывает, где оно проходит, а где нет. А ICMP-проверка отвечает на предыдущий вопрос — виден ли вообще сам хост.

Слушает — не значит доступен

Пруфен проверяет доступность портов и хостов с интервалом от 2 минут из 13 точек мониторинга и присылает уведомление в Email, Telegram или Макс, когда порт перестал принимать соединения; если он не поднимается, подключается голосовой звонок. Результат виден по каждой локации отдельно — сразу понятно, это сервер лёг или закрылся маршрут из одного региона.

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

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

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

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