MAATRIX / Блог / Что ss и netstat на самом деле рассказывают о ваших соединениях

Что ss и netstat на самом деле рассказывают о ваших соединениях

MAATRIX

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

ss и netstat: чем отличаются и когда какой использовать

ss (пакет iproute2) и netstat (пакет net-tools) решают одну и ту же задачу — показать состояние сетевых соединений на сервере, — но устроены по-разному. netstat — более старый инструмент, который для сбора данных обходится более тяжёлым способом чтения информации о сокетах, и на нагруженном сервере с десятками тысяч соединений это заметно медленнее. ss читает те же данные из ядра через более эффективный интерфейс и на том же сервере отрабатывает кратно быстрее — разница особенно заметна, когда счётчик соединений идёт на тысячи.

Есть и более практичный момент: во многих современных дистрибутивах Linux пакет net-tools (а с ним netstat, ifconfig, route) не входит в базовую установку — его нужно ставить отдельно, тогда как ss обычно уже стоит вместе с iproute2, без которого не поднимется сеть. Поэтому на свежем сервере netstat иногда просто отсутствует, и первая же попытка его вызвать возвращает command not found.

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

ss -tan       # все TCP-соединения, числовые адреса, без резолва имён
ss -tln       # только слушающие TCP-порты (LISTEN)
ss -tanp      # то же самое плюс PID/имя процесса (нужен root)

netstat -tan  # аналог ss -tan
netstat -tln  # аналог ss -tln
netstat -tanp # аналог ss -tanp

-t ограничивает вывод TCP (без него обе команды покажут ещё и UDP-сокеты, где состояний в привычном смысле нет), -a показывает все сокеты, а не только установленные, -n отключает резолв имён хостов и портов в DNS//etc/services — без него команда может подвисать на медленном обратном DNS-резолвинге для каждого адреса в выводе, поэтому -n стоит ставить почти всегда по умолчанию. Дальше в статье используются флаги ss, но всё сказанное про состояния и логику диагностики один в один переносится на netstat.

Как читать состояния TCP-соединений

Каждая строка в выводе ss -tan или netstat -tan содержит состояние TCP-соединения — оно и есть главный источник информации о том, что реально происходит с сокетом. Разберём четыре состояния, которые чаще всего вызывают вопросы.

LISTEN — процесс открыл порт и ждёт входящих подключений, но само по себе это ничего не говорит о том, приходят ли на него реальные запросы. LISTEN в выводе означает только «сокет создан и готов принимать» — это первое, что стоит проверить, если сервис вроде бы запущен, а подключения не проходят: если строки с LISTEN на нужном порту нет вообще, проблема не в сети, а в том, что сервис не поднялся или слушает не тот адрес/порт, который вы проверяете.

ESTABLISHED — полноценное, активное на уровне TCP соединение: рукопожатие завершено, обе стороны могут обмениваться данными. Наличие ESTABLISHED-соединений — это ожидаемое, штатное состояние для любого сервиса, который реально кто-то использует. Само по себе большое число ESTABLISHED не проблема — вопрос в том, соответствует ли оно ожидаемой нагрузке (условно, у веб-сервиса с активным трафиком их будет много, у редко используемого внутреннего API — единицы), и не растёт ли оно бесконтрольно там, где должно быть стабильным.

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

CLOSE_WAIT — а вот это состояние стоит проверять внимательнее. Оно означает, что удалённая сторона уже прислала сигнал о закрытии соединения (FIN), операционная система на вашем сервере это получила и подтвердила, но ваше собственное приложение до сих пор не вызвало закрытие своего конца сокета. В норме CLOSE_WAIT — это доли секунды между получением FIN и вызовом close() в коде приложения. Если в выводе ss регулярно видно много соединений в CLOSE_WAIT, которые не убывают, а копятся, — это почти всегда означает, что приложение не закрывает сокеты корректно: где-то в коде есть путь (часто — обработка исключения или таймаут выше по стеку), после которого сокет остаётся открытым с точки зрения приложения, хотя удалённая сторона уже давно от него отключилась. Такая утечка постепенно съедает лимит файловых дескрипторов процесса и рано или поздно приводит к ошибкам вида «too many open files».

