Перейти к содержимому
New

DeepSeek V4.1 Flash free for 7 days: 10M tokens a day after a top-up of $10 or more. Until Nov 30.

How it works

TCP/IP и порты: как сервисы слушают на вашей машине

Порт — это пронумерованная дверь на вашей машине. Разбираем, как TCP и UDP работают с портами, затем запускаем, находим и проверяем локальный сервер на 127.0.0.1.

Начальный10 мин чтенияАвтор: NotHoly

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

Если вы читали, что делает DNS, то уже знаете, как имя превращается в адрес. Здесь разобрано, откуда берутся порты, как их используют два основных транспортных протокола и как посмотреть, какие порты слушает ваша машина. Лабораторная работает на 127.0.0.1, loopback-адресе, который всегда означает «эта машина», поэтому ничего за пределы компьютера не уходит.

Что такое порт

Порт — это 16-битное число, то есть значение от 0 до 65535. В заголовках TCP и UDP есть порт источника и порт назначения. Когда пакет доходит до компьютера, операционная система читает порт назначения и отдаёт данные программе, которая попросила принимать на этом номере.

Представьте многоквартирный дом. Почтовый адрес доводит посылку до здания. Номер квартиры подсказывает курьеру, в какую дверь стучать. В этой картине IP-адрес — здание, а порт — квартира. Кто-то решает, какая квартира какая, а жилец может съехать и уступить номер другому.

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

Почему это важно

Каждый запущенный сервис открывает хотя бы один порт. Это относится и к программам, которые вы не запускали намеренно: программам обновления, клиентам синхронизации, серверам разработки, плагинам и софту, который сам ставит себя фоновой службой. Список слушающих портов — самая простая и точная картина того, что ваша машина предлагает сети.

Если вы проверяете собственный хост, этот список надо записать первым. Прежде чем спрашивать, обновлён ли сервис и хорошо ли он настроен, нужно знать, что он существует. Работа по безопасности начинается с инвентаризации, а инвентаризация начинается с дверей.

Коротко об уровнях

TCP/IP — это набор протоколов, на котором работает интернет. Он устроен по уровням, и пока вам нужны не все из них. Здесь важны четыре:

  • Канальный (Link): передаёт кадры внутри одного сегмента локальной сети, например Wi-Fi или Ethernet.
  • Сетевой (Internet): IP передаёт пакеты между хостами через сети по IP-адресам.
  • Транспортный (Transport): TCP или UDP передают данные между программами, используя порты.
  • Прикладной (Application): протокол, на котором говорит программа, например HTTP, DNS или SSH.

Каждый уровень оборачивает данные верхнего уровня в свой заголовок. В IP-заголовке адреса источника и назначения. В заголовке TCP или UDP порты источника и назначения. Внутри лежат данные приложения. Когда пакет приходит, каждый уровень снимает свой заголовок и передаёт остальное выше.

Порт живёт на транспортном уровне. IP не знает, какой программе отдать данные. Это работа транспортного уровня, и поэтому один IP-адрес может одновременно отдавать веб-страницу на порту 443 и базу данных на порту 5432.

Как TCP открывает соединение

TCP, описанный в RFC 9293, даёт программе надёжный упорядоченный поток байтов. Прежде чем пойдут данные приложения, две стороны договариваются о начальных порядковых номерах и подтверждают, что каждая может отправлять и получать. Эта договорённость называется трёхсторонним рукопожатием.

  1. Клиент отправляет сегмент SYN. Если по-простому, он говорит: хочу подключиться к порту 8000, мой начальный порядковый номер X.
  2. Если порт 8000 слушает какая-то программа, сервер отвечает SYN-ACK. Он подтверждает X и отправляет свой начальный номер.
  3. Клиент отвечает ACK. Соединение открыто, и клиент может отправлять запрос.

Когда порт никто не слушает, целевая машина SYN-ACK не отправляет. Она шлёт сегмент RST (reset), и клиент сразу сдаётся с сообщением вроде «connection refused». Такой быстрый ответ полезен. Он говорит, что хост получил ваш пакет и что на этом порту соединения никто не принимает.

Молчаливое ожидание, которое заканчивается таймаутом, значит другое. Очень часто файрвол отбросил пакеты без ответа. «Refused» и «timed out» — две разные улики, и различать их надо уметь.

Соединения на 127.0.0.1 используют ту же логику TCP, хотя пакеты никогда не доходят до сетевой карты. Операционная система проводит рукопожатие программно, поэтому на loopback-адресе безопасно тренироваться.

Чем отличается UDP

UDP, описанный в RFC 768, намного проще. Его заголовок занимает 8 байт: порт источника, порт назначения, длина и контрольная сумма. Рукопожатия, подтверждений, повторной передачи и упорядочивания нет. Отправитель передаёт датаграмму, а сеть либо доставляет её, либо нет. Получатель ничего не подтверждает, если приложение само не реализует такую логику.

У этой простоты есть применение. DNS-запросы обычно идут по UDP на порт 53: короткий вопрос и короткий ответ укладываются в один обмен, а рукопожатие добавило бы лишние обмены пакетами. Голос и видео в реальном времени часто используют UDP, как и QUIC, транспорт под HTTP/3. У каждого из этих протоколов свои правила на случай потерь и повторов.

Порты работают для UDP так же. Порт назначения выбирает программу. Разница в том, что у UDP-порта нет состояния соединения. Любая программа, привязанная к этому порту, получает всё, что приходит, от кого угодно. Слушатель TCP видит только соединения, завершившие рукопожатие. Вопрос в обоих случаях один: какая программа получит этот пакет.

Кто решает, какой номер что значит

Internet Assigned Numbers Authority (IANA) ведёт реестр Service Name and Transport Protocol Port Number Registry. В нём записано, какой сервис обычно привязан к каждому номеру порта для TCP, UDP и нескольких других транспортных протоколов. Известные записи: 22 для ssh, 53 для domain (DNS), 80 для http и 443 для https.

RFC 6335 делит пространство номеров на три диапазона:

  • От 0 до 1023: системные порты, их ещё называют well-known. Многие системы разрешают привязываться к ним только привилегированным процессам.
  • От 1024 до 49151: пользовательские порты, где вендоры регистрируют многие сервисы.
  • От 49152 до 65535: динамические или частные порты. Клиенты часто используют их как временные порты источника.

Реестр фиксирует назначения и соглашения. В самих TCP и UDP ничто их не навязывает. Любая программа с нужными правами может привязаться почти к любому свободному порту. Реестр говорит, что обычно работает на этом номере, и про вашу машину может ошибаться. Прежде чем делать вывод, посмотрите, что на самом деле слушает.

Лабораторная: локальный веб-сервер на 127.0.0.1

Нужны терминал и Python 3. Лабораторная использует встроенный в Python HTTP-сервер, поэтому дополнительно ничего ставить не надо.

Команды для Windows ниже я запускал на Windows 11 Pro с Git Bash, Python 3.12.10 и curl 8.12.1. Команды для Linux и macOS на этой машине я не запускал, поэтому их вывод не показан. Там, где есть блок вывода, он получен от команды прямо над ним.

Одно замечание про Python: в моей сессии Git Bash python3 не нашёлся, а python запустил Python 3.12. Если на Windows python3 не работает, используйте python. Остальная часть команды не меняется.

Шаг 1: создайте папку с одним файлом

Сервер публикует всё, что лежит в папке, где он запущен, поэтому сначала положите туда что-нибудь небольшое.

bash
mkdir -p ~/port-lab && cd ~/port-lab
printf '<!doctype html><title>lab</title><h1>Port 8000 lab</h1>\n' > index.html

В PowerShell создайте такую же папку и запишите эти две строки в index.html любым редактором.

Шаг 2: запустите сервер и оставьте его работать

Держите этот терминал открытым. Сервер работает, пока вы его не остановите.

bash
cd ~/port-lab
python3 -m http.server 8000 --bind 127.0.0.1

Флаг --bind 127.0.0.1 говорит Python слушать только на loopback-интерфейсе. Без него Python слушает на всех интерфейсах машины. Эта разница пригодится позже, когда будете читать список слушающих портов.

Шаг 3: найдите слушающий сокет

Выберите команду для своей операционной системы. Каждая выводит сокеты в состоянии LISTEN, которые используют порт 8000.

Linux:

bash
ss -tlnp | grep 8000

macOS:

bash
lsof -iTCP -sTCP:LISTEN -n -P | grep 8000

Windows PowerShell:

powershell
Get-NetTCPConnection -State Listen -LocalPort 8000

Вот что вывела команда для Windows на моей машине, с конвейером через Format-Table:

text
LocalAddress LocalPort  State OwningProcess
------------ ---------  ----- -------------
127.0.0.1         8000 Listen         45608

На вашей машине идентификатор процесса будет другим. Запустите Get-Process -Id <OwningProcess> со своим номером и убедитесь, что владелец — запущенный вами процесс Python. В моём случае это был python.

На что смотреть:

  • LocalAddress равен 127.0.0.1. Сокет принимает соединения только с этой машины. Если бы вы не указали --bind, здесь стоило бы ожидать 0.0.0.0. Этот вариант на своей машине я не проверял, так что считайте его ожидаемым результатом, а не наблюдённым.
  • State равен Listen. Сокет ждёт соединений.
  • OwningProcess указывает на ожидаемую программу. Если там что-то незнакомое, остановитесь и выясните, что это, прежде чем продолжать.

Шаг 4: подключитесь

Откройте второй терминал и выполните:

bash
curl -v http://127.0.0.1:8000/

Вот вывод из моего запуска, оставлены только нужные строки:

text
* Trying 127.0.0.1:8000...
* Connected to 127.0.0.1 (127.0.0.1) port 8000
> GET / HTTP/1.1
> Host: 127.0.0.1:8000
< HTTP/1.0 200 OK
< Server: SimpleHTTP/0.6 Python/3.12.10
< Content-type: text/html
<!doctype html><title>lab</title><h1>Port 8000 lab</h1>

Читайте по порядку. Trying означает, что curl открывает TCP-соединение. Адрес уже IP, поэтому DNS-запроса нет. Connected означает, что трёхстороннее рукопожатие завершилось. GET / HTTP/1.1 — это прикладной уровень: теперь curl говорит по этому соединению на HTTP. 200 OK означает, что сервер нашёл файл и ответил. Строка HTML внизу — ваш файл.

Сервер пишет строку о запросе в свой терминал. У меня это было так:

text
127.0.0.1 - - [08/Nov/2026 06:07:56] "GET / HTTP/1.1" 200 -

Адрес источника — тот же loopback-адрес, значит запрос пришёл с этой машины.

Шаг 5: остановите сервер и повторите

Вернитесь в терминал сервера и нажмите Ctrl+C. Так сервер останавливается чисто. На своей машине я остановил процесс через Stop-Process -Force, что завершает его без возможности корректно закрыться. В любом случае слушающий сокет исчезает, как только процесс завершается.

Убедитесь, что сокета больше нет:

powershell
Get-NetTCPConnection -State Listen -LocalPort 8000

У меня вывод был пустым. Теперь снова выполните ту же команду curl:

bash
curl -v http://127.0.0.1:8000/

Вот вывод из моего запуска:

text
* Failed to connect to 127.0.0.1 port 8000 after 2048 ms: Could not connect to server
curl: (7) Failed to connect to 127.0.0.1 port 8000 after 2048 ms: Could not connect to server

Код завершения curl был 7: это значит, что подключиться не удалось.

Вот что произошло. Ваш SYN дошёл до 127.0.0.1 на порт 8000. Там никто не слушал, поэтому операционная система ответила сбросом. curl сообщил об отказе сразу, не дожидаясь таймаута. Если вместо этого будет долгое ожидание и таймаут, подозревайте файрвол или маршрут, на котором пакеты отбрасываются.

Что показала лабораторная

Один и тот же адрес и порт вели себя по-разному. Со слушателем рукопожатие завершилось и сервер ответил. Без него отказ пришёл за миллисекунды. Разницу между этими случаями определяет список слушающих портов. Когда соединение ведёт себя неожиданно, сначала проверьте этот список.

Частые ошибки

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

Считать номер порта доказательством того, что на нём работает. Порт 80 обычно означает HTTP, но программу ничто не заставляет говорить на нём по HTTP. Подключитесь и прочитайте, что присылает сервис, или проверьте, какому процессу принадлежит сокет.

Забывать про UDP. ss -tlnp показывает только слушателей TCP. Для UDP используйте ss -ulnp, а на Windows Get-NetUDPEndpoint. На машине может быть активный UDP-сервис, который никогда не появится в проверке TCP.

Принимать таймаут за «сервиса нет». Таймаут часто означает, что между вами и портом стоит фильтр. Отказ означает, что хост ответил и порт никто не слушает. Дальнейшие шаги в этих случаях разные.

Завершить не тот процесс. Прежде чем останавливать процесс, проверьте имя процесса-владельца и его командную строку. Сервер, оставшийся от прошлой попытки, может по-прежнему держать порт и отдавать файлы из папки, которую вы публиковать не собирались. Проверьте владельца, прежде чем считать порт своим.

Почему с этого начинают работу по безопасности

Слушающие порты описывают то, что ваша машина предлагает сети, намеренно или нет. Прежде чем тестировать что-то ещё на своём хосте, выведите список слушающих портов, сопоставьте каждую запись с известной вам программой и закройте лишнее. Забытый сервер разработки — хороший пример записи, которая выглядит безобидно и всё равно отвечает.

Те же команды можно применять на машинах, которые вы не контролируете, но для сканирования или проверки чужой системы нужно письменное разрешение её владельца. Тренируйтесь на своих машинах и в своей лаборатории.

Что дальше

Повторите упражнение с UDP. Короткий слушатель на Python на 127.0.0.1, порт 5399, в моём запуске получил датаграмму от отправителя. Ничего особенного слушатель не делал:

python
import socket

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("127.0.0.1", 5399))
data, addr = s.recvfrom(1024)
print("got", data, "from", addr)

Запустите его в одном терминале, затем отправьте датаграмму из другого через socket.sendto. Я получил вывод got b'hello' from ('127.0.0.1', 63411). Заметьте, что рукопожатия не было. Пакет просто пришёл. Порт 5353 на моей машине уже был занят, привязка к нему завершилась ошибкой доступа, поэтому для теста я выбрал 5399.

Потом прочитайте часть RFC 9293 об установлении соединения и сравните её с описанным выше обменом пакетами. Затем переходите к статье как работает HTTP: там разобрано, как HTTP работает поверх TCP и что содержат запрос и ответ.

Источники

RFC 9293, Transmission Control Protocol (TCP), IETF, August 2022. https://www.rfc-editor.org/rfc/rfc9293

RFC 768, User Datagram Protocol, IETF, August 1980. https://www.rfc-editor.org/rfc/rfc768

Service Name and Transport Protocol Port Number Registry, IANA. https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml

RFC 6335, Internet Assigned Numbers Authority (IANA) Procedures for the Management of the Service Name and Transport Protocol Port Number Registry, IETF, August 2011. https://www.rfc-editor.org/rfc/rfc6335

Источники

  1. RFC 9293: Transmission Control Protocol (TCP)
  2. RFC 768: User Datagram Protocol
  3. IANA: Service Name and Transport Protocol Port Number Registry
  4. RFC 6335: IANA Procedures for the Service Name and Port Number Registry

Ещё по теме: Сети и веб

Сети и веб

Что такое TLS и как HTTPS защищает трафик

TLS 1.3 даёт HTTPS конфиденциальность, целостность и аутентификацию сервера. Разбираем рукопожатие, цепочки сертификатов и то, чего замок в адресной строке не доказывает.

Начальный10 мин чтения