Pull to refresh

Компьютерные сети «под капотом»: детальный разбор по уровням OSI и TCP/IP

Level of difficultyMedium
Reading time36 min
Views9.1K

На собеседованиях по часто задают знаменитый вопрос, узнаваемость которому по большей части дал facebook*: «Что происходит после того, как вы вводите URL сайта в адресную строку браузера и нажимаете Enter?». Несмотря на кажущуюся простоту, этот вопрос покрывает широкий спектр тем – DNS, TCP/IP, HTTP, и даже работу браузера. Разработчики разных уровней иногда теряются в деталях ответа. Понимание этого процесса важно для инженеров – оно показывает, как взаимодействуют между собой различные сетевые протоколы и уровни. Ниже мы шаг за шагом рассмотрим, как данные проходят через каждый слой сетевого стека, и проиллюстрируем это примерами.

Общая схема: модели OSI и TCP/IP

Прежде чем углубляться в детали, вспомним архитектуру сетевых моделей. OSI – эталонная модель из 7 уровней (физический, канальный, сетевой, транспортный, сеансовый, уровень представления, прикладной). TCP/IP – прикладная модель Интернета, часто представляется в виде 4 или 5 уровней (физический + канальный, сетевой, транспортный, прикладной). Фактически протоколы Интернета соответствуют уровням OSI примерно так:

  • Прикладной уровень (OSI 7) – соответствует Application (TCP/IP). Протоколы: HTTP, HTTPS, DNS, FTP и др. Отвечает за взаимодействие приложений с сетью, формирование сообщений.

  • Уровень представления (OSI 6) – в TCP/IP не выделен отдельно (частично входит в прикладной). Отвечает за преобразование данных: формат, кодировки, шифрование.

  • Сеансовый уровень (OSI 5) – также часто неявно включён в прикладной. Отвечает за установление/поддержание сеансов связи (например, TLS-сессия).

  • Транспортный уровень (OSI 4) – Transport (TCP/IP). Протоколы: TCP, UDP. Обеспечивает сквознуюдоставку данных между узлами, сегментацию, надёжность (в случае TCP).

  • Сетевой уровень (OSI 3) – Internet (TCP/IP). Протокол IP (IPv4/IPv6), маршрутизация пакетов через сети, логическая адресация.

  • Канальный уровень (OSI 2) – Link (TCP/IP). Протоколы: Ethernet (IEEE 802.3), Wi-Fi (802.11), PPP и др. Отвечает за передачу фреймов между соседними узлами, физическую адресацию (MAC), обнаружение ошибок.

  • Физический уровень (OSI 1) – Physical (TCP/IP). Определяет электрические/оптические сигналы, модуляцию, разъёмы, битовую передачу по среде.

Прикладной уровень: URL, DNS и HTTP-запрос