Кроме этих четырёх в выводе изредка встречаются SYN-SENT и SYN-RECV (соединение в процессе установки — рукопожатие ещё не завершено) и FIN-WAIT-1/FIN-WAIT-2 (соединение в процессе закрытия с вашей стороны). Разово по одной-две строки в этих состояниях — норма, устойчиво большое и растущее их число заслуживает того же внимания, что и накопление CLOSE_WAIT.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Считаем соединения по состояниям

Листать построчно вывод ss на сервере с тысячами соединений бессмысленно — первым делом стоит сгруппировать вывод по состояниям и посмотреть на общую картину:

ss -tan | awk 'NR>1 {print $1}' | sort | uniq -c | sort -rn
   842 ESTAB
   311 TIME-WAIT
    47 LISTEN
     6 CLOSE-WAIT
     2 SYN-RECV

Тот же приём с netstat, только состояние в его выводе лежит в другой колонке:

netstat -tan | awk '/^tcp/ {print $6}' | sort | uniq -c | sort -rn

У ss есть и более прямой способ — фильтр по состоянию прямо в команде, без последующей группировки в отдельную строку на состояние:

ss -tan state established | wc -l
ss -tan state time-wait | wc -l
ss -tan state close-wait

Такой срез — первое, что стоит смотреть при жалобе «сервер тормозит» или «не принимает соединения»: он сразу показывает, где сосредоточена основная масса соединений и есть ли в списке состояние, которого там в принципе быть не должно в таком количестве (в первую очередь — растущий CLOSE_WAIT).

Кто держит порт: находим процесс по соединению

Частая практическая задача — не просто увидеть, что порт занят, а понять, каким именно процессом, особенно если на порту вместо ожидаемого сервиса откликается что-то другое или запуск сервиса падает с ошибкой «address already in use». Флаг -p у обеих утилит добавляет PID и имя процесса в вывод, но для сокетов, принадлежащих не вашему пользователю, обычно требует прав root:

sudo ss -tlnp sport = :443
LISTEN 0 511 0.0.0.0:443 0.0.0.0:*  users:(("nginx",pid=1842,fd=6))

Аналогично можно смотреть не только слушающие порты, но и конкретные установленные соединения — например, найти все сессии с определённым удалённым адресом:

sudo ss -tanp dst 203.0.113.44

Через netstat то же самое:

sudo netstat -tlnp | grep :443
sudo netstat -tanp | grep 203.0.113.44

Если под рукой нет ни ss, ни netstat с рабочим -p (в контейнерах и урезанных окружениях это бывает), тот же вопрос «кто держит порт» решают lsof -i :443 или короткая fuser 443/tcp — обе команды тоже требуют root для чужих процессов, но не всегда есть в минимальном образе, так что рассчитывать заранее на конкретный инструмент в незнакомом окружении не стоит — стоит проверить, что реально установлено.

Сервер слушает, но нет входящих — как отличить от «всё повисло»

Жалобы «сайт недоступен» и «сервис не отвечает» на уровне TCP выглядят по-разному, и ss/netstat быстро показывают, какой именно случай перед вами.

Сервис слушает, но подключений нет вовсе. В выводе есть строка LISTEN на нужном порту, а ESTABLISHED-соединений с внешними адресами по этому порту нет или почти нет, хотя по логам или ожиданиям трафик должен идти. Это признак того, что проблема не в самом сервисе, а раньше — на пути к нему: файрвол блокирует порт снаружи, security group у облачного провайдера не пропускает трафик, DNS указывает не на тот сервер, балансировщик не считает бэкенд здоровым и не шлёт на него запросы. Сам сервис в такой ситуации обычно жив и не является источником проблемы — искать нужно на уровне сети до него.

Много соединений висит в непонятном состоянии. Здесь картина обратная: LISTEN на месте, но помимо ожидаемых ESTABLISHED в выводе накапливается большое и растущее число соединений в CLOSE_WAIT (утечка на стороне приложения, разобрана выше) или в SYN-RECV (незавершённые рукопожатия — либо очень высокая частота новых подключений, с которой сервис не справляется, либо признак SYN-флуда). В обоих случаях трафик до сервера доходит — проблема не в сети перед ним, а в том, как сам сервис или ОС на сервере обрабатывает уже дошедшие соединения.

