Почему ss linux вытеснил netstat
Раньше все админы жили на netstat. Проблема в том, что netstat читает сетевое состояние из текстовых файлов в /proc (вроде /proc/net/tcp), парсит их построчно, и на нагруженном сервере с десятками тысяч соединений это работает медленно, подтормаживает и даёт неполную картину. Команда ss обращается к ядру напрямую через специальный сокет-интерфейс netlink (семейство NETLINK_SOCK_DIAG) и выдаёт ту же информацию в разы быстрее, плюс умеет фильтровать на стороне ядра. Поэтому правило простое: на современном Linux используй ss, а netstat держи в голове как запасной вариант для старых систем, где iproute2 может оказаться без свежих фич, или где вообще стоит только net-tools.
Чтобы понимать вывод, нужно знать про сокеты linux одну базовую вещь. Сокет - это конечная точка сетевого соединения, "розетка", в которую воткнут процесс. Слушающий (LISTEN) сокет сидит на каком-то порту и ждёт входящих. Установленное (ESTABLISHED) соединение - это уже воткнутый кабель между двумя розетками: твоим адресом-портом и адресом-портом на той стороне. Вся диагностика крутится вокруг двух вопросов: кто слушает порт и кто куда подключён. Третий, более тонкий вопрос - в каком состоянии соединения и не копится ли что-то нездоровое.

Базовые команды ss: кто слушает порт
Самая частая задача - узнать, кто слушает порт. Команда ss linux решает её так:
Код: Выделить всё
sudo ss -tlnpКод: Выделить всё
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=812,fd=6))
LISTEN 0 4096 [::]:80 [::]:* users:(("nginx",pid=812,fd=8))
LISTEN 0 128 127.0.0.1:5432 0.0.0.0:* users:(("postgres",pid=940,fd=5))Теперь все TCP-соединения с разбивкой по состояниям:
Код: Выделить всё
ss -tanКод: Выделить всё
ss -sКод: Выделить всё
Total: 1843
TCP: 1722 (estab 210, closed 1450, orphaned 0, timewait 1448)
Transport Total IP IPv6
RAW 0 0 0
UDP 12 8 4
TCP 272 250 22
INET 284 258 26Состояния TCP linux: что норма, а что тревога
Соединение TCP проходит через несколько состояний, как через шлагбаумы. Знать tcp состояния linux важно: по ним диагностируют половину сетевых проблем. Полный список ss такой: established, syn-sent, syn-recv, fin-wait-1, fin-wait-2, time-wait, closed, close-wait, last-ack, listening, closing. На практике чаще всего смотришь на эти:
- LISTEN - сокет ждёт входящих. Норма для серверного процесса.
- ESTAB (ESTABLISHED) - соединение установлено, данные ходят. Норма, рабочее состояние.
- SYN-SENT / SYN-RECV - идёт рукопожатие. Если их много и они залипают - проблема на установке соединения (фаервол молча режет, потери пакетов, SYN-флуд).
- TIME-WAIT - сторона, которая закрыла соединение первой, ждёт примерно 60 секунд (фиксированный в ядре 2*MSL), чтобы добить хвосты пакетов и не перепутать старое соединение с новым на том же порту. Это нормальная часть протокола. Тысячи TIME-WAIT на сервере, который делает много коротких исходящих запросов (например, к API или к базе по новому соединению на каждый запрос), - не баг сам по себе, но намёк, что стоит включить keep-alive или пул соединений.
- FIN-WAIT-1 / FIN-WAIT-2 / LAST-ACK / CLOSING - промежуточные стадии закрытия. Сами по себе мелькают, но если массово залипают - сеть теряет пакеты закрытия.
- CLOSE-WAIT - вот это почти всегда баг приложения. Оно означает: вторая сторона уже закрыла соединение (прислала FIN), ядро это подтвердило, но твоё приложение так и не вызвало close() на своём сокете. Соединение висит мёртвым грузом. Если CLOSE-WAIT копится и не уходит - в коде где-то забыли закрыть сокет, и ты медленно течёшь по файловым дескрипторам, пока не упрёшься в лимит (потом получишь "Too many open files").
Отфильтровать по состоянию очень удобно прямо через ss:
Код: Выделить всё
ss -tan state close-wait
ss -tan state time-wait
ss -tan state established '( dport = :443 or sport = :443 )'Код: Выделить всё
watch -n 2 'ss -Htan state close-wait | wc -l'Recv-Q и Send-Q: где затык
Две колонки, которые новички пролистывают, а зря - именно они показывают насыщение. И читаются они совершенно по-разному для слушающих и для установленных сокетов, это ключевой нюанс.
Для ESTABLISHED: Recv-Q - сколько байт пришло в приёмный буфер ядра, но приложение ещё не прочитало (не успело забрать через read/recv). Send-Q - сколько байт мы отправили, но другая сторона ещё не подтвердила (ACK). Если Recv-Q стабильно большой - приложение не успевает читать данные, тормозит обработка (упёрлись в CPU, в блокировку, в медленную бизнес-логику). Если большой Send-Q - данные не уходят: затык в сети, переполнено окно получателя или он не подтверждает.
Для LISTEN эти колонки значат совсем другое, и это супер-полезно. Recv-Q - сколько уже полностью установленных соединений лежит в accept-очереди и ждёт, пока приложение вызовет accept(). Send-Q - максимальный размер этой accept-очереди (тот самый backlog, равный минимуму из аргумента listen() в приложении и ядерного net.core.somaxconn). Смотри вывод выше: у nginx Send-Q равен 4096 - это лимит очереди. Важно: это именно accept-очередь готовых соединений, а не SYN-очередь полуоткрытых (та отдельная и регулируется net.ipv4.tcp_max_syn_backlog).
Что происходит при переполнении: если в Recv-Q у слушающего сокета число близко к Send-Q или равно ему - приложение не успевает вызывать accept(), очередь забита. Ядро тогда либо отбрасывает завершающий ACK от клиента (и клиент уходит в ретрансмит), либо роняет соединение. С клиента это выглядит как таймауты и подвисания "на ровном месте" при формально живом сервере. Счётчик таких отбросов виден в nstat -az TcpExtListenOverflows (или ListenDrops) - если он растёт, упираешься именно в backlog. Лечение: поднять somaxconn (на 2026 значение по умолчанию в свежих ядрах - 4096, на старых 128 и это часто мало), увеличить backlog в самом приложении и разобраться, почему оно медленно принимает соединения.
Фильтры можно комбинировать - по порту, адресу, состоянию:
Код: Выделить всё
ss -tn dport = :443
ss -tn sport = :80
ss -tn dst 10.0.0.5
ss -tn dst 10.0.0.0/24 dport = :5432netstat для совместимости и связка с другими инструментами
Зашёл на старый сервер, а ss ведёт себя странно или хочется привычного формата - тогда netstat linux выручает. Прямые аналоги:
Код: Выделить всё
netstat -tlnp # кто слушает (как ss -tlnp)
netstat -tan # все TCP (как ss -tan)
netstat -s # статистика протоколов (ss -s короче, nstat точнее)ss редко работает в одиночку. Типичные связки: lsof -i :PORT и fuser PORT/tcp - альтернативный способ узнать процесс на порту; ip (тоже iproute2) - для адресов и маршрутов; nstat и /proc/net/snmp - для счётчиков ретрансмитов и отбросов; ss -i вместе с tcpdump - когда надо понять, тормозит сеть или приложение. На 2026 для глубокой диагностики latency соединений в проде всё чаще берут eBPF-инструменты из пакета bcc и bpftrace (например tcpconnect, tcpretrans, tcplife, tcptracer) - они показывают события установки и закрытия соединений в реальном времени с PID и временем жизни, чего ss как срез "здесь и сейчас" дать не может. ss остаётся идеальным первым шагом, eBPF - следующим, когда нужен поток событий, а не моментальный снимок.
Грабли и заблуждения
- Запустил ss без sudo и удивляешься пустой колонке Process. Имена процессов чужих пользователей видны только под root.
- Перепутал sport и dport в фильтре и не понял, почему вывод пустой. sport - твой локальный порт, dport - удалённый.
- Решил, что TIME-WAIT - это утечка, и кинулся крутить ядерные параметры. Нет: TIME-WAIT уходит сам примерно за минуту, это норма протокола. И отдельно: НЕ включай net.ipv4.tcp_tw_recycle - этот параметр сломан для NAT и вообще удалён из ядра начиная с версии 4.12 (на любом современном Linux 2026 года его просто нет). Если реально упёрся в нехватку портов на клиенте, делающем тьму исходящих, правильные шаги - connection pooling и keep-alive, расширение net.ipv4.ip_local_port_range, и аккуратно tcp_tw_reuse (только для исходящих). Опасен растущий CLOSE-WAIT - вот его и лови.
- Забыл флаг n и ждёшь вывод секунды, потому что ss пытается резолвить каждый адрес и порт через DNS, а если DNS тупит - команда подвисает. На диагностике почти всегда ставь n.
- Думаешь, что Recv-Q у LISTEN и у ESTAB - это одно и то же. Нет: у LISTEN это accept-очередь и backlog, у ESTAB - непрочитанные и неподтверждённые байты. Перепутаешь смысл - сделаешь неверный вывод.
- Считаешь, что ss -s в строке TCP показывает все сокеты. timewait там может не входить в estab, а closed - это не "закрытые навсегда", а сокеты в процессе закрытия и orphaned. Для точных счётчиков протокола бери nstat.
- Выполни sudo ss -tlnp и найди, какой процесс слушает какой порт. Найди хотя бы один сокет на 127.0.0.1 (только локально) и один на 0.0.0.0 или [::] (наружу). Посмотри в колонку Send-Q - какой backlog у твоего веб-сервера?
- Запусти ss -s и посмотри на estab и timewait. Прикинь соотношение - если timewait в разы больше estab, у тебя много короткоживущих соединений.
- Открой в браузере любой сайт, тут же выполни ss -tnp dport = :443 - увидишь живые ESTABLISHED-соединения к нему с процессом-браузером.
- Сосчитай зависшие соединения: ss -Htan state close-wait | wc -l. В норме на свежей системе будет 0.
- Глянь внутренности живого соединения: ss -tin dport = :443 - найди rtt и cwnd, прикинь задержку до сервера без всякого ping.
- Если есть netstat, сравни netstat -tlnp и ss -tlnp - убедись, что данные те же.
- Какими флагами ss показать слушающие TCP-сокеты вместе с процессами, и зачем тут sudo?
- Чем CLOSE-WAIT принципиально отличается от TIME-WAIT и какое из них указывает на баг в приложении?
- Что означают Recv-Q и Send-Q у сокета в состоянии LISTEN, чем эта очередь отличается от SYN-очереди, и при каком соотношении сервер начнёт терять входящие соединения?
- Как через ss отфильтровать все соединения к удалённому порту 5432, и почему включать tcp_tw_recycle для борьбы с TIME-WAIT в 2026 - плохая идея?
ss - твой основной инструмент по сокетам, netstat - запасной для старых машин. ss -tlnp отвечает "кто слушает порт", ss -tan и ss -s дают общую картину по состояниям, ss -i заглядывает внутрь TCP. TIME-WAIT - норма и уходит сам; tcp_tw_recycle давно удалён из ядра, не трогай его. Растущий CLOSE-WAIT - баг приложения и утечка дескрипторов. Recv-Q/Send-Q у LISTEN - это accept-очередь готовых соединений и её лимит (backlog), а не SYN-очередь; их переполнение = тихая потеря соединений и таймауты у клиентов, смотри TcpExtListenOverflows. У ESTAB те же колонки значат непрочитанные и неподтверждённые байты. Всегда добавляй n, чтобы не висеть на DNS. А для потока событий "кто и когда соединялся" на 2026 переходи от снимка ss к eBPF-инструментам tcpconnect, tcplife и tcpretrans.