Прикладной уровень – самый верхний, где работают приложения пользователя и протоколы высокого уровня вроде HTTP(S) и DNS. В нашем сценарии первые шаги происходят именно на этом уровне:

  1. Разбор URL. После ввода адреса (например, https://www.example.com/path), браузер разбирает URL: протокол (http или https), доменное имя (www.example.com), путь (/path) и т.д. На основе схемы (http vs https) браузер определяет порт по умолчанию (80 для HTTP, 443 для HTTPS). Также проверяется, не является ли введённая строка поисковым запросом или локальным адресом.

  2. DNS-запрос имени в IP-адрес. Браузеру нужен IP-адрес сервера, соответствующий доменному имени. Для этого используется система DNS (Domain Name System) – “телефонная книга” Интернета, которая преобразует понятные доменные имена в числовые IP-адреса. Сначала браузер проверяет кеш: есть ли нужный IP уже в кэше DNS браузера, затем в кэше ОС, затем может обратиться к локальному файлу hosts. Если нигде не найдено, формируется DNS-запрос. Этот DNS-запрос – отдельное сетевое сообщение: браузер обращается к настроенному DNS-серверу (обычно это рекурсивный DNS провайдера или локальный ресолвер) и запрашивает A-запись для домена.

    • Поиск по иерархии DNS: Если адрес не найден в ближних кэшах, запрос уходит в сеть. Рекурсивный DNS-сервер провайдера сам выполняет необходимые шаги: обращается к корневому DNS-серверу, получает адрес DNS-сервера домена верхнего уровня (TLD, например .com), затем – адрес авторитетного DNS-сервера для конкретного доменного имени. Наконец, авторитетный сервер возвращает нужный IP-адрес. Этот IP передаётся браузеру. Таким образом DNS-система выясняет “на каком сервере живёт сайт”.

    Пример DNS-запроса на Python и Java: для иллюстрации можно программно выполнить DNS-резолвинг. В Python стандартная функция socket.gethostbyname() делает запрос через системный резолвер, а в Java – класс InetAddress.

    Пример (Python): получим IP-адрес для домена:

    import socket
    
    def resolve_hostname(hostname: str):
        try:
            ip_address = socket.gethostbyname(hostname)
            print(f"IP-адрес для {hostname}: {ip_address}")
        except socket.gaierror:
            print(f"Не удалось разрешить имя: {hostname}")

    Пример (Java): аналогичный DNS-запрос:

    import java.net.InetAddress;
    
    public class DnsResolver {
        public static void main(String[] args) {
            String hostname = "example.com";
    
            try {
                InetAddress address = InetAddress.getByName(hostname);
                String ip = address.getHostAddress();
                System.out.println("IP-адрес для " + hostname + ": " + ip);
            } catch (Exception e) {
                System.err.println("Не удалось разрешить имя: " + hostname);
                e.printStackTrace();
            }
        }
    }
  3. Установление TCP-соединения. Получив IP-адрес сервера, браузер переходит к установлению соединения с ним (это уже работа транспортного уровня, но инициируется на прикладном по необходимости отправить запрос). Если используется HTTPS, сначала откроется TCP-соединение на порт 443, если HTTP – на порт 80. Браузер также определяет номер исходящего порта на клиенте: операционная система выделяет свободный ephemeral port (обычно >1024) для этого соединения. Например, браузер может использовать порт 50000 на своей стороне для подключения к server_ip:80. Важный аспект – номера портов: порт идентифицирует приложение/службу на узле. Веб-сервер по умолчанию слушает 80/443, а браузеру не важен конкретный исходящий порт (кроме как уникальный ID сеанса); ОС выдаёт случайный свободный.

  4. Формирование HTTP-запроса. Прежде чем отправить HTTP-запрос, если URL начинается с https://, браузер инициирует TLS-handshake (об этом – в разделе сеансового уровня). После установления (возможного) безопасного канала, прикладной уровень формирует сам HTTP-запрос. HTTP относится к прикладному уровню и определяет синтаксис и семантику веб-запросов и ответов. Браузер собирает HTTP-запрос типа GET к указанному ресурсу (например, GET /path HTTP/1.1) с необходимыми заголовками – Host (доменное имя), User-Agent (идентификация клиента), Accept (типы принимаемых данных), Cookies и др. Заголовки содержат дополнительную информацию о клиенте и запросе. Получив IP и установив соединение, браузер отправляет этот HTTP-запрос на сервер.

    Пример HTTP-запроса «вручную»: ниже демонстрируется, как можно отправить простой HTTP GET-запрос через TCP-сокет, без высокоуровневых библиотек – это помогает понять структуру протокола.

    Пример (Python): прямое обращение через сокет:

    import socket
    
    def simple_http_client():
        host = "example.com"
        port = 80
        request = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n"
    
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
            s.connect((host, port))
            s.sendall(request.encode("utf-8"))
    
            response = b""
            while True:
                chunk = s.recv(1024)
                if not chunk:
                    break
                response += chunk
    
        print(response.decode("utf-8", errors="replace"))

    Здесь мы видим текст HTTP-запроса, который отправляется по установленному TCP-соединению.

    Пример (Java): отправка HTTP-запроса с использованием сокета:

    import java.io.*;
    import java.net.Socket;
    
    public class SimpleHttpClient {
        public static void main(String[] args) {
            Socket socket = null;
    
            try {
                socket = new Socket("example.com", 80);
    
                PrintWriter out = new PrintWriter(socket.getOutputStream());
                BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    
                out.print("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n");
                out.flush(); // отправляем HTTP-запрос
    
                String line;
                while ((line = in.readLine()) != null) {
                    System.out.println(line); // читаем и выводим построчно
                }
    
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (socket != null && !socket.isClosed()) {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    Этот код устанавливает соединение с сервером и выполняет HTTP-запрос вручную. В реальности браузер параллельно может делать несколько запросов (за HTML, за ресурсами страницы) и обрабатывать ответы.

  5. Получение HTTP-ответа. В ответ на наш запрос сервер формирует HTTP-ответ: статусную строку (например, HTTP/1.1 200 OK), набор заголовков (Content-Type, Content-Length, Date, Set-Cookie, и т.д.) и тело ответа – собственно запрошенный ресурс (HTML-код страницы, данные API или др.). Статусный код информирует об результате обработки (200 – успех, 404 – не найдено, 500 – ошибка сервера и т.д.). Заголовки ответа несут мета-информацию: тип содержимого, размер, параметры кеширования, возможно указание на сжатие (Content-Encoding) и прочее. Тело ответа содержит данные (для веб-страницы – HTML разметка). Браузер получает эти данные по тому же TCP-соединению, разбирает заголовки и передаёт тело на отображение. При передаче больших ответов TCP обеспечивает разбивку на сегменты и сборку на принимающей стороне, так что приложение (браузер) видит непрерывный поток данных.

Примечание: Загрузка страницы обычно включает несколько запросов. Сначала основной HTML, затем браузер анализирует HTML и для каждого скрипта, CSS, изображения делает отдельные запросы (может открывать дополнительные TCP-соединения, обычно до 6–8 на домен). Здесь для простоты мы описываем один цикл запрос-ответ.

На прикладном уровне могут работать и другие протоколы в зависимости от ситуации: например, перенаправление на другой URL (HTTP 3xx) потребует нового запроса; если используется протокол HTTP/2 или HTTP/3, то внутри соединения может идти мультиплексирование запросов. Но базовые принципы остаются: приложение формирует запросы, которые через транспорт доставляются на сервер.

Уровни сеанса и представления: TLS, шифрование и формат данных

Сеансовый уровень (OSI 5) отвечает за установление и управление сеансом связи между приложениями. В веб-контексте яркий пример – протокол TLS (Transport Layer Security), который обеспечивает безопасный сеанс поверх транспортного соединения. Уровень представления (OSI 6) отвечает за представление данных в нужном формате – это включает сериализацию, кодирование, сжатие, шифрование. Рассмотрим основные аспекты, которые можно отнести к этим уровням, хотя в современных сетях они часто реализованы на уровне приложений или библиотек.

TLS – установка защищённого сеанса (сеансовый уровень)

При соединении по HTTPS браузер и сервер выполняют TLS-handshake – процесс установления безопасного сеанса. TLS (ранее SSL) можно отнести сразу к двум уровням: сеансовому (управление соединением, обмен ключами) и представления (шифрование). После открытия TCP-сокета на порт 443, прежде чем отправлять HTTP-запрос, инициируется рукопожатие TLS. В ходе этого многошагового протокола клиент и сервер обмениваются сообщениями, чтобы: взаимно удостовериться, договориться о параметрах шифрования (версия протокола, набор шифров), обменяться сертификатами и сгенерировать общие сеансовые ключи для шифрования трафика.

Классический (до TLS 1.3) ход handshake примерно таков:

  • Client Hello: клиент (браузер) отправляет сообщение с версиями TLS, набором поддерживаемых шифров и случайным числом.

  • Server Hello: сервер отвечает выбором версии и шифра, предоставляет свой сертификат X.509 (с открытым ключом) и может запросить сертификат клиента (реже, при mTLS). Также сервер может отправить свой публичный ключ DH (для генерации общего секрета) или выполнить шаги обмена ключами.

  • [Client Certificate:] (опционально, если сервер запросил клиентский сертификат, клиент отправляет свой сертификат для аутентификации).

  • Key Exchange: клиент генерирует предмастер-ключ (pre-master secret), шифрует открытым ключом сервера и отправляет серверу, либо (в случае Diffie-Hellman) обе стороны уже обмениваются необходимыми данными для вычисления общего мастер-ключа.

  • Finished: оба вычисляют общий мастер-ключ и сессионные симметричные ключи шифрования. Затем посылают завершающие сообщения, подтверждающие успешное установление защищённого канала.

После рукопожатия дальнейший трафик (HTTP-запросы и ответы) шифруется этими симметричными ключами. Таким образом, устанавливается сеанс TLS, гарантирующий шифрование и целостность передаваемых данных. На этом этапе можно считать, что на OSI уровень сеанса установлен защищённый канал.

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

Примечание: TLS также выполняет функции представления (шифрование/дешифрование данных). Но помимо TLS, сеансовый уровень в общем случае может обеспечивать и контроль диалога между приложениями (например, точки синхронизации, повторное соединение и прочее), хотя в типичной стеку TCP/IP эти обязанности минимальны или возложены на приложение.

Представление данных: сериализация, кодировки, сжатие

Уровень представления отвечает за то, чтобы данные, отправляемые приложением одного хоста, были понятны приложению на другом хосте. Это включает несколько задач:

  • Форматирование и синтаксис данных. Например, преобразование структур данных в последовательность байт (сериализация). В веб-приложениях это может быть преобразование объектов в JSON или XML строку, подготовка двоичного протокола. Если клиент и сервер используют разные внутренние представления, уровень представления выполняет перевод в стандартный формат. Например, преобразование текстовой информации в определённой кодировке (UTF-8, UTF-16, ASCII и т.д.). HTTP-протокол, хоть и считается прикладным, имеет аспекты представления – например, заголовок Content-Type/Charset указывает, в какой кодировке отправлен текст, и принимающая сторона декодирует его в свою внутреннюю форму. Также, если данные передаются в бинарном виде (например, изображение или файл), они могут быть закодированы (например, base64) для передачи в текстовом протоколе – опять-таки задача уровня представления.

  • Шифрование на уровне данных. Помимо TLS, возможны механизмы шифрования данных на уровне презентации. Например, приложение может самостоятельно зашифровать часть полезных данных перед передачей (PGP-шифрование письма и т.д.). В общем случае, презентационный уровень может шифровать и сжимать данные, снимая эту задачу с прикладного уровня. Однако в вебе это редко выделено: шифрование выполняет TLS, а сжатие – HTTP (Content-Encoding: gzip). Тем не менее, концептуально, сжатие (gzip/deflate) – функция представления, т.к. изменяет представление данных (уменьшая размер).

  • Компрессия, сериализация. Например, сериализация объектов (Protocol Buffers, Thrift) – тоже аспект уровня 6. Два приложения могут договориться использовать определённый формат обмена – JSON, protobuf, XML. Протокол HTTP передаёт octet stream, а понимание того, как интерпретировать байты – задача представления и приложения. Современные REST API обычно обмениваются JSON – текстовый формат UTF-8, требующий правильной кодировки/декодировки. Если бы одна сторона посылала EBCDIC-текст, а другая ожидала ASCII, необходима была бы трансляция на уровне представления (в HTTP это решается договариванием о charset).

Подытоживая: в нашем случае уровень представления проявляется в том, что HTML-страница и другие данные имеют определённый формат (HTML является текстом в UTF-8), возможно сжаты (gzip), и браузер (приложение-получатель) должен правильно интерпретировать байты. Благо современный веб стандартизирован, и большинство этих аспектов заложены в сам HTTP и формат контента, но по модели OSI эти функции относятся к уровню 6.

Транспортный уровень: TCP и UDP – надёжная и ненадёжная доставка

Транспортный уровень отвечает за доставку данных от приложения на одном узле к приложению на другом узле сквозь сеть, обеспечивая нужную надежность и порядок. В Интернете основными транспортными протоколами являются TCP (Transmission Control Protocol) и UDP (User Datagram Protocol). В нашем сценарии при загрузке веб-страницы используются оба: DNS-запрос, как правило, идёт по UDP, а HTTP-сессия – по TCP (или QUIC/UDP в случае HTTP/3, но это отдельный случай). Рассмотрим сначала TCP, как базу веб-трафика, затем UDP (на примере DNS).

TCP – потоковое надёжное соединение

TCP – соединительно-ориентированный протокол, предоставляющий надёжный двунаправленный поток данных между двумя узлами. Основные характеристики TCP: установление соединения (handshake), гарантия доставки данных, сохранение порядка, управление перегрузкой сети и контроль потока. Для достижения этого TCP работает по принципу нумерации и подтверждения пакетов, повторной передачи потерянных, и адаптации скорости отправки.

Установление соединения (3-way handshake). Перед тем как передавать данные по TCP, стороны согласовывают параметры соединения через трёхстороннее рукопожатие (three-way handshake). Как мы уже указали в разделе приложения, клиент (браузер) инициирует соединение, отправляя специальный сегмент с флагом SYN на серверный адрес и порт. Сервер, если готов принять соединение на этом порту, отвечает сегментом SYN+ACK. Клиент в ответ шлёт ACK, после чего соединение установлено. В процессе handshake стороны договариваются о начальных номерах последовательности (sequence numbers) – каждому байту передаваемых данных в TCP-соединении присваивается порядковый номер. Также согласуются размеры окон и другие опции (например, MSS – Maximum Segment Size, опция масштабирования окна, selective acknowledgements (SACK) и др., зависящие от поддержки).

 Трёхстороннее рукопожатие TCP: клиент инициирует (SYN), сервер подтверждает и отвечает своим SYN (SYN-ACK), клиент подтверждает (ACK). После этого канал готов для передачи данных.
 Трёхстороннее рукопожатие TCP: клиент инициирует (SYN), сервер подтверждает и отвечает своим SYN (SYN-ACK), клиент подтверждает (ACK). После этого канал готов для передачи данных.

При установлении TCP-соединения обе стороны также резервируют ресурсы: буферы для приёма/передачи. Каждое соединение идентифицируется парой сокетов (IP-адрес и порт клиента + IP-адрес и порт сервера). Операционная система на сервере заносит новое соединение в таблицу (сокет в состоянии ESTABLISHED), ассоциируя его с процессом (например, веб-сервером). На клиенте браузер получает сокет, связанный с удалённым адресом.

Передача данных и управление потоком. После установки соединения начинается обмен данными. TCP работает с единицами, называемыми сегменты (TCP segment) – это контейнер для данных приложения с заголовком TCP. Однако для приложений TCP представляет данные как непрерывный байтовый поток. Браузер может передать строку HTTP-запроса длиной, скажем, 150 байт – TCP может отправить это одним сегментом (если помещается), а может разбить, если необходимо. С другой стороны, если приложение передаёт большой файл (несколько мегабайт), TCP нарежет его на последовательность сегментов оптимального размера для передачи. Типичный максимальный размер сегмента ограничен MSS (~1460 байт для Ethernet с MTU 1500) – об этом ниже в сетевом уровне.

Каждый TCP-сегмент имеет заголовок ~20 байт (без опций), включающий важные поля: исходный и целевой портыпорядковый номер последовательности (Sequence Number) первого байта данных этого сегмента, номер подтверждения (Ack Number) – следующий ожидаемый байт от собеседника, флаги (SYN, ACK, FIN, RST, PSH и др.), размер окна, контрольную сумму и пр. Эти поля позволяют реализовать надёжность и управление соединением.

Надёжность и порядок: TCP нумерует байты, а принимающая сторона отправляет подтверждения (ACK). Например, клиент отправил 1000 байт данных, нумерованных с 1 по 1000. Сервер получит их (возможно, сегментами по 500 байт) и отправит ACK с номером 1001 – ожидает байт 1001 следующим, что означает все байты до 1000 приняты успешно. Если какой-то сегмент потеряется в сети, отправитель не получит своевременный ACK на этот диапазон байт и по таймауту повторно отправит сегмент. Таким образом достигается гарантированная доставка: если сеть уронит пакет, отправитель попробует снова (и так до исчерпания повторных попыток или получения ответа). Кроме того, благодаря номерам последовательности принимающая сторона может восстановить порядок – если сегменты пришли не по порядку (что бывает, если разные маршруты или ретрансмиссия), TCP на стороне получателя буферизует их и собирает в правильной последовательности перед передачей в приложение.

Управление потоком (flow control): TCP реализует механизм скользящего окна (Sliding Window). Поле “Window Size” в заголовке определяет, сколько байт получатель готов принять (без подтверждения). Это предотвращает переполнение буфера получателя. Например, если у сервера мало памяти, он может объявить окно 5000 байт – клиент после отправки 5000 байт остановится и будет ждать ACK (который сдвинет окно). Flow control – это “настроение” получателя, информирующее отправителя о текущей вместимости.

Управление перегрузкой (congestion control): Помимо контроля на уровне получателя, TCP стремится не перегрузить сеть. Для этого алгоритмы TCP (Tahoe, Reno, NewReno, CUBIC и др. – современные ОС используют CUBIC) контролируют отправку: есть понятиe Congestion Window (cwnd) – размер окна, определяемый отправителем на основе оценки состояния сети. В начале соединения TCP запускает slow start – медленный старт: cwnd начинает с 1-2 MSS и быстро удваивается каждый RTT (если приходят ACK) до первого признака потери. При потере пакета (нет ACK, таймаут либо дупликативные ACK) алгоритм снижает скорость (уменьшает cwnd, входит в режим предотвращения перегрузки). Благодаря этому TCP автоматически регулирует скорость передачи под пропускную способность сети, стараясь не переполнить буферы маршрутизаторов (что вызывает потери). Это сложный, но ключевой аспект: TCP congestion control – основа стабильности Интернета, предотвращает коллапс от перегрузки.

Завершение соединения: после обмена данными любая сторона может инициировать закрытие соединения, отправив сегмент с флагом FIN. Другой сторона отвечает ACK и сама может отправить FIN (обычно закрытие двустороннее). После обмена FIN-ACK соединение считается закрытым. Сокеты переходят в состояние TIME_WAIT на короткое время (обычно ~1-2 минуты) для уверенности, что все задержавшиеся пакеты старого соединения исчезнут из сети.

Реализация TCP в ОС: В современных ОС (например, Linux) стек TCP/IP реализован в ядре. Когда браузер (приложение) вызывает connect()send() или recv(), эти системные вызовы переходят в пространство ядра. Ядро ведёт каждое TCP-соединение: выделяет структуры (например, struct sock в Linux), буферы отправки и приёма. Отправляемые данные сначала копируются в отправной буфер TCP; далее ядро сегментирует их по MSS, формирует заголовки TCP/IP и передаёт драйверу сетевой карты. При приходе пакетов с сети драйвер передаёт их TCP-стеку, который собирает байты в приёмном буфере и будит приложение, передавая ему данные через recv(). Вся логика нумерации, ACK, ретрансмиссий выполняется ядром. Таким образом, прикладной разработчик оперирует удобным интерфейсом (поток байтов), а сложность надёжной доставки скрыта внутри ОС. Например, Linux TCP/IP стек обеспечивает ту самую надёжность: нумерует сегменты, отслеживает кумулятивный ACK, при потере – retransmit, контролирует окно и перегрузку. Это происходит автоматически для любого сокета TCP, что и делает протокол TCP удобным для приложений.

Пример использования TCP-сокета: мы уже видели выше, как на прикладном уровне открывается сокет и передаются данные (HTTP пример). Библиотеки и фреймворки обычно скрывают детали, но можно напрямую использовать сокеты.

  • Пример: создание простого TCP-сервера на Python, который прослушивает порт и принимает соединения:

    import socket
    
    def start_tcp_server(host="0.0.0.0", port=8080):
        try:
            with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server:
                server.bind((host, port))
                server.listen()
                print(f"Сервер запущен на {host}:{port}. Ожидание подключения...")
    
                conn, addr = server.accept()
                with conn:
                    print(f"Установлено соединение с {addr}")
                    data = conn.recv(1024)
                    print(f"Получены данные: {data.decode('utf-8', errors='replace')}")
                    conn.sendall(b"Hello, client!")
    
        except Exception as e:
            print(f"Ошибка сервера: {e}")

UDP – датаграммы без установления соединения

UDP – второй основной транспортный протокол. В отличие от TCP, UDP не устанавливает соединения и не гарантирует доставку или порядок. Он предоставляет простой механизм отправки отдельных датаграмм (сообщений) от приложения к приложению (идентифицируется также по портам). UDP-заголовок очень простой – всего 8 байт: включает порт источникапорт назначения, длину и контрольную сумму. Нет полей для номеров последовательности или окна. Это означает, что UDP не делает повторных попыток и не управляет потоком – оно “бросает” пакеты в сеть, и они либо дойдут, либо нет. Если приложению нужна надежность, оно должно самому позаботиться (например, протоколы на базе UDP могут реализовывать свои ACK/ретрансмит – пример QUIC). Но многие приложения используют UDP именно ради простоты и скорости (меньше оверхеда) – например, DNS, протоколы потокового видео/аудио, игровые протоколы, VoIP, которые могут предпочесть потерю нескольких пакетов вместо задержек от повторных передач.

В нашем случае, когда браузер делает DNS-запрос, обычно используется UDP на порт 53. DNS протокол сам по себе работает так: формируется один UDP-запрос (например, “какой IP у host.com?”) и отправляется на DNS-сервер; тот отвечает UDP-датаграммой с результатом (или несколькими). Объём DNS-ответа обычно небольшой (в пределах 512 байт для классического UDP DNS, EDNS расширяет). Если DNS-ответ не помещается в UDP (бывает при DNSSEC, больших записях), сервер может установить флаг “TC” (Truncation) – тогда клиент переключается на TCP для повторного запроса. Но в большинстве случаев UDP справляется.

Особенности UDP: отсутствие соединения означает низкую задержку установления (нет handshake – экономия ~1-2 RTT по сравнению с TCP). Но и “нет гарантий”: пакеты могут потеряться или прийти вразнобой. DNS с этим справляется: если нет ответа, resolver повторяет запрос или пробует другого сервера. Многие realtime-приложения (VoIP, видеоконференции) тоже используют UDP и сами обрабатывают потери (немного искажений в аудио лучше, чем пауза от ретрансмиссии). Также UDP полезен для протоколов, где требуется мультикаст или broadcast (TCP такое не поддерживает).

Пример отправки UDP-датаграммы: отправим простое сообщение по UDP (например, эхо-серверу).

Пример (Python):

import socket

def simple_udp_client():
    server_ip = "8.8.8.8"
    server_port = 53
    message = "Hello"

    try:
        # Создаём UDP-сокет
        with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
            sock.settimeout(2)  # Таймаут 2 секунды

            # Отправляем сообщение
            sock.sendto(message.encode("utf-8"), (server_ip, server_port))

            try:
                data, addr = sock.recvfrom(512)
                print(f"Ответ от {addr}: {data.decode('utf-8', errors='replace')}")
            except socket.timeout:
                print("Нет ответа")

    except Exception as e:
        print(f"Ошибка: {e}")

Здесь мы просто демонстрируем отправку; в реальности на UDP:53 сервер 8.8.8.8 ожидает DNS-сообщение, на произвольный “Hello” скорее всего не ответит. Тем не менее, код показывает, как использовать sendto для отправки, и recvfrom для приема UDP.

Пример (Java):

import java.net.*;
import java.nio.charset.StandardCharsets;

public class UdpClientExample {
    public static void main(String[] args) {
        String message = "Hello";
        byte[] buffer = message.getBytes(StandardCharsets.UTF_8);

        try {
            InetAddress serverAddress = InetAddress.getByName("8.8.8.8");
            int serverPort = 53;

            try (DatagramSocket socket = new DatagramSocket()) {
                socket.setSoTimeout(2000); // Устанавливаем таймаут на 2 секунды

                // Отправка пакета
                DatagramPacket requestPacket = new DatagramPacket(buffer, buffer.length, serverAddress, serverPort);
                socket.send(requestPacket);

                // Приём ответа
                byte[] responseBuffer = new byte[512];
                DatagramPacket responsePacket = new DatagramPacket(responseBuffer, responseBuffer.length);

                socket.receive(responsePacket); // Блокируется до получения или таймаута

                String response = new String(
                    responsePacket.getData(), 0, responsePacket.getLength(), StandardCharsets.UTF_8
                );
                System.out.println("Ответ от " + responsePacket.getAddress() + ": " + response);
            }
        } catch (SocketTimeoutException e) {
            System.out.println("Нет ответа (таймаут).");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Мы создали UDP-сокет, отправили датаграмму и попытались принять ответ (с таймаутом). Этот шаблон похож на то, как работает DNS: послали запрос – ждем ответ.

Порты и мультиплексирование: как и TCP, UDP использует порты для доставки данных конкретному приложению. ОС держит таблицу сопоставления UDP-порт -> процесс. Если пакет пришёл на порт 53, и есть процесс – DNS-сервер, ожидающий на этом порту, ОС передаст ему данные. В случае с браузером: когда мы вызвали socket.gethostbyname, ОС сформировала UDP-запрос от какого-то исходящего порта (например 55000) на порт 53 DNS-сервера. Ответ придёт с источником 53 на назначение 55000, и ОС сопоставит порт 55000 с запущенным resolver-процессом (часто внутри системной библиотеки) и передаст ему ответ.

UDP и TCP одновременно: Интересно, что некоторые приложения могут использовать и TCP, и UDP. Например, система доменных имен: обычно UDP, но как упомянуто, может перейти на TCP для больших ответов (или для передачи зон). Другой пример – QUIC (протокол на базе UDP, заменяющий связку TCP+TLS для HTTP/3) – браузер и сервер могут договориться использовать QUIC вместо TCP. Но чтобы не усложнять, мы рассматривали классический TCP.

Резюмируя транспортный уровень: TCP обеспечивает надежную двустороннюю связь, необходимую для корректной передачи веб-страниц (гарантия, что HTML, CSS, JS придут полностью и по порядку). UDP предоставляет быстрый способ запросить вспомогательные данные (DNS) или использовать в особых случаях. Оба протокола мультиплексируют множество соединений/датаграмм между приложениями с помощью портов. Транспортный уровень изолирует вышележащие уровни от деталей сетевого уровня: приложения оперируют сокетами (TCP) или сообщениями (UDP) между узлами, не беспокоясь о том, по каким маршрутам проходят пакеты и через какие сети – это задача уровня ниже.

Сетевой уровень: IP, маршрутизация, фрагментация и ICMP

Сетевой уровень ответственен за доставку пакетов от источника к получателю через набор промежуточных сетей. Главный протокол этого уровня – IP (Internet Protocol). Он предоставляет адресацию узлов (IP-адреса) и логическую отправку пакетов (IP-дейтаграмм) с возможностью прохождения через маршрутизаторы. Сетевой уровень делает возможным связь непосредственно не связанных узлов, прокладывая маршрут.

Протокол IP и IP-пакет

IP-пакет (дейтаграмма) – основная единица на сетевом уровне. Он состоит из заголовка (минимум 20 байт для IPv4 без опций) и полезной нагрузки (данных транспортного уровня, например сегмента TCP или датаграммы UDP). В IPv4-заголовке содержатся поля: адрес источника и адрес назначения (по 32 бита каждый), версия (4), длина заголовка, флаги и смещение фрагментации, TTL (Time To Live), протокол верхнего уровня (TCP=6, UDP=17 и т.д.), контрольная сумма заголовка, и др. Поля IP позволяют маршрутизировать пакет и контролировать его доставку:

  • IP-адреса (источника и назначения) – логические адреса в сети. В момент отправки браузером IP-пакета: источник = IP машины пользователя (например, 192.168.1.10 если дома за NAT, или публичный IP), назначение = IP-адрес сервера (полученный через DNS). Маршрутизаторы по пути будут смотреть адрес назначения, чтобы решить куда направить пакет дальше. IP-адрес обычно состоит из префикса сети и хоста – маршрутизация основана на префиксе (например, адрес 93.184.216.34 попадает в сеть 93.184.216.0/24, за которую отвечает определенный маршрутизатор).

  • TTL (Time To Live) – поле (8 бит), которое уменьшается каждым маршрутизатором на единицу. Если TTL достигает 0, пакет уничтожается, а отправителю может вернуться сообщение об ошибке (ICMP Time Exceeded). TTL предотвращает бесконечное блуждание пакетов по круговым маршрутам. Обычно TTL устанавливается 64 или 128 при отправке.

  • Протокол верхнего уровня – указывает, какой транспортный протокол заключён в данных (TCP, UDP, ICMP и т.п.), чтобы на стороне получателя передать данные соответствующему обработчику.

  • Фрагментация: IP поддерживает разбиение больших пакетов на более мелкие фрагменты, если они не проходят по размеру в канал на пути. Поля Identification, Flags, Fragment Offset в заголовке IPv4 относятся к фрагментации. Если пакет больше, чем MTU выходного интерфейса, маршрутизатор может (для IPv4) его фрагментировать на несколько IP-пакетов. На получателе сетевой уровень соберет фрагменты обратно в исходный пакет (на основе ID и смещений). Фрагментация нежелательна, но возможна. В IPv6 фрагментация делается только отправителем (маршрутизаторы не фрагментируют). Обычно TCP старается узнать MTU пути (Path MTU Discovery) и не посылать сегменты, больше чем может пройти, чтобы избежать фрагментации.

IP – ненадёжный протокол: важно понимать, что IP сам по себе не гарантирует доставку и не устанавливает соединения. Это протокол типа best effort – “сделаем всё, что можем”. Пакет может потеряться (вызвано перегрузкой, битой CRC в канале и прочим) – IP этого не обнаружит. Пакет может прийти дублированным – IP не избавит от дубликатов. Может прийти вне порядка – IP тоже не отслеживает порядок. Эти задачи возлагаются на транспортный уровень (TCP). IP лишь старается доставить. Но IP очень масштабируем – маршрутизаторы просто пересылают пакеты без сложного состояния на каждое соединение (в отличие от телефонной сети, где соединение устанавливалось по цепочке). Именно поэтому Интернет работает – IP-протокол прост и stateless для отдельных пакетов.

Маршрутизация пакетов

Когда наш IP-пакет (содержаший, например, TCP-сегмент с HTTP-запросом) выходит от компьютера пользователя, ему нужно добраться до сервера, обычно находящегося в другой сети, зачастую через множество промежуточных узлов. Маршрутизаторы (роутеры) – сетевые устройства уровня 3, которые принимают IP-пакеты и пересылают их дальше по направлению к получателю, согласно таблице маршрутов.

Как пакет идёт от клиента к серверу:

  • Сначала, на локальном уровне, компьютер определяет, куда отправить пакет: напрямую получателю или через шлюз. Он сравнивает IP-адрес назначения с собственной адресацией и маской подсети. Если сервер не в той же локальной сети, пакет направляется на маршрут по умолчанию – обычно домашний роутер или шлюз провайдера. Например, пользователь 192.168.1.10/24, шлюз 192.168.1.1, сервер 93.184.216.34 – другой сети – значит, пакет адресуется на MAC-адрес шлюза (это происходит на канальном уровне через ARP, о чём ниже).

  • Домашний маршрутизатор получив пакет, смотрит адрес назначения (93.184.216.34). Он ищет в своей таблице маршрутизации, куда отправлять пакеты для этой подсети/адреса. Вероятно, домашний роутер не знает точно где эта сеть, поэтому отправит провайдеру (у него настроен uplink). Провайдерский маршрутизатор далее направит пакет по своей магистрали – возможно на вышестоящий маршрутизатор, и так далее по иерархии провайдеров. В конечном итоге пакет достигнет маршрутизатора, отвечающего за сеть 93.184.216.0/24, который узнает, что 93.184.216.34 находится, например, за интерфейсом к какому-то датацентру, и отправит прямо на сервер.

Каждый маршрутизатор по пути уменьшает TTL на 1. Если по какой-то причине маршрут зациклится или слишком длинный – TTL станет 0 и пакет будет отброшен.

Пример: предположим, пользователь – Москва, провайдер МГТС, хочет зайти на example.com (сервера в США). Пакет пройдет: домашний роутер -> узел МГТС -> магистральный маршрут -> международный шлюз -> ... -> маршрутизаторы в сети Cloud/Hosting -> сервер. При этом он, возможно, пройдет через 10–15 узлов (ping/traceroute можно использовать, чтобы увидеть цепочку – они используют TTL и ICMP).

После того, как пакет проделает путь и достигнет сервера, на сервере сетевой уровень передаст его транспортному (TCP), тот – приложению (веб-серверу), который сформирует ответ. Ответный пакет пойдет обратно через те же уровни: HTTP ответ -> TCP сегменты -> IP-пакеты, которые по маршрутам (возможно, не строго симметричным тем же узлам, но в Интернете маршрут обычно приблизительно симметричен) вернутся к клиенту.

Следующая схема иллюстрирует прохождение пакета через разные устройства сети:

 Схематичный маршрут пакета через интернет: компьютер слева отправляет IP-пакет (источник 10.0.0.1, назначение 150.150.0.1) своему шлюзу (роутер), который выполняет трансляцию адресов (NAT) на публичный адрес 200.100.10.1. Далее пакет проходит через интернет к роутеру получателя, который доставляет его серверу (150.150.0.1). Обратный ответ идет аналогичным путём.
 Схематичный маршрут пакета через интернет: компьютер слева отправляет IP-пакет (источник 10.0.0.1, назначение 150.150.0.1) своему шлюзу (роутер), который выполняет трансляцию адресов (NAT) на публичный адрес 200.100.10.1. Далее пакет проходит через интернет к роутеру получателя, который доставляет его серверу (150.150.0.1). Обратный ответ идет аналогичным путём.

NAT – Преобразование сетевых адресов

В современном интернете распространена технология NAT (Network Address Translation) – она часто используется на домашних роутерах, корпоративных шлюзах для экономии публичных IPv4-адресов. NAT работает на границе между частной сетью и интернетом, подменяя адреса (и порты) в пакетах.

Например, дома ваши устройства имеют адреса из приватного диапазона (192.168.x.x). Роутер имеет один публичный IP от провайдера. Когда компьютер 192.168.1.10 отправляет пакет на 93.184.216.34, роутер переписывает исходный адрес в пакете: с 192.168.1.10 на свой публичный (скажем, 203.0.113.5). Также переписывается порт источника (если нужно, для уникальности). Роутер сохраняет соответствие в таблице трансляции (NAT table): что соединение от внутреннего 192.168.1.10:50000 соответствует внешнему 203.0.113.5:61000, например. Когда от сервера приходит ответ на 203.0.113.5:61000, роутер на лету заменяет адрес назначения (публичный -> 192.168.1.10) и порт (61000 -> 50000) и отправляет в локальную сеть. Таким образом, для внутреннего клиента это прозрачно, а со стороны интернет весь трафик как будто идет от одного адреса (роутера). NAT “скрывает” внутренние узлы за одним IP.

Хотя NAT – не часть изначально чистой модели TCP/IP, сейчас это повсеместно. NAT-бокс хранит состояние активных трансляций (обычно по TCP-соединениям или UDP “псевдо-сессиям”). Если долго нет активности, запись может удалиться (и тогда последующие пакеты не пройдут). NAT выполняется на сетевом уровне (переписывая IP) и транспортном (порты). Иногда его относят к “между” сетевым и транспортным уровням.

Отметим, что NAT нарушает принцип сквозной адресации – поэтому появились решения вроде STUN/TURN для P2P за NAT, а в IPv6 NAT не требуется (каждое устройство может иметь публичный адрес). Но IPv4 NAT – стандарт де-факто.

Протокол ICMP

Помимо передачи пакетов, сетевой уровень включает ICMP (Internet Control Message Protocol) – протокол контроля и сообщений об ошибках. ICMP не используется для передачи данных приложения, но сети используют его для служебных целей: сообщить об ошибке или проверить доступность. Например, если маршрутизатор не может доставить пакет (нет маршрута до хоста, или TTL истёк, или требуется фрагментация, а флаг DF установлен) – он отправит ICMP сообщение обратно источнику (типы Destination Unreachable, Time Exceeded, Fragmentation Needed etc.). Самая известная утилита – ping – посылает ICMP Echo Request пакеты, а хост-назначение отвечает ICMP Echo Reply, позволяя проверить, жив ли узел и за какое время доходят пакеты. Traceroute – использует ICMP Time Exceeded: посылает пакеты с разными TTL, чтобы промежуточные роутеры ответили ICMP сообщением, раскрывая свои адреса.

ICMP сообщения инкапсулируются в IP (Protocol = 1 для ICMPv4). Таким образом, ICMP технически работает над IP, но относится логически к сетевому уровню, т.к. обслуживает его (контроль). Основное назначение ICMP – уведомление об ошибках передачи. Например, если пакет слишком большой и фрагментация запрещена (DF), роутер отбросит его и пришлёт ICMP “Fragmentation needed”. TCP-стек получив ICMP может уменьшить MSS и повторить. Или если конечный порт недоступен (UDP), получатель шлёт ICMP Port Unreachable. Эти механизмы помогают протоколам реагировать на проблемы.

В контексте загрузки веб-страницы ICMP явно браузером не вызывается (кроме случаев диагностики), но он может возникать: например, если путь до сервера имеет MTU меньше 1500 и первый пакет был с DF – может прийти ICMP Fragmentation needed, заставив TCP снизить MSS. Или если сеть совсем недостижима – роутер отправит ICMP Destination Unreachable, и стек TCP сообщит об ошибке соединения.

Итог по сетевому уровню: IP обеспечивает глобально адресованную “службу доставки” для транспортного уровня. Он разбивает Интернет на сети и маршрутизирует пакеты. Дополнительные протоколы (ICMP, ARP/NDP) обеспечивают поддержку этой доставки. IP-пакеты инкапсулируют TCP/UDP сегменты и передаются через разные каналы и маршрутизаторы, пока не достигнут узла-назначения. Без сетевого уровня компьютеры за пределами локальной сети не могли бы обмениваться данными – именно IP связал мир в единую сеть.

Канальный уровень: Ethernet, MAC-адреса и ARP

Канальный уровень (Data Link Layer) отвечает за передачу данных между непосредственно соединёнными узлами в пределах одной сети (сегмента). Он оперирует кадрами (frames) – блоками данных, которые содержат полезную нагрузку (например, IP-пакет) и заголовок/концовку канального протокола. На канальном уровне работают технологии локальных сетей: Ethernet, Wi-Fi, PPP, Frame Relay, ATM (частично), и др. В контексте домашней/офисной сети и большинства подключений – главный протокол канального уровня это Ethernet(кабельное) или Wi-Fi (беспроводное). Рассмотрим Ethernet как типовой пример.

Ethernet и фреймы

Ethernet объединяет функции двух уровней OSI: канального и физического (802.3 стандарт описывает и формат кадра, и электрические сигналы). Логически, Ethernet – это канальный протокол с MAC-адресацией. Каждое устройство (сетевая карта) имеет уникальный MAC-адрес (6 байт, обычно записывается как 01:23:45:67:89:AB). Ethernet-кадр содержит MAC-адрес источника и назначения. Когда узел хочет отправить IP-пакет по Ethernet, ему нужно указать MAC-адрес получателя на локальной сети (или MAC-адрес роутера-шлюза, если пакет наружу).

Структура Ethernet-кадра: классический Ethernet II кадр включает: преамбулу (7 байт) и SFD (1 байт) – на физическом уровне для синхронизации; затем заголовок: Destination MAC (6 байт)Source MAC (6 байт)EtherType (2 байта); далее Payload (полезные данные) от 46 до 1500 байт (если данных меньше 46, добавляется Padding до минимальной длины кадра 64 байта); и в конце FCS (Frame Check Sequence, 4 байта) – контрольная сумма для проверки ошибок. Поле EtherType указывает, какой протокол инкапсулирован в payload – например, 0x0800 означает IP версии 4, 0x86DD – IPv6, 0x0806 – ARP и т.д.

Формат Ethernet-кадра (Ethernet II): указаны основные поля – преамбула (Preamble), Start of Frame Delimiter (SFD), MAC-адрес получателя (Destination Address), MAC-адрес отправителя (Destination Address), EtherType (Type), полезные данные (Data Payload), и FCS (контрольная сумма). Полевые размеры в байтах: 6+6+2 для адресов и типа, данные 46–1500, FCS 4.
Формат Ethernet-кадра (Ethernet II): указаны основные поля – преамбула (Preamble), Start of Frame Delimiter (SFD), MAC-адрес получателя (Destination Address), MAC-адрес отправителя (Destination Address), EtherType (Type), полезные данные (Data Payload), и FCS (контрольная сумма). Полевые размеры в байтах: 6+6+2 для адресов и типа, данные 46–1500, FCS 4.

Когда IP-пакет готов к отправке, сетевой уровень передаёт его канальному. Канальный (Ethernet) сформирует кадр: в поле Source MAC поставит MAC компьютера отправителя, в Destination MAC – MAC адрес получателя в той же сети. Если пакет направляется на внешний адрес, получателем на канальном уровне будет шлюз – т.е. кадр адресуется MAC-у роутера. Если же и адресат находится в этой же локальной сети (редкий случай для веб-сервера), то MAC-адресом назначения будет MAC самого сервера.

Как узнать MAC назначения? – через протокол ARP (Address Resolution Protocol). ARP – сервисный протокол, связывающий IP и MAC в пределах сети. Когда нужно отправить IP-пакет на, скажем, 192.168.1.1, а MAC неизвестен, хост отправляет широковещательный запрос “кто имеет IP 192.168.1.1?” (ARP Request) по сети. В кадре Destination MAC = FF:FF:FF:FF:FF:FF (broadcast). Узел с этим IP (роутер) ответит ARP Reply: “192.168.1.1 is at AA:BB:CC:DD:EE:FF”. Компьютер кэширует эту связь в ARP-кэше и использует MAC. Таким образом, ARP позволяет доставить IP-пакет до соседа по каналу. ARP работает на канальном уровне: ARP-запросы и ответы – это собственный тип EtherType=0x0806, они не выходят за пределы локальной сети (роутеры не ретранслируют ARP).

Например, наш браузер получил IP адрес 93.184.216.34. Предположим, наш шлюз – 192.168.1.1 (MAC = ab:cd:ef:12:34:56). Компьютер делает ARP-запрос для 192.168.1.1, получает MAC, затем формирует Ethernet-кадр: Dest MAC = ab:cd:ef:12:34:56 (роутер), Src MAC = (свой MAC), EtherType = 0x0800 (IPv4), payload = IP-пакет (внутри которого source IP=192.168.1.10, dest IP=93.184.216.34). Этот кадр отправляется в эфир (или коммутатору). Роутер его получает (видит свой MAC в dest), извлекает IP-пакет и дальше по сетевому уровню отправляет к интернету.

Доставка кадра и роль коммутаторов: в Ethernet-сети все узлы могут быть соединены через коммутатор (switch) – устройство уровня 2, которое пересылает кадры по MAC-адресам. Коммутатор строит таблицу соответствия MAC-адресов портам. Когда приходит кадр, switch смотрит dest MAC: если он известен – отправит кадр только в соответствующий порт; если неизвестен – разошлет на все (бродкаст). Это прозрачно для протоколов: Ethernet рассматривается как “общая среда”, но физически это коммутируемая топология (звезда). В старых сетях были концентраторы (hub) – уровень 1 устройства, они просто повторяли сигнал на все порты, сейчас заменены коммутаторами.

Контроль ошибок на канальном уровне: Ethernet включает FCS – отправитель считает CRC32 по кадру, получатель проверяет. Если не совпало – кадр отбрасывается. Так обнаруживаются битовые ошибки линии. Однако Ethernet не исправляет ошибки: поврежденный кадр просто теряется (верхние уровни должны позаботиться). В некоторых средах (HDLC, PPP) канальный уровень может запрашивать повтор, но Ethernet этого не делает – упрощает логику, полагаясь на TCP для повторов.

MAC vs IP: стоит отметить, что узел может иметь несколько сетевых интерфейсов, у каждого свой MAC (Ethernet, WiFi). IP-адрес обычно “привязан” к интерфейсу. MAC-адреса локально уникальны (в пределах LAN), IP глобально (в интернете). MAC-адрес меняется при переходе пакета из сети в сеть: например, на каждом участке маршрута Ethernet кадр будет иметь MAC-источник = интерфейс отправляющего роутера, MAC-назначение = интерфейс следующего роутера. IP-адреса при этом не меняются (кроме NAT случаев) – IP остается тем же от источника до получателя.

MTU и фрагментация: Maximum Transmission Unit – максимальный размер данных в кадре. У Ethernet MTU = 1500 байт по умолчанию. IP-пакет большего размера придётся фрагментировать (если IPv4 и не стоит DF). В IPv6 – просто не отправится, если больше MTU. Обычно TCP узнает MTU и посылает сегменты соответствующего размера (MSS). Иногда на VPN или tunnel-интерфейсах MTU может быть меньше (например 1400) – тогда неправильная конфигурация может приводить к “зависанию” определенных запросов (если ICMP блокируется и Path MTU Discovery не проходит).

Пример заголовков: Приведём пример значений полей, собрав всё вместе: допустим, отправляется кадр Ethernet:

  • Dest MAC: ab:cd:ef:12:34:56 (MAC роутера),

  • Src MAC: fe:dc:ba:98:76:54 (MAC отправителя),

  • EtherType: 0x0800 (IPv4).

Внутри Ethernet-пейлоада – IP-заголовок:

  • Version/IHL = 4/5, Total Length = 60 байт,

  • Source IP = 192.168.1.10, Dest IP = 93.184.216.34, TTL = 64, Protocol = 6 (TCP).

  • Далее TCP-заголовок: Src Port = 50000, Dest Port = 80, Seq = 1001, Ack = 1 (например, ACK на SYNACK), Flags = ACK, Window = 65535, и т.д.

  • TCP Data: начинается с GET / HTTP/1.1\r\n... (HTTP payload).

Каждый уровень добавил свой заголовок: так и строится инкапсуляция. Можно представить это как вложенные “матрёшки”: [Ethernet [IP [TCP [HTTP-данные]]]]. При приёме происходит обратное: сетевой адаптер проверяет FCS, передает кадр ОС; драйвер смотрит EtherType 0x0800 – передает IP-пакет стеку IP; IP-стек смотрит Protocol 6 – передает данные TCP-стеку; TCP собирает байты и отдаёт приложению (браузеру или серверу).

ARP – разрешение адресов

Мы уже коснулись ARP – он важная часть канального уровня взаимодействия с сетевым. В нашем процессе при первом обращении к шлюзу происходит ARP. ОС обычно хранит ARP-кеш (несколько минут для записей). При общении в LAN (например, сервер тоже в LAN) ARP потребуется для каждого нового адресата, если не закешировано. ARP пакеты очень просты (запрос содержит: “кто имеет IP X? отправитель: MAC=A, IP=A_ip”; ответ: “IP X = MAC=B”). Они передаются прямо в Ethernet кадрах. ARP не выходит за пределы локального сегмента. Соответственно, в нашем “что происходит...” ARP – один из первых шагов (если MAC шлюза не известен).

Без ARP не узнать MAC, без MAC не отправить Ethernet кадр. Аналог ARP для IPv6 – NDP (Neighbor Discovery Protocol) работает схожим образом (но на ICMPv6 сообщениях).

Безопасность ARP: ARP не имеет аутентификации, поэтому возможны атаки (ARP Spoofing) – злоумышленник отвечает на запросы, притворяясь другим. В локальных сетях применяют защиту (динамическое ARP слежение, статические ARP, etc.) вне рамок данной статьи.

Коммутация и VLAN: Ethernet коммутаторы могут разделять сеть на VLAN – логические сегменты, ARP работает внутри VLAN. Если два узла в разных VLAN – напрямую кадры не ходят, нужно роутер (L3) между ними.

Итак, канальный уровень обеспечивает непосредственную передачу: наш IP-пакет благополучно дошёл до первого маршрутизатора. Далее, между роутерами также существуют канальные протоколы: обычно тоже Ethernet (например, провайдеры соединены оптоволокном Ethernet), либо PPP/HDLC на серийных линках, либо SONET/SDH – неважно, принцип тот же: IP-пакеты инкапсулируются в кадры, кадры гонятся по физическим линиям.

Ошибка на канальном уровне: если, например, Ethernet обнаружил ошибку (FCS mismatch) – кадр отброшен. TCP этого не узнает напрямую (просто не получит ACK, и ретранслирует). При сильных помехах много кадров теряется – TCP замедляется. Канальный уровень Ethernet никак не исправляет, но некоторые канальные протоколы могут (например, протоколы радиосвязи могут иметь свои повторные запросы).

Наконец, кадр доходит до конечного узла (сервер). Там сетевой адаптер проверяет FCS, передаёт вверх IP, TCP, HTTP – сервер обрабатывает запрос, отвечает. Ответный трафик аналогично проходит все уровни. На уровне 2 сервер отправит кадры с MAC своего интерфейса как источник и MAC своего шлюза как получателя (если ответ идёт вне локальной сети). Роутеры направят ответ до нашего роутера, тот по NAT заменит адрес на внутренний и отправит нам кадр с нашим MAC. Сетевой интерфейс примет его и через стек данные дойдут до браузера.

Физический уровень: сигнал по проводу или воздуху

Физический уровень – самый нижний. Он определяет, как битовые единицы воплощаются в физических сигналах и передаются по среде. Физический уровень описывает электрические, оптические или радио-сигналы, используемые для представления битов, а также характеристики кабелей, разъёмов, частоты, модуляции и т.д.

Примеры физического уровня: Ethernet 1000BASE-T (гигабитный Ethernet по медной витой паре), Wi-Fi 802.11ac (радиоволны 5 ГГц, OFDM-модуляция), оптический 10GBase-SR (лазер в оптоволокне, 850 nm), DSL, сотовые стандарты (LTE/5G) – это всё технологии физического уровня.

Основная задача уровня 1 – кодирование битов в сигнал и обратное декодирование на приемной стороне. Аппаратная часть (трансивер сетевой карты, Wi-Fi чип) принимает цифровые биты от канального уровня и генерирует аналоговый сигнал на физической среде (электрическое напряжение в кабеле, импульсы света в оптоволокне или электромагнитные волны в эфире).

Примеры механизмов физического уровня:

  • В Ethernet 100BASE-TX (Fast Ethernet) используется кодирование MLT-3 (три уровня сигнала) после предварительного 4B5B кода, обеспечивающего достаточное изменение для синхронизации.

  • В Gigabit Ethernet 1000BASE-T используется 5-уровневая PAM-5 модуляция на четырех парах – сложная схема, позволяющая 1 Гбит/с по категории 5 кабелю.

  • В Wi-Fi 802.11 OFDM используется ортогональное частотное мультиплексирование – одновременно посылаются много поднесущих с разной фазой/амплитудой (QAM), достигая высоких скоростей в эфире.

Все эти детали обычно скрыты от программиста, но они определяют скорость передачи (бит/с) и надежность на физическом уровне. Например, ваш Ethernet порт договорится на 100 Mbps или 1 Gbps – это скорость физического уровня. На практике, если 100 Mbps, то максимум ~12.5 Мбайт/c полезных данных вы получите (минус накладные расходы).

Физический уровень также определяет, дуплекс соединения (half-duplex – устаревшие хабы, или full-duplex – современные point-to-point кабели), топологию (шина, звезда – но это уже больше уровень 2/1 совместно) и способ синхронизации.

Еще аспект – медиума доступ (MAC) субуровень: часто говорят об уровне 2 как включающем подуровень MAC и LLC. В Ethernet узлы в сетях с общей средой (раньше коаксиальный сегмент) должны были разделять среду (CSMA/CD – прослушивание и обнаружение коллизий). Сейчас с коммутаторами коллизий нет, каждый линк отдельный, но Wi-Fi, например, всё ещё использует общую среду – там CSMA/CA (выбор случайной задержки перед передачей, избежание одновременного эфира). Эти алгоритмы можно отнести либо к канальному (MAC sublayer), либо частично к физическому (в плане реализации).

Важно отметить: физический уровень не понимает содержимого битов – он просто транслирует сигналы. Нет понятий адресов или пакетов. Он не распознает ошибок, кроме как может на уровне сигнала (например, потеря несущей, либо PHY может считать статистику ошибок). Но контроль правильности приема кадра – это сделал FCS на уровне 2.

В рамках нашего примера: компьютер подключен кабелем Ethernet к роутеру. Значит, по витой паре идут электрические дифференциальные сигналы. Когда мы отправляем кадр, Ethernet-контроллер берет битовую последовательность кадра и кодирует (например, 8B/10B + MLT3 для 100Mbps). На проводе это изменение напряжения во времени. Роутер этот аналоговый сигнал принимает, декодирует обратно в биты. Если Wi-Fi – сигнал был бы модуляцией несущей частоты, принимающий радио тракт выделяет битовый поток.

Скорости и стандарт: физический уровень определяет, например, что 802.11ac Wi-Fi может дать теоретически 433 Мбит/с на поток на 80 МГц канале (и сложив потоки – несколько сотен Мбит). Ethernet 10GBASE-SR – 10 Гбит/с на расстояние до 300 м по OM3 волокну. Физический уровень также накладывает ограничения – задержки (пинг не может быть меньше чем задержка света/электронов по меди), возможность ошибок (дальние расстояния – затухание, помехи – BER).

В домашнем случае физический уровень обычно не проблемен – короткие кабели, стабильный сигнал. А вот по пути, если интернет проходит через радио-реле или спутник, физический уровень может иметь большие задержки или потери.

Подведем итог: физический уровень предоставляет сырую передачу битов. Он олицетворяет собой “канал” в физическом смысле. Все остальные уровни в конце сводятся к тому, чтобы физический уровень на одном узле послал правильную последовательность импульсов, а физический уровень на другом успешно её считал. И затем битовый поток собирается обратно в кадры, пакеты, сегменты, данные...

Современные интерфейсы обычно делают почти всю работу физического уровня аппаратно (ASIC, FPGA) – поэтому инженеру программно с ним редко приходится иметь дело. Но стоит помнить: пропускная способность и надежность сети во многом зависят от этого уровня (кабель хорошего качества? Wi-Fi с помехами?).

Вывод и сводная таблица

Мы проследили путь запроса от ввода URL до получения веб-страницы, рассмотрев роль каждого уровня сетевого стека. Напоследок приводим краткую таблицу соответствия уровней модели OSI, их основных функций, и примеров протоколов/технологий:

Уровень (OSI)

Протоколы/технологии

Основные функции

7. Прикладной

HTTP, HTTPS, DNS, FTP, SMTP, etc.

Взаимодействие приложений; формирование запросов и данных пользователя. Определяет смысл и содержимое сообщений (например, содержание веб-страницы).

6. Представления

TLS (шифрование данных), SSL, JPEG, JSON (форматы), ASCII/UTF-8 (кодировки)

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

5. Сеансовый

TLS (установление сессии), RPC, NetBIOS Sessions

Управление диалогом между приложениями: установка, поддержание и завершение сеанса связи. В вебе – TLS-сессия для HTTPS, поддержание сессий (например, cookie + логика приложения).

4. Транспортный

TCP, UDP, (QUIC), SCTP

Доставка данных между хостами с нужной надежностью. Мультиплексирование портов. TCP – надёжный поток: сегментация, сборка, контроль порядка, перегрузки, ретрансмиссия. UDP – ненадёжные сообщения.

3. Сетевой

IP (IPv4/IPv6), ICMP, IPsec, RIP/OSPF/BGP (маршрутные протоколы)

Логическая адресация узлов и маршрутизация пакетов через сети. IP-пакеты передаются от источника к назначению через маршрутизаторы. Реализует без соединения, best-effort доставку.

2. Канальный

Ethernet (MAC), Wi-Fi (802.11 MAC), ARP, PPP, Frame Relay

Передача кадров между узлами одной сети (или соседними по каналу). Физическая адресация (MAC). Обнаружение ошибок (FCS) и, иногда, их коррекция или повтор. Управление доступом к среде (CSMA/CD, CA).

1. Физический

Ethernet PHY (100BASE-T, 1000BASE-T), Wi-Fi радиоволны, Optical fiber, DSL, Coax

Передача битов как электрических/оптических сигналов. Определяет уровни напряжения, частоты, модуляцию, разъёмы, тайминги. Обеспечивает связь на самом базовом уровне – сигнал по кабелю или эфиру.

В контексте стека TCP/IP часто уровни 5-7 OSI совмещают в один “Прикладной”, а 1-2 называют “канал данных”. Тем не менее, при анализе «что происходит при вводе URL» мы фактически затронули все семь абстракций: от пользовательского ввода (приложение) до электрических импульсов (физический). Каждый уровень добавляет свою “служебную информацию” (заголовки) и решает свою часть задачи, предоставляя сервис вышестоящим уровням.

Таким образом, когда вы нажимаете Enter после ввода адреса, происходит колоссальная работа: DNS-протокол находит нужный сервер, TCP устанавливает соединение с гарантией доставки, при необходимости TLS шифрует канал, затем по HTTP запрашивается документ, который через те же уровни возвращается обратно. По пути срабатывают ARP для поиска MAC-адресов, маршрутизаторы IP направляют пакеты через полмира, Ethernet кадры бегут по проводам, а физический уровень в каждом сегменте надежно переносит биты.

На выходе браузер получает HTML-код страницы, после чего начинает его отображать (парсинг, рендеринг и т.д. – это уже вне сетевого стека). Вся сетвая магия происходит за доли секунды и остаётся невидимой для пользователя – но именно слаженная работа всех этих уровней делает возможным привычный веб-сёрфинг.

* - Компания Meta (признана в РФ экстремистской организацией и запрещена)

Tags:
Hubs:
+8
Comments23

Articles