Основы Computer Science Как компьютеры общаются: сети, модель OSI, стек TCP/IP
0%

Как компьютеры общаются: сети, модель OSI, стек TCP/IP

Как компьютеры общаются: сети, модель OSI, стек TCP/IP

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

Задача звучит обманчиво просто: «взять биты здесь и получить их там». Но между «здесь» и «там» — ненадёжные провода, радиоэфир, десятки чужих машин-посредников, разные скорости и разные производители железа. Как из этого хаоса получается надёжный GET /index.html, долетающий до нужного сервера и возвращающийся с картинкой котика? Ответ — не одна гениальная идея, а стопка простых идей, сложенных слоями. Разберём эту стопку снизу вверх: сначала поймём, почему сеть устроена слоями, потом пройдём каждый слой (провод → кадр → пакет → соединение → приложение), увидим, как запрос заворачивается в конверты (инкапсуляция) и летит через маршрутизаторы, и в конце честно посмотрим, где эта красивая модель протекает и портит жизнь.

Почему это трудно: первые принципы

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

  • Среда ненадёжна. Провод ловит наводки, Wi-Fi глушит микроволновка, бит переворачивается, целый кусок данных теряется. Физика не гарантирует, что отправленное дойдёт неповреждённым.
  • Машин много, а среда общая. К одному кабелю или к одной точке доступа подключены десятки устройств. Если все заговорят разом — каша. Нужен способ адресовать кому предназначены данные и разруливать, кто вещает сейчас.
  • Мир не помещается в одну сеть. Ваш Wi-Fi дома — крошечный островок. Сервер в Амстердаме — на другом островке. Между ними — не один провод, а цепочка чужих сетей. Нужен способ проложить маршрут через незнакомые промежуточные сети.
  • Отправитель и получатель — процессы, а не машины. На одном сервере крутятся веб-сервер, почта и база данных. Дойти «до машины» мало — надо попасть в нужную программу на ней.
  • Все разные. Ноутбук на Wi-Fi, сервер на оптоволокне, телефон на 5G, роутеры трёх производителей. Они обязаны понять друг друга, ничего не зная о внутренностях соседа.

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

Идея слоёв: стек протоколов

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

  • Каждый уровень пользуется услугой уровня снизу и предоставляет услугу уровню сверху.
  • Уровень общается только с таким же уровнем на другой машине (свой «язык») и только с соседними уровнями на своей машине. Он ничего не знает об устройстве далёких уровней.

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

Историю этой идеи придумывали дважды, поэтому у неё две модели — учебная и рабочая.

Модель OSI и стек TCP/IP: два взгляда на один стек

Модель OSI (Open Systems Interconnection, 1984) — эталонная схема из семи уровней. Её редко реализуют буквально, но она — общий словарь: когда инженер говорит «проблема на третьем уровне» или «это L7-балансировщик», он имеет в виду именно уровни OSI.

Стек TCP/IP — то, на чём реально работает интернет. В нём четыре уровня: прикладной, транспортный, межсетевой и канальный. Он проще OSI (сеансовый и представления просто влились в прикладной), потому что вырос не из комитета, а из работающего кода ARPANET. Дальше мы будем говорить в терминах TCP/IP, кивая на номера OSI, где это привычнее.

Уровни снизу вверх

Физический и канальный: биты и кадры среди соседей

Самый низ — физический уровень: как представить 0 и 1 в реальном мире. Напряжением в медном проводе, вспышкой света в оптоволокне, модуляцией радиоволны в Wi-Fi. Здесь живут электрика и физика; для нас важно, что отсюда наверх поднимается поток битов — уже не аналоговый сигнал, а честные нули и единицы (как из напряжений получаются логические уровни, мы видели в статье Булева логика и вентили).

Над ним — канальный уровень (Data Link, L2): он группирует биты в кадры (frames) и доставляет их между устройствами в пределах одной локальной сети — от вашего ноутбука до Wi-Fi-роутера, от роутера до соседнего порта коммутатора. Здесь появляется первый адрес — MAC-адрес: 48-битный номер, зашитый в сетевую карту на заводе (вроде 00:1b:44:11:3a:b7). Технологии этого уровня — Ethernet (провод) и Wi-Fi (радио). Канальный уровень решает вторую нашу проблему — «среда общая»: когда к одному сегменту подключены много устройств, нужны правила, кто вещает и как разбирать коллизии, плюс контрольная сумма (CRC), чтобы отбросить кадр, повреждённый по дороге.