Различить эти два сценария — вопрос буквально одной команды: если после ss -tan | grep :443 вы видите один LISTEN и пустоту, ищите блокировку на пути к серверу; если видите растущий список строк в состояниях, отличных от ESTABLISHED, ищите проблему в самом приложении или в его нагрузке.

Много TIME_WAIT на нагруженном сервере — не паникуйте

Отдельно стоит сказать про типичную ложную тревогу: администратор видит на нагруженном сервере несколько тысяч соединений в TIME_WAIT и решает, что это авария. В большинстве случаев это не так. TIME_WAIT — обязательная, предусмотренная протоколом стадия закрытия каждого соединения, а не результат ошибки. Сервер, который обрабатывает много коротких соединений в секунду (например, HTTP-запросы без переиспользования соединения, каждое из которых устанавливается и закрывается заново), будет постоянно проводить через TIME_WAIT соединения пропорционально этой частоте — просто потому, что через эту стадию обязан пройти каждый закрытый сокет. Абсолютное число здесь растёт вместе с частотой новых соединений и само по себе ни о чём плохом не говорит.

Тревожиться стоит не из-за абсолютного числа TIME_WAIT, а из-за конкретных практических последствий, если они реально наступили: например, если исходящих соединений с одного и того же локального адреса на один и тот же удалённый адрес и порт настолько много, что заканчивается пул доступных исходящих портов, и новые соединения начинают падать с ошибкой — вот это уже реальная проблема, а не количество строк в выводе ss. О том, как посчитать, сколько исходящих портов реально доступно и когда они действительно могут закончиться, — в статье про переиспользование соединений и цену установки нового и в разборе конкретного случая, когда закончились исходящие порты из-за TIME_WAIT. Если задача — сократить число коротких соединений как таковых (а не просто перестать пугаться цифры), первый шаг обычно не тюнинг sysctl, а включение постоянных соединений (keep-alive) там, где протокол это позволяет — это снижает саму частоту входа в TIME_WAIT, а не маскирует симптом.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

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

Почему ss быстрее, чем netstat, на сервере с большим числом соединений?

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

Можно ли доверять флагу -p без root?

Для собственных процессов текущего пользователя PID и имя обычно показываются и без root, но для чужих сокетов (в том числе системных сервисов, запущенных от других пользователей) ядро не отдаёт эту информацию непривилегированному процессу — нужен sudo. Без прав вы просто увидите пустое поле вместо PID, а не ошибку.

Много CLOSE_WAIT — это всегда баг в коде приложения?

В подавляющем большинстве случаев — да, это означает, что приложение получило сигнал о закрытии соединения от удалённой стороны, но не вызвало закрытие своего сокета. Разовые единичные CLOSE_WAIT, которые быстро исчезают, — норма; устойчиво растущее число — повод смотреть логи и код обработки соединений конкретного сервиса, а не сетевую конфигурацию.

Стоит ли специально уменьшать длительность TIME_WAIT через настройки ядра?

Прежде чем что-то менять на уровне ядра, стоит убедиться, что проблема вообще есть — то есть реально заканчиваются исходящие порты или память под сокеты, а не просто цифра в выводе ss выглядит большой. Если проблема подтверждена, зачастую эффективнее сократить саму частоту создания новых коротких соединений (например, keep-alive на стороне клиента или прокси), чем подстраивать параметры ядра под уже сложившийся паттерн трафика.

ss показывает соединение, которого, по ощущениям, быть не должно — что делать дальше?

Определите процесс через ss -tanp или lsof -i, посмотрите, с каким удалённым адресом и портом установлено соединение, и сопоставьте это с логами приложения или файрвола за то же время. Само по себе неожиданное соединение — повод разобраться, а не сразу считать инцидентом: иногда это легитимный фоновый процесс (обновления, мониторинг, служебные подключения), о котором просто забыли.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →