По-настоящему разобраться в модели OSI — препарируем один HTTP-запрос на семь уровней

· · Модель OSI, TCP/IP, Сети, Wireshark, Ethernet, TCP, HTTP, C#, .NET, Socket, Технический консалтинг

Модель OSI — это то, с чего непременно начинается любой учебник по сетям, и при этом постоянно приходится слышать одно и то же: «названия семи уровней я перечислить могу, но по-настоящему представить себе это не получается». Физический уровень, канальный уровень, сетевой уровень… названия заучены, но так и остаётся непонятным, как эта семиэтажная схема связана с кодом на C#, который вы пишете, или с сетевым сбоем, происходящим прямо сейчас у вас на глазах.

В этом блоге мы уже писали о поведении на уровне L4 (транспортном) — о заблуждении, что каждый вызов Send на стороне TCP можно получить одним соответствующим вызовом Receive и о причинах остановки связи с промышленной камерой из-за повторных передач TCP и способах их диагностики — но статьи, которая свела бы воедино саму идею «уровней», лежащую в основе всего этого, у нас не было.

Подход этой статьи прост. Мы соберём один настоящий Ethernet-кадр, несущий один HTTP GET-запрос, и препарируем его снаружи внутрь. Семь уровней — это не просто рисунок в концептуальной схеме: они физически вложены друг в друга внутри 171 байта, передаваемых по сети. Стоит увидеть это своими глазами — на hex-дампе и в Wireshark — и модель OSI перестаёт быть предметом зубрёжки.

Код, приведённый в этой статье, опубликован на GitHub в виде готового к сборке и запуску набора примеров (библиотека для сборки и препарирования кадров, экспорт pcap-файла, который открывается в Wireshark, и модульные тесты).

osi-model-packet-anatomy - komurasoft-blog-samples (GitHub)

1. Сначала вывод

  • Модель OSI — это «словарь», а не «реализация». В реальном интернете фактически работает стек протоколов TCP/IP (по сути четыре уровня); собственные 7-уровневые протоколы OSI проиграли гонку за внедрение и в итоге почти не использовались. Выжила модель как общий язык — фразы вроде «давайте разберёмся на уровне L2» или «это проблема уровня L7».12
  • Семь уровней физически вложены друг в друга в виде последовательности байт. В кадре, несущем HTTP GET, первые 14 байт — это заголовок Ethernet (L2), следующие 20 байт — заголовок IPv4 (L3), ещё 20 байт — заголовок TCP (L4), а всё остальное — текст HTTP (L7). То, как заголовок каждого уровня начинается сразу после предыдущего, можно напрямую увидеть на hex-дампе в этой статье и в Wireshark.
  • Единственное, что ваш код на C# пишет напрямую, — это байты уровня L7. В Socket.Send передаются только данные приложения; заголовки TCP и IP добавляет стек протоколов ОС, а заголовок Ethernet и электрический сигнал — сетевой адаптер (NIC). Именно поэтому выбор между HttpClient, SslStream и Socket — это выбор того, «начиная с какого уровня и ниже» ответственность передаётся ОС.3
  • Уровни L5 (сеансовый) и L6 (представления) не существуют как независимые уровни в реальном стеке. Часть их роли берут на себя TLS и кодировка символов, но в модели TCP/IP всё это объединено в один прикладной уровень. Если фраза «не понимаю, что такое L6» — про вас, то это не ваша вина: именно здесь модель и реальность расходятся.2
  • По-настоящему модель OSI приносит пользу на практике при диагностике неполадок и в разговоре. Сужение поиска с помощью терминологии уровней — «ping проходит, а HTTP не работает, значит L3 жив, подозреваем L4 и выше» — становится общим языком, понятным и сетевым инженерам, и инфраструктурным командам, и разработчикам приложений.

2. Почему модель OSI так и остаётся зубрёжкой

Большинство объяснений начинаются с такой таблицы.

Уровень Название Описание
L7 Прикладной уровень Предоставляет приложениям сервисы связи
L6 Уровень представления Преобразует формат представления данных
L5 Сеансовый уровень Управляет началом и завершением сеанса связи
L4 Транспортный уровень Обеспечивает надёжную передачу данных
L3 Сетевой уровень Отвечает за маршрутизацию и адресацию
L2 Канальный уровень Передаёт данные между соседними узлами
L1 Физический уровень Преобразует биты в электрические сигналы

Эта таблица верна, но каждая строка написана настолько абстрактным языком, что при чтении не складывается никакого образа. Скажите кому-нибудь «преобразует формат представления данных» — и вряд ли найдётся человек, способный указать, о какой именно строке его собственного кода идёт речь.

Стоит честно упомянуть ещё один исторический факт. Модель OSI — это стандарт, утверждённый ISO (Международной организацией по стандартизации) и ITU-T (ISO/IEC 7498-1, ITU-T X.200), и изначально она задумывалась вместе с масштабным набором собственных протоколов OSI, соответствующих каждому из семи уровней.1 Но в гонке за внедрение в 1990-х годах фактическим стандартом стал TCP/IP, у которого уже были работающие реализации, а сами протоколы OSI в итоге остались почти невостребованными. RFC 1122, заложивший основы интернета, описывает мир фактически через четыре уровня — канальный, уровень интернета (IP), транспортный и прикладной, — без независимых уровней, соответствующих L5 и L6.2

Иными словами, смысл изучения модели OSI сегодня заключается не в том, что «именно так всё и реализовано», а в том, чтобы получить в руки инструмент мышления слоями и общий словарь для диагностики неполадок. Приняв эту предпосылку, можно перестать мучиться вопросом «где же реальные L5 и L6», и картина сразу становится намного яснее.

3. Главное: препарируем один HTTP-запрос

На этом с вступлением закончим и посмотрим на реальный объект. Следующий hex-дамп — это Ethernet-кадр (всего 171 байт), несущий один HTTP-запрос GET /index.html HTTP/1.1. Он собран примером кода, представленным в начале статьи, и поскольку в нём корректно рассчитаны и контрольная сумма заголовка IPv4, и контрольная сумма TCP, Wireshark отображает его как совершенно нормальный пакет.

0000  02 00 00 00 00 01 02 00  00 00 00 02 08 00 45 00  ..............E.
0010  00 9d 12 34 40 00 40 06  3b 99 c0 00 02 0a c6 33  ...4@.@.;......3
0020  64 50 cb 84 00 50 00 00  03 e8 00 00 07 d0 50 18  dP...P........P.
0030  ff ff a3 05 00 00 47 45  54 20 2f 69 6e 64 65 78  ......GET /index
0040  2e 68 74 6d 6c 20 48 54  54 50 2f 31 2e 31 0d 0a  .html HTTP/1.1..
0050  48 6f 73 74 3a 20 65 78  61 6d 70 6c 65 2e 63 6f  Host: example.co
0060  6d 0d 0a 55 73 65 72 2d  41 67 65 6e 74 3a 20 4b  m..User-Agent: K
0070  6f 6d 75 72 61 53 6f 66  74 44 65 6d 6f 2f 31 2e  omuraSoftDemo/1.
0080  30 0d 0a 41 63 63 65 70  74 3a 20 74 65 78 74 2f  0..Accept: text/
0090  68 74 6d 6c 0d 0a 43 6f  6e 6e 65 63 74 69 6f 6e  html..Connection
00a0  3a 20 63 6c 6f 73 65 0d  0a 0d 0a                 : close....

Глядя на столбец ASCII справа, можно заметить, что где-то посередине (со смещения 0x36) начинается читаемый человеком текст GET /index.html HTTP/1.1. А что же тогда представляют собой предшествующие 54 байта? Если скормить их разборщику из примера, он выдаст такой отчёт.

[ L2 Ethernet II | offset   0 |  14 bytes | dst=02:00:00:00:00:01 src=02:00:00:00:00:02 type=0x0800 (IPv4)
  [ L3 IPv4        | offset  14 |  20 bytes | 192.0.2.10 -> 198.51.100.80 TTL=64 proto=6 (TCP) checksum=OK
    [ L4 TCP         | offset  34 |  20 bytes | 52100 -> 80 [Psh, Ack] seq=1000 win=65535
      [ L7 HTTP        | offset  54 | 117 bytes | GET /index.html HTTP/1.1

Это та самая схема, на которую больше всего хочется, чтобы вы обратили внимание в этой статье. В ней три ключевых момента.

  1. Каждый уровень начинается «сразу после предыдущего». Заголовок Ethernet занимает байты 0–13, заголовок IPv4 — байты 14–33, заголовок TCP — байты 34–53, а HTTP начинается с 54-го байта. Уровень — это не концептуальная категория, а нечто, что можно указать как диапазон байт от начала кадра.
  2. Внутренний уровень — это не что иное, как «полезная нагрузка (груз)» внешнего уровня. С точки зрения Ethernet всё, что идёт после него — начиная с IPv4, — это просто груз; ему нет дела до того, TCP там внутри или нет. С точки зрения IP грузом является всё, что после него, — TCP, с точки зрения TCP грузом является HTTP. Каждый уровень читает только свой собственный заголовок и передаёт груз выше, не трогая его. Именно эта структура «безразличия» и есть инкапсуляция.
  3. L1 (физический уровень) и L5/L6 в этой схеме не появляются вовсе. Работа L1 — превратить эту последовательность байт в электрический сигнал, свет или радиоволны, поэтому в дампе она никак не отражается; а L5 и L6, как уже говорилось в предыдущей главе, не имеют самостоятельной сущности в обычном HTTP/1.1 без шифрования. «Из семи уровней в дампе видны только четыре» — такова реальность.

Повторим ещё раз: дело здесь вовсе не в особенности HTTP. Будь то соединение с базой данных, gRPC или GigE Vision для промышленной камеры — любой пакет TCP/IP поверх Ethernet имеет ровно такую же вложенную структуру. Меняется только содержимое груза уровня L7.

4. L2, канальный уровень — доставка «до соседа»

Пройдём по результатам разбора снаружи внутрь. Первые 14 байт — это заголовок Ethernet II.

02 00 00 00 00 01   MAC-адрес назначения (6 байт)
02 00 00 00 00 02   MAC-адрес источника (6 байт)
08 00               EtherType = 0x0800 (груз — IPv4)

Задача L2 — «доставить до соседнего узла в пределах одного и того же сетевого сегмента». Для указания получателя используется MAC-адрес. MAC-адрес — это идентификатор, присвоенный сетевому адаптеру, и, как правило, он никогда не доходит до конечного получателя через маршрутизатор. У кадра, отправленного с офисного ПК на веб-сервер, MAC-адресом назначения будет не веб-сервер, а шлюз по умолчанию (маршрутизатор).

Здесь у многих хотя бы раз возникает вопрос: «если уже есть IP-адрес, зачем нужен ещё и MAC-адрес?». Ответ кроется в разделении ответственности между уровнями. IP-адрес указывает на «конечный пункт назначения», а MAC-адрес — на «того, кто перенесёт данные на следующем участке пути». Если провести аналогию с курьерской доставкой, IP-адрес — это адрес получателя на накладной, а MAC-адрес — название «следующего пункта пересылки». Накладная (L3) не меняется до самого конца, а вот метка (L2) переклеивается на каждом участке. Именно ARP отвечает за определение MAC-адреса соседнего узла по IP-адресу, и таблицу соответствия, которую помнит ПК, можно посмотреть командой arp -a.

С точки зрения оборудования устройство, читающее и пересылающее данные на основе L2, — это коммутатор (switching hub). Коммутатор выбирает порт, ориентируясь исключительно на MAC-адрес назначения, и не заглядывает в IP-адрес, который едет внутри как груз.

5. L3, сетевой уровень — доставка на край света

Следующие 20 байт — это заголовок IPv4. Выделим основные поля.

45          Версия=4, длина заголовка=5 слов (20 байт)
00 9d       Общая длина 157 байт (заголовок IP + TCP + HTTP; 14 байт Ethernet не включены)
40 06       TTL=64, протокол=6 (груз — TCP)
3b 99       Контрольная сумма заголовка
c0 00 02 0a IP-адрес источника   192.0.2.10
c6 33 64 50 IP-адрес назначения  198.51.100.80

Задача L3 — «доставить до конечного узла-получателя, независимо от того, через сколько маршрутизаторов придётся пройти». В отличие от L2, который переносит данные лишь на один участок (один хоп) за раз, IP-адрес назначения на уровне L3 остаётся неизменным от начала до конца передачи. Маршрутизатор смотрит на IP-адрес назначения полученного пакета, решает, «какому маршрутизатору передать его дальше», и отправляет пакет, переписав заголовок L2.

TTL (Time To Live) — это поле, позволяющее наглядно увидеть, как пакет «пересекает маршрутизаторы». Оно уменьшается на единицу при каждом прохождении через маршрутизатор, а когда достигает нуля, пакет отбрасывается, а отправителю возвращается ошибка. Команда tracert (утилита Windows для трассировки маршрута) намеренно отправляет пакеты с TTL, увеличивая его 1, 2, 3 и так далее, тем самым выявляя промежуточные маршрутизаторы один за другим. Иными словами, каждая строка в выводе tracert — это физическое воплощение «хопа уровня L3».

6. L4, транспортный уровень — какому процессу доставить

Следующие 20 байт — это заголовок TCP.

cb 84       Порт источника 52100
00 50       Порт назначения 80
00 00 03 e8 Порядковый номер
00 00 07 d0 Номер подтверждения
50 18       Длина заголовка=5 слов, флаги [PSH, ACK]
ff ff       Размер окна
a3 05       Контрольная сумма

К моменту завершения работы L3 пакет уже добрался до «хоста (машины)» назначения. Но на этой машине одновременно могут работать и веб-сервер, и база данных, и множество других процессов, ведущих обмен данными. Одна из задач L4 — с помощью номера порта определить, какому процессу (какому сокету) передать данные. Порт назначения 80 по соглашению означает «номер, на котором ожидает подключений HTTP-сервер»; ОС смотрит на этот номер и помещает данные в буфер приёма соответствующего процесса.

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

Есть один любопытный факт, показывающий расхождение между идеалом модели и реальностью. Контрольная сумма TCP определена не как сумма, вычисляемая только по самому сегменту TCP, а как сумма, вычисляемая с «псевдозаголовком», в который спереди подставлена информация уровня L3 (IP-адреса источника и назначения).4 Будь уровни по-настоящему независимы друг от друга, адреса L3 не должны были бы попадать в вычисления L4. Это хороший пример того, что модель OSI — лишь карта для упорядочивания мышления, тогда как реальные протоколы при необходимости свободно пересекают границы уровней.

7. L5 и L6 — там, где модель расходится с реальностью

Итак, на схеме разбора не оказалось ни L5 (сеансового уровня), ни L6 (уровня представления). Это главная причина того, почему у многих не складывается понимание OSI, поэтому скажем прямо.

L5 и L6 не существуют как независимые уровни в реальном стеке TCP/IP. В модели интернета по RFC 1122 всё, что находится выше TCP, — это просто «прикладной уровень».2 Роли, задуманные моделью OSI, на практике рассредоточены и поглощены следующим образом.

Задумка OSI Куда это поглощено в реальности
L5: установление и управление диалогом (сеансом) TLS-сеанс/рукопожатие, файлы cookie и токены HTTP, собственная логика управления входом в приложении
L6: преобразование и шифрование представления данных Шифрование TLS, кодировка символов (UTF-8), форматы сериализации вроде JSON

Например, в случае HTTPS поверх TCP (L4) добавляется TLS, а HTTP передаётся уже внутри него. Вопрос «а к какому же уровню относится TLS?» так и просится в экзаменационный билет, но закреплять за ним один-единственный правильный ответ практического смысла не имеет. Важнее не мгновенно ответить «это L6», а уметь объяснить, что TLS находится выше L4 и ниже L7, совмещая роль управления сеансом (нечто вроде L5) и роль шифрования (нечто вроде L6).

В коде на .NET это соотношение напрямую отражается в том, как классы оборачивают друг друга. Если обернуть поток L4, полученный через TcpClient.GetStream(), классом SslStream, то всё, что записано методом Write в виде открытого текста, будет зашифровано в записи TLS ещё до того, как попадёт в TCP.5

using var client = new TcpClient("example.com", 443);
// Оборачиваем байтовый поток L4 в TLS (примерно соответствует L5/L6)
using var ssl = new SslStream(client.GetStream());
await ssl.AuthenticateAsClientAsync("example.com");
// Здесь пишется открытый текст L7 (HTTP). Шифрование — забота SslStream
await ssl.WriteAsync(Encoding.ASCII.GetBytes("GET / HTTP/1.1\r\n..."));

Оборачивание потока в поток — это кодовый эквивалент инкапсуляции. Если посмотреть в Wireshark на трафик по порту 443, то полезная нагрузка TCP видна лишь как непрозрачный блок с пометкой Application Data, что позволяет с обратной стороны убедиться: «L7 оказался упакован в нечто, эквивалентное L6».

8. К какому уровню на самом деле обращается ваш код на C#

Сопоставим всё вышеизложенное с инструментами, которыми пользуется разработчик Windows-приложений.

Уровень Пример из реального мира Пример API .NET Кто добавляет заголовок
L7 Прикладной HTTP, gRPC, собственный протокол HttpClient, Grpc.Net.Client, самописная сборка сообщений Ваш код / библиотека
(соответствует L5/L6) TLS, сериализация, кодировка символов SslStream, JsonSerializer, Encoding Библиотека
L4 Транспортный TCP, UDP Socket, TcpClient, UdpClient (заголовок формирует ОС) Стек протоколов ОС
L3 Сетевой IP, маршрутизация, ICMP Ping, NetworkInterface (только чтение) Стек протоколов ОС
L2 Канальный Ethernet, Wi-Fi, ARP (обычно недоступен напрямую из приложения) Драйвер сетевого адаптера / сам адаптер
L1 Физический Электрический сигнал, свет, радиоволны Сетевой адаптер / кабель / эфир

Из этой таблицы можно сделать два вывода.

Во-первых, выбор API — это выбор того, «начиная с какого уровня и ниже» ответственность передаётся кому-то другому. Используя HttpClient, можно передать даже сборку L7; используя Socket, весь L7 придётся писать самостоятельно. И наоборот, обычное Windows-приложение практически никогда не работает напрямую с L3 и ниже (raw-сокеты требуют прав администратора и сопряжены с серьёзными ограничениями).

Во-вторых, в Socket.Send передаются «только» байты уровня L7 — и ничего больше. Часть 2 из примера — это демонстрация, отправляющая настоящий HTTP-запрос через loopback, и приложение готовит только 60 байт HTTP-текста; те 54 байта заголовков, которые мы видели в главе 3, добавляют ОС и сетевой адаптер. Сетевой код вашего приложения на самом деле выполняет лишь работу по формированию самого внутреннего груза из семи уровней — именно такова точная позиция модели OSI по отношению к вашему коду.

using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
await socket.ConnectAsync(IPAddress.Loopback, port);

byte[] request = Encoding.ASCII.GetBytes(
    $"GET / HTTP/1.1\r\nHost: localhost:{port}\r\nConnection: close\r\n\r\n");

// Передаются только байты уровня L7. Заголовки TCP/IP/Ethernet
// добавляются по ту сторону этого вызова (ОС и сетевым адаптером)
int sent = await socket.SendAsync(request);

9. Практика: собираем кадр и открываем его в Wireshark

Чтобы превратить «прочитал и понял» в «увидел и понял», расскажем, как запустить пример кода.

В центре примера находится SampleFrameBuilder, который собирает кадр из главы 3 строго в том порядке, в котором он оборачивается от L7 к L2. Последовательность методов напрямую отражает то, что происходит на каждом уровне при отправке.

public static byte[] BuildHttpGetFrame()
{
    // L7: приложение передаёт в Send «только» эту последовательность байт
    byte[] http = Encoding.ASCII.GetBytes(HttpRequest);

    // L4: стек протоколов ОС добавляет заголовок TCP спереди
    byte[] tcp = WrapInTcp(http);

    // L3: далее спереди добавляется заголовок IPv4
    byte[] ip = WrapInIpv4(tcp);

    // L2: непосредственно перед драйвером сетевого адаптера добавляется заголовок Ethernet
    return WrapInEthernet(ip);
}

При запуске демо помимо hex-дампа и вложенного представления этот кадр записывается в виде pcap-файла.

dotnet run --project samples/Demo

Откройте получившийся sample-http-get.pcap в Wireshark.6 В центральной панели друг под другом выстроятся четыре строки — Ethernet II / Internet Protocol Version 4 / Transmission Control Protocol / Hypertext Transfer Protocol, и при клике на любую из них подсвечивается только соответствующий диапазон байт на hex-дампе внизу. То же самое, что вы видели во вложенном представлении из главы 3, показывает вам общепринятый в отрасли инструмент. Убедившись в этом, переходите к настоящему трафику: запустите захват в Wireshark, задайте фильтр вроде tcp.port == 443, откройте в браузере какой-нибудь сайт — и вы убедитесь, что весь реальный трафик устроен по этой же самой вложенной схеме.

Кроме того, в реализацию самого разборщика (PacketDissector) заложен ещё один практический урок. Например, при извлечении полезной нагрузки IPv4 нельзя просто брать весь остаток буфера приёма — нужно доверять полю TotalLength в заголовке и вырезать данные строго по нему. Дело в том, что у Ethernet есть минимальная длина кадра (60 байт), и к коротким пакетам в конец добавляется бессмысленное дополнение (padding). «Устранение особенности внешнего уровня (padding) с помощью информации о длине из внутреннего уровня» — ещё один пример того, как разделение ответственности между уровнями проявляется в реализации; это проверяется модульным тестом.

10. Использование модели OSI на практике — сужение поиска с помощью терминологии уровней

В начале статьи было сказано: «OSI выжила как словарь». Приведём два примера того, где этот словарь действительно приносит пользу.

Диагностика неполадок. Обращение вида «не могу подключиться к серверу» можно систематически сужать, переведя его на язык уровней. Стандартный подход — проверка снизу вверх.

Проверка Что использовать Какой уровень подтверждается как рабочий
Индикатор связи / индикатор подключения Wi-Fi Визуальный осмотр L1–L2
Ping шлюза в том же сегменте ping 192.168.x.1 L3 в непосредственной близости
Ping удалённого хоста ping <адрес> L3 на всём маршруте
TCP-подключение к порту получателя Test-NetConnection <адрес> -Port 443 L4 (плюс межсетевые экраны по пути)
Отправка HTTP-запроса curl -v или само приложение L7 (плюс TLS)

Например, если «ping проходит, а Test-NetConnection не срабатывает», значит, всё вплоть до L3 работает исправно, и подозрение сужается до чего-то, блокирующего порт на уровне L4 (остановленная служба, межсетевой экран, ошибка в номере порта). Если «TCP-соединение устанавливается, а HTTP возвращает 400», проблема не в сети, а в L7 (в содержимом запроса). Вместо того чтобы наугад передёргивать кабели, шаг за шагом устанавливать, вплоть до какого уровня всё живо, — вот как модель OSI применяется на практике.

Общий язык для общения. Фразы вроде «похоже на проблему L2, посмотри, пожалуйста, порт коммутатора» или «это вопрос L7, значит, к команде приложения» точно понимаются и сетевыми инженерами, и инфраструктурными специалистами, и разработчиками приложений. Номер уровня — это общая для отрасли система координат, позволяющая кратко указать зону ответственности.

11. Разбор типичных заблуждений

Напоследок разберём и исправим типичные заблуждения, связанные с моделью OSI.

  • «Интернет работает на семи уровнях OSI» — не работает. Реально реализован TCP/IP (по сути четыре уровня), а OSI — это справочная модель для объяснения и общения.2
  • «Четыре уровня TCP/IP точно соответствуют семи уровням OSI» — соответствие для L5–L7 по своей сути нечёткое. Часто встречается таблица вида «прикладной уровень TCP/IP = L5 + L6 + L7 модели OSI», но, как показано в главе 7, роли L5 и L6 в реальности рассредоточены между TLS и форматами сериализации.
  • «TLS — это протокол шестого (или пятого) уровня» — закреплять его за одним уровнем бессмысленно. Точнее сказать так: он находится выше L4 и ниже L7, совмещая роли, соответствующие L5 и L6.
  • «По номеру порта можно определить протокол» — порт 80 вовсе не гарантирует HTTP. Номер порта — это соглашение, и что там на самом деле передаётся, не узнать, пока не заглянешь в полезную нагрузку. Именно поэтому разборщик из примера определяет HTTP не по номеру порта, а по начальным символам содержимого.
  • «Коммутаторы — устройства L2, маршрутизаторы — устройства L3» — как отправная точка это верно, но в реальности обычным делом являются устройства, охватывающие сразу несколько уровней: коммутаторы уровня L3, балансировщики нагрузки, работающие на L4, WAF, анализирующие L7. Точнее рассматривать вопрос как «до какого уровня читает это устройство».

12. Заключение

  • Модель OSI — это не реализация, а словарь для мышления слоями. В реальности работает TCP/IP (по сути четыре уровня)
  • Семь уровней — это не концептуальная схема, а физически вложенная структура внутри последовательности байт одного кадра (L2: 0–13, L3: 14–33, L4: 34–53, L7: 54 и далее)
  • Каждый уровень читает только свой заголовок и не интересуется грузом (внутренними уровнями) — это и есть инкапсуляция
  • L5 и L6 не существуют самостоятельно в реальном стеке; их роли поглощены TLS и кодировками
  • Ваш код на C# пишет только байты уровня L7. Заголовки TCP/IP добавляет ОС, а Ethernet — сетевой адаптер
  • Практическая польза заключается в диагностике неполадок путём последовательной проверки работоспособности снизу вверх и в общем языке между командами
  • Собрав кадр с помощью примера кода и открыв его в Wireshark, всё содержание этой статьи можно проверить собственными глазами

Похожие статьи

Смежные направления консультирования

KomuraSoft LLC (合同会社小村ソフト) занимается проектированием и реализацией Windows-приложений, взаимодействующих по TCP/IP, поиском первопричин проблем связи с промышленным оборудованием («иногда обрывается», «замедляется»), а также поддержкой в диагностике неполадок с помощью захвата пакетов.

Справочные ссылки

  1. ITU-T, X.200 : Information technology - Open Systems Interconnection - Basic Reference Model: The basic model. Первоисточник базовой эталонной модели OSI (по содержанию эквивалентен ISO/IEC 7498-1). Об определении каждого из семи уровней.  2

  2. IETF, RFC 1122 - Requirements for Internet Hosts – Communication Layers. RFC, определяющий требования, которые должны реализовывать интернет-хосты. Об организации стека протоколов в четыре уровня: канальный, IP, транспортный и прикладной.  2 3 4 5

  3. Microsoft Learn, Socket Class (System.Net.Sockets). API сокетов в .NET. О том, что приложение передаёт только полезную нагрузку, а формирование заголовков протоколов берёт на себя стек протоколов ОС. 

  4. IETF, RFC 9293 - Transmission Control Protocol (TCP). Действующая спецификация TCP. Об обеспечении надёжности с помощью порядковых номеров и подтверждений, а также об использовании псевдозаголовка с IP-адресами при вычислении контрольной суммы.  2

  5. Microsoft Learn, SslStream Class (System.Net.Security). О классе, оборачивающем существующий поток (обычно NetworkStream для TCP) для обеспечения шифрования и аутентификации по TLS. 

  6. Wireshark Foundation, Wireshark User’s Guide. Об открытии файлов захвата, о соответствии между панелью деталей пакета и панелью байтовой последовательности, а также об использовании фильтров отображения. 

Недавние статьи с теми же тегами помогут подробнее изучить близкие темы.

Значки в области уведомлений и всплывающие (toast) уведомления в Windows-приложениях — подводные камни NotifyIcon и выбор правильного AppNotification

Практическое руководство о том, как удерживать бизнес-приложение Windows в области уведомлений (system tray) и оповещать пользователя с п...

Японская эра, праздники и даты закрытия периода в бизнес-приложениях — устойчивый к смене эры дизайн, JapaneseCalendar и расчёт рабочих дней на практике

Показать «Рэйва 8» в отчёте, рассчитать рабочие дни без учёта праздников, оплатить в последний рабочий день месяца, следующего за закрыти...

Не оборачивайте HttpClient в using ── практика HTTP-взаимодействия в бизнес-приложениях на C# (паттерны создания, тайм-ауты, повторные попытки)

HttpClient в C#, создаваемый через using при каждом запросе, приводит к исчерпанию сокетов, а статический HttpClient перестаёт учитывать ...

Эти страницы показывают тему статьи в более широком контексте услуг и решений.

Статья напрямую связана со следующими услугами.

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

Вопросы, которые часто возникают при консультациях по теме статьи.

Действительно ли семь уровней модели OSI используются в реальном интернете?
Нет, не используются. В реальном интернете фактически работает стек протоколов TCP/IP (по сути четыре уровня); собственные 7-уровневые протоколы OSI проиграли гонку за внедрение в 1990-х годах и в итоге почти не использовались. RFC 1122, заложивший основы интернета, описывает мир в терминах четырёх уровней: канального, уровня IP, транспортного и прикладного. Выжила модель как общий словарь для диагностики неполадок и общения — фразы вроде «давайте разберёмся на уровне L2» или «это проблема уровня L7».
Где на самом деле находятся сеансовый уровень (L5) и уровень представления (L6)?
Они не существуют как независимые уровни в реальном стеке TCP/IP. Роли, задуманные моделью OSI, на практике рассредоточены и поглощены в других местах: L5 (установление и управление диалогом) соответствует TLS-рукопожатию или файлам cookie и токенам HTTP, а L6 (преобразование и шифрование представления данных) — шифрованию TLS, кодировке символов и форматам сериализации вроде JSON. Нет практического смысла закреплять TLS за каким-то одним уровнем — важнее уметь объяснить, что он находится выше L4 и ниже L7, совмещая роли, эквивалентные L5 и L6.
Если уже есть IP-адрес, зачем нужен ещё и MAC-адрес?
Потому что уровни по-разному разделяют ответственность. IP-адрес (L3) — это адрес, указывающий на конечный пункт назначения, и он не меняется от начала до конца передачи. MAC-адрес (L2) — это метка, указывающая на того, кто перенесёт данные на следующем участке пути, и она заменяется при каждом пересечении маршрутизатора. У кадра, отправленного с офисного ПК на веб-сервер, MAC-адресом назначения будет не веб-сервер, а шлюз по умолчанию (маршрутизатор). ARP как раз и служит для определения MAC-адреса соседнего узла по IP-адресу, и таблицу соответствия можно посмотреть командой arp -a.
Какая практическая польза от модели OSI?
Диагностика неполадок и общение между командами. Обращение вида «не могу подключиться к серверу» можно систематически сужать, проверяя работоспособность по уровням снизу вверх: визуальная проверка индикатора связи (L1–L2), пинг шлюза (L3), проверка TCP-соединения с портом назначения (L4) и отправка HTTP-запроса (L7). Например, если ping проходит, а TCP-соединение не устанавливается, подозрение сужается до чего-то, блокирующего порт на уровне L4 — остановленной службы или межсетевого экрана. Модель также служит общей системой координат в отрасли для краткого указания зоны ответственности, как в выражении «похоже на проблему L2, посмотри, пожалуйста, коммутатор».

Об авторе

Страница с профилем автора статьи.

Го Комура

Представитель KomuraSoft LLC

Специализируется на разработке программного обеспечения для Windows, техническом консалтинге и расследовании сбоев, особенно в проектах с унаследованными системами и трудно воспроизводимыми ошибками.

Публичные ссылки

Вернуться в блог