Как работает маршрутизация запросов и ответов по портам
Что проверяют
Понимаете ли вы, кто на самом деле решает, какому процессу достанутся данные, и чем соединение отличается от порта. Следом обычно спрашивают: как один сервер на порту 8080 держит тысячи клиентов одновременно.
Как отвечать
Маршрутизаторы в сети смотрят на IP-адрес, порты им не нужны (исключение NAT, который порты переписывает). Порт это 16-битное поле заголовка TCP или UDP, значения от 0 до 65535. Доставкой до приложения занимается стек TCP/IP в ядре ОС:
- сервер: процесс открывает слушающий сокет и привязывает его к порту. В Java это
new ServerSocket(8080), в Spring Boot свойствоserver.port, по умолчанию 8080. Второй процесс на тот же порт получитBindException: Address already in use; - клиент: для исходящего соединения ОС выделяет эфемерный порт, в Linux по умолчанию из диапазона от 32768 до 60999;
- соединение: TCP-соединение определяется четвёркой «IP и порт клиента, IP и порт сервера». Ядро находит сокет по ней, поэтому ответ приходит клиенту на его эфемерный порт, а тысячи клиентов делят один серверный порт;
accept(): возвращает новыйSocket, но локальный порт у него тот же 8080.
Порты от 0 до 1023 в Linux нельзя занять без root или CAP_NET_BIND_SERVICE, поэтому Java-сервис обычно слушает 8080, а на 443 стоит nginx или балансировщик. Дальше запрос по пути или заголовку Host раскидывает прокси, это маршрутизация уровня HTTP, а не портов. В контейнерах добавляется проброс: docker run -p 8080:80 или port и targetPort в Kubernetes Service.
Где спотыкаются
Говорят, что номер порта читает само приложение или веб-сервер. Заголовки разбирает ядро, приложение получает из сокета уже готовый поток байт.
Считают, что accept() открывает новый порт на каждого клиента, и выводят отсюда лимит в 65 тысяч соединений на сервер. Такой лимит касается исходящих соединений с одного адреса на один и тот же адрес и порт назначения.
Предлагают решать конфликт портов через NAT. Конфликт решается сменой порта или остановкой процесса, который его занял, а найти этот процесс помогают ss -ltnp или lsof -i :8080.