Важное ограничение: MAC-адрес работает только внутри локальной сети. Он не поможет дотянуться до сервера в другой стране — тот в другой сети, до него надо маршрутизировать. Этим занимается следующий уровень.

Сетевой (межсетевой): пакеты и IP-адреса через весь мир

Межсетевой уровень (Internet, L3) решает третью проблему — «мир не в одной сети». Его задача — доставить данные между любыми двумя машинами интернета, прыгая через цепочку промежуточных сетей. Единица здесь — пакет (packet), а адрес — IP-адрес.

IPv4-адрес — это 32 бита, привычно записанные как четыре байта: 93.184.216.34. Их всего ~4 млрд, и они кончились ещё в 2011-м — отсюда IPv6 со 128-битными адресами (2606:2800:220:1:248:1893:25c8:1946), которых хватит навсегда. В отличие от MAC, IP-адрес не привязан к железу: он выдаётся сети и говорит не «какая это карта», а «где в топологии интернета находится машина» — как почтовый индекс.

Устройства этого уровня — маршрутизаторы (routers). Каждый держит таблицу маршрутизации: «пакеты для такой-то сети отправляй в такой-то соседний роутер». Пакет путешествует по прыжкам (hop by hop): роутер смотрит на IP получателя, находит в таблице следующий прыжок, пересылает — и забывает о пакете. Так, эстафетой, конверт добирается от вашего дома до дата-центра.

Инкапсуляция: запрос спускается вниз по стеку, обрастая заголовками

Ключевая черта IP — он ненадёжен нарочно (best-effort, «как получится»). Пакет может потеряться, прийти дважды, обогнать соседа и прибыть не по порядку. IP не обещает ничего, кроме «я постараюсь». Это выглядит как недостаток, но это осознанный выбор: сделать нижний уровень тупым, быстрым и дешёвым, а надёжность — если она нужна — построить выше. Это и есть знаменитый end-to-end принцип (Saltzer, Reed, Clark, 1984): умную логику держи по краям сети, а середину оставляй простой. Чтобы заблудившийся пакет не кружил вечно, в его заголовке есть TTL (time to live) — счётчик прыжков, который каждый роутер уменьшает на единицу; дошёл до нуля — пакет выбрасывают. На этом же счётчике работает утилита traceroute.

За то, как маршрутизаторы вычисляют кратчайшие пути в графе сети (протоколы OSPF, BGP), отвечают алгоритмы на графах — интуиция про них в треке Алгоритмы, а как ядро ОС реально гоняет пакеты через сетевую карту и прерывания — в треке Операционные системы.

Транспортный: соединения, порты, надёжность

IP умеет доставить пакет до машины, но не до программы и без всяких гарантий. Четвёртую и первую проблемы — «дойти до нужного процесса» и «сделать доставку надёжной» — закрывает транспортный уровень. Здесь появляется порт: 16-битный номер, адресующий конкретное приложение на машине. Пара (IP-адрес, порт) называется сокетом и однозначно указывает на «эту программу на этой машине». Веб-сервер слушает порт 443, почта — 25, ваш браузер для каждого запроса берёт случайный порт-«обратный адрес». Стандартные порты стоит помнить:

Порт Протокол Служба
22 TCP SSH — удалённый вход
53 UDP / TCP DNS — имена в адреса
80 TCP HTTP — веб без шифрования
443 TCP / UDP HTTPS — веб (UDP для HTTP/3 · QUIC)
5432 TCP PostgreSQL

На этом уровне — два главных протокола с противоположной философией.

UDP (User Datagram Protocol) — тонкая обёртка над IP: добавляет порты и контрольную сумму, и всё. Так же ненадёжен: датаграмма может пропасть или прийти не в срок. Зато быстр и без церемоний — идеален там, где свежесть важнее полноты: игры, голос, видеозвонки, тот же DNS. Потерялся один пакет с кусочком звука — проще пропустить, чем ждать.

TCP (Transmission Control Protocol) — наоборот, строит поверх ненадёжного IP надёжный упорядоченный поток байтов. Он берёт на себя всё, чего не даёт IP:

  • Соединение — стороны сначала договариваются, что говорят друг с другом.
  • Надёжность — каждый кусок нумеруется; получатель шлёт подтверждения (ACK); неподтверждённое TCP пересылает заново.
  • Порядок — по номерам получатель собирает куски в исходной последовательности, даже если пакеты обогнали друг друга.
  • Управление потоком и перегрузкой — TCP следит, чтобы не завалить ни медленного получателя, ни перегруженную сеть, и сам подбирает темп.

Соединение открывается тройным рукопожатием (three-way handshake) — тремя пакетами, которыми стороны синхронизируют стартовые номера:

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

Цена надёжности — задержки и «болтливость»: рукопожатие тратит целый круговой обход сети (RTT) до первого байта данных, а строгий порядок порождает проблему, к которой мы вернёмся ниже. Выбор между TCP и UDP — классический trade-off «надёжно, но медленнее» против «быстро, но сам разбирайся с потерями».

Прикладной: протоколы, которые видит программа

Верхний уровень — прикладной. Здесь живут протоколы, которыми пользуются программы: HTTP (веб), DNS (имена → адреса), SMTP (почта), SSH, а также TLS — шифрование поверх TCP, превращающее HTTP в HTTPS. На этом уровне данные снова обретают смысл: не «пакет с байтами», а «запрос страницы» или «письмо». Как именно устроены HTTP, DNS и путь запроса в браузере — тема следующей статьи, чтобы не размазывать; тут нам важно, что прикладной протокол просто отдаёт свои байты вниз транспортному уровню и не думает о пакетах, маршрутах и потерях — за него всё сделал стек.

Инкапсуляция: матрёшка из заголовков

Как эти уровни физически стыкуются? Через инкапсуляцию (см. схему выше). Когда данные спускаются по стеку у отправителя, каждый уровень оборачивает пакет соседа своим заголовком — как вкладывают конверт в конверт:

  1. Браузер формирует HTTP-запрос — просто текст с данными.
  2. Транспорт добавляет спереди TCP-заголовок (порты, номера, ACK) → получается сегмент.
  3. Межсетевой добавляет IP-заголовок (IP-адреса откуда/куда, TTL) → получается пакет.
  4. Канальный добавляет Ethernet-заголовок (MAC соседа) и контрольную сумму → кадр.
  5. Физический превращает кадр в поток битов и вещает в среду.

У получателя всё идёт в обратном порядке: сетевая карта собирает биты в кадр, снимает Ethernet-заголовок и отдаёт пакет ядру ОС; ядро снимает IP-заголовок, потом TCP-заголовок, и чистые данные всплывают в нужную программу по номеру порта. Красота этой схемы — в разделении труда: коммутатор читает только Ethernet, маршрутизатор — только IP, ОС получателя — только TCP-порт, и лишь само приложение видит данные. Каждый посредник вскрывает ровно свой конверт и не лезет глубже.

Мелкая, но показательная деталь: числа в заголовках (порты, длины, адреса) всегда пишутся big-endian — старший байт первым. Это соглашение так и называется — network byte order, и оно живёт независимо от того, как байты лежат внутри вашего процессора (порядок байтов и почему он вообще бывает разным — в статье Представление данных). Игнорировать его — классический баг сетевого кода на «неправильной» архитектуре.

Адреса и имена: четыре разных «где»

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

Адрес Уровень Пример Область действия Отвечает на вопрос
MAC канальный 00:1b:44:11:3a:b7 одна локальная сеть какая это сетевая карта
IP сетевой 93.184.216.34 весь интернет где машина в топологии
Порт транспортный 443 внутри одной машины какая программа на ней
Доменное имя прикладной example.com для людей как это назвать словами

Связывают эти уровни две служебные системы. DNS переводит доменное имя в IP-адрес (её разберём в следующей статье). ARP внутри локальной сети находит MAC по известному IP — «у кого из вас адрес 192.168.1.1? назовите свой MAC». А поскольку публичных IPv4 на всех не хватает, дома и в офисах используют приватные адреса (192.168.x.x, 10.x.x.x) и NAT (Network Address Translation): домашний роутер подменяет ваш приватный адрес на один общий публичный и раздаёт по портам, кто из внутренних устройств что запросил. Именно поэтому десяток гаджетов в квартире выходят в интернет «под одним IP» — и именно поэтому входящее соединение снаружи «просто так» до вашего ноутбука не достучится (это и защита, и вечная боль p2p-приложений).

Пощупать руками

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

# Жив ли узел и какой round-trip? (шлёт ICMP-пакеты сетевого уровня, L3)
ping example.com

# Показать маршрут по прыжкам — тот самый TTL в действии (L3)
traceroute example.com        # в Windows: tracert

# Имя → IP: запрос к DNS (прикладной уровень поверх UDP:53)
dig +short example.com

# Какие сокеты слушает моя машина: (IP, порт) в столбце Local Address
ss -tlnp                      # старый аналог: netstat -tlnp

# Ручной HTTP-запрос: TCP-соединение + прикладной протокол одной командой
curl -v https://example.com

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

import socket

# --- Сервер: слушает TCP-порт и возвращает то, что получил ---
def server():
    # AF_INET = IPv4, SOCK_STREAM = TCP (надёжный поток).
    # Для UDP было бы SOCK_DGRAM — и никакого listen/accept.
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.bind(("0.0.0.0", 9000))   # (IP, порт) — на каком сокете слушать
        s.listen()                  # готов принимать соединения
        conn, addr = s.accept()     # блокируемся, пока клиент не постучится
        with conn:
            print("подключился", addr)
            data = conn.recv(1024)  # получить до 1024 байт из потока
            conn.sendall(data)      # отправить обратно (эхо)

# --- Клиент: три строчки, и TCP сам делает рукопожатие ---
def client():
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect(("127.0.0.1", 9000))   # тут скрыт three-way handshake
        s.sendall(b"privet")             # b"" — сырые байты, а не строка
        print(s.recv(1024).decode())     # -> privet

Один вызов connect() разворачивается в SYN/SYN-ACK/ACK, пересылку потерянных сегментов и сборку по порядку — но программе видна лишь надёжная труба для байтов. Практическое введение в сокеты, ставшее классикой, — Beej’s Guide to Network Programming.

Где эта абстракция протекает

Стек TCP/IP — великая абстракция: она позволяет писать connect() и не думать о проводах. Но, как всякая абстракция, она протекает, и знать, где именно, — разница между «работает у меня» и «работает в проде под нагрузкой на другом континенте».

  • Задержка не сводится к нулю никакими деньгами. Скорость света конечна: пакет Москва↔Нью-Йорк физически не долетит быстрее ~40 мс в одну сторону. Пропускную способность (сколько гигабит в секунду) купить можно, а задержку (RTT) — почти нет. Их вечно путают: «широкий канал» не значит «быстрый отклик». Чат и игра страдают от RTT, скачивание фильма — от пропускной способности. Число круговых обходов в вашем протоколе важнее толщины трубы.
  • Пакеты реально теряются и приходят не по порядку. IP этого не скрывает — скрывает TCP, и не бесплатно. Один потерянный сегмент заставляет TCP придержать все последующие, пока потерянный не будет переслан, — head-of-line blocking. Именно из-за него придумали HTTP/3 поверх UDP (QUIC): чтобы потеря в одном потоке не стопорила остальные.
  • «Соединение» — это иллюзия состояния. Никакого провода между вами и сервером нет — есть лишь согласованные счётчики на двух машинах. Уснул ноутбук, сменил Wi-Fi на 5G, NAT-роутер «забыл» вашу трансляцию по таймауту — и соединение молча мертво, хотя обе стороны думают, что оно живо. Отсюда keep-alive, переподключения и таймауты в любом сетевом коде.
  • MTU и фрагментация. У кадра есть максимальный размер (обычно ~1500 байт). Пакет крупнее режется на части, и потеря одной теряет весь пакет. Неудачно настроенный MTU (частая беда VPN) даёт «сайт открывается, но большие страницы висят» — коварнейший из багов.
  • NAT ломает симметрию. Модель «любая машина может позвонить любой» давно неверна: из-за NAT входящие соединения к домашним устройствам по умолчанию невозможны, что усложняет видеозвонки и p2p и породило целую индустрию «пробивания NAT» (STUN/TURN).

Эти грабли так стары и так регулярны, что их собрали в канонический список — восемь заблуждений о распределённых системах (L. Peter Deutsch, Sun, 1994): «сеть надёжна», «задержка нулевая», «полоса бесконечна», «сеть безопасна», «топология неизменна», «есть один администратор», «транспорт бесплатен», «сеть однородна». Каждое звучит очевидно ложно — и каждое раз за разом молча закладывается в код, который потом падает в проде. Оригинал — Fallacies of distributed computing.

Немного истории

Обратите внимание на 1983-й и 1989-й: веб появился на десятилетия позже сети. Интернет — это транспорт (TCP/IP), а веб — лишь одно из приложений поверх него, самое известное, но не единственное. Их постоянно смешивают, хотя это разные слои: сеть довозит байты куда угодно, веб — конкретный способ этими байтами обмениваться страницами.

Типичные заблуждения

  • «Интернет и веб — одно и то же». Нет. Интернет — сеть (TCP/IP), веб — приложение (HTTP) поверх неё. Почта, видеозвонки, игры — тоже интернет, но не веб.
  • «IP-адрес намертво привязан к устройству». Нет, привязан MAC. IP выдаётся сетью и меняется при переходе в другую сеть; за NAT десяток устройств делят один публичный IP.
  • «TCP гарантирует доставку». Он гарантирует либо доставку по порядку, либо явную ошибку, если дозвониться не удалось. Разорванный кабель TCP не починит — он честно сообщит о разрыве, а не доставит волшебно.
  • «Толстый канал = быстрый интернет». Полоса и задержка — разные вещи. Для отклика важнее RTT и число круговых обходов, а не гигабиты.
  • «Сеть надёжна, раз обычно работает». Она ненадёжна по устройству (best-effort IP). «Обычно работает» обеспечивают TCP, повторы и таймауты — и рушится ровно там, где о них забыли.

Мини-итог

Компьютеры общаются не одной хитрой программой, а стопкой простых слоёв, где каждый решает свою часть трудной задачи и говорит только с соседями. Физический уровень гонит биты по среде; канальный собирает их в кадры и доставляет соседям в локальной сети по MAC-адресам; сетевой (IP) везёт пакеты между любыми машинами мира по IP-адресам, прыгая через маршрутизаторы, — быстро, дёшево и ненадёжно нарочно; транспортный доводит данные до нужной программы по портам, а TCP вдобавок делает поток надёжным и упорядоченным ценой задержек; прикладной уровень оперирует уже смыслом — HTTP, DNS, почтой. Склеивает всё инкапсуляция: данные обрастают заголовками сверху вниз у отправителя и раздеваются снизу вверх у получателя. Модель OSI даёт общий словарь из семи уровней, стек TCP/IP — четыре рабочих. И как всякая абстракция, сеть протекает: задержку не купить, пакеты теряются, «соединение» — лишь согласие двух счётчиков, а восемь заблуждений о распределённых системах ждут каждого, кто поверит, что «сеть надёжна».

Основательные источники, если захочется глубже: RFC 791 (IP) и RFC 793 (TCP) — первоисточники; книга Куроса и Росса «Computer Networking: A Top-Down Approach» — лучший учебник «сверху вниз»; Cloudflare Learning Center — короткие разборы по темам.

Что дальше

Мы построили трубу, по которой байты долетают от одной машины до другой. Осталось понять, что именно по ней ходит, когда вы открываете сайт: как доменное имя превращается в адрес (DNS), как устроен диалог браузера и сервера (HTTP), и что происходит между нажатием Enter в адресной строке и появлением страницы. Об этом — следующая статья трека.

Как работает веб: DNS, HTTP, браузер, клиент и сервер

Нашли неточность? Выделите фрагмент текста — рядом появится жучок.

Нужен разбор именно вашей ситуации?

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

Доска запросов