Как компьютеры общаются: сети, модель OSI, стек TCP/IP
До сих пор в этом треке мы разбирали одну машину: как из транзисторов собирается процессор (Как работает процессор), как он читает код и данные из памяти, как операционная система делит железо между программами (Что делает ОС). Всё это происходило внутри одного корпуса. Но настоящая мощь компьютеров началась тогда, когда они научились разговаривать друг с другом: почта, веб, мессенджеры, банковские переводы, эта самая страница у вас в браузере — всё это один компьютер, попросивший данные у другого, за тысячи километров.
Задача звучит обманчиво просто: «взять биты здесь и получить их там». Но между «здесь» и
«там» — ненадёжные провода, радиоэфир, десятки чужих машин-посредников, разные скорости и
разные производители железа. Как из этого хаоса получается надёжный GET /index.html,
долетающий до нужного сервера и возвращающийся с картинкой котика? Ответ — не одна
гениальная идея, а стопка простых идей, сложенных слоями. Разберём эту стопку снизу
вверх: сначала поймём, почему сеть устроена слоями, потом пройдём каждый слой (провод →
кадр → пакет → соединение → приложение), увидим, как запрос заворачивается в конверты
(инкапсуляция) и летит через маршрутизаторы, и в конце честно посмотрим, где эта красивая
модель протекает и портит жизнь.
Почему это трудно: первые принципы
Прежде чем строить решение, прочувствуем проблему. Соединить две машины проводом и гонять по нему напряжение — это ещё не сеть. Настоящих трудностей минимум пять, и каждый слой в итоге появится, чтобы закрыть одну из них.
- Среда ненадёжна. Провод ловит наводки, Wi-Fi глушит микроволновка, бит переворачивается, целый кусок данных теряется. Физика не гарантирует, что отправленное дойдёт неповреждённым.
- Машин много, а среда общая. К одному кабелю или к одной точке доступа подключены десятки устройств. Если все заговорят разом — каша. Нужен способ адресовать кому предназначены данные и разруливать, кто вещает сейчас.
- Мир не помещается в одну сеть. Ваш Wi-Fi дома — крошечный островок. Сервер в Амстердаме — на другом островке. Между ними — не один провод, а цепочка чужих сетей. Нужен способ проложить маршрут через незнакомые промежуточные сети.
- Отправитель и получатель — процессы, а не машины. На одном сервере крутятся веб-сервер, почта и база данных. Дойти «до машины» мало — надо попасть в нужную программу на ней.
- Все разные. Ноутбук на Wi-Fi, сервер на оптоволокне, телефон на 5G, роутеры трёх производителей. Они обязаны понять друг друга, ничего не зная о внутренностях соседа.
Попытка решить всё это одним куском кода дала бы неподъёмного монстра, который к тому же пришлось бы переписывать при каждой новой технологии связи. Инженеры пошли другим путём — тем же, что и везде в компьютерных науках: разделили задачу на слои абстракции, где каждый слой решает ровно одну из проблем и предоставляет соседу сверху чистую услугу, пряча свою кухню.
Идея слоёв: стек протоколов
Ключевой приём — протокол: заранее согласованный набор правил, по которым две стороны обмениваются данными (формат сообщений, порядок реплик, что означает каждое поле). А ключевая архитектура — стек протоколов, где протоколы сложены уровнями:
- Каждый уровень пользуется услугой уровня снизу и предоставляет услугу уровню сверху.
- Уровень общается только с таким же уровнем на другой машине (свой «язык») и только с соседними уровнями на своей машине. Он ничего не знает об устройстве далёких уровней.
Аналогия — обычная почта. Вы (прикладной уровень) пишете письмо, не думая о самолётах. Отдаёте его почте (транспорт), та кладёт в конверт с адресом, сортировочные центры (маршрутизаторы) перекидывают конверт от города к городу, курьер (канальный уровень) несёт его на последнем километре. Вы «разговариваете» с адресатом смыслом письма; сортировочный центр — только с другими центрами языком почтовых индексов. Никто не знает всю цепочку целиком, и это прекрасно: авиапочту можно заменить на поезда, не переучивая отправителей.
Историю этой идеи придумывали дважды, поэтому у неё две модели — учебная и рабочая.
Модель 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 получателя, находит в таблице следующий прыжок, пересылает — и забывает о пакете. Так, эстафетой, конверт добирается от вашего дома до дата-центра.
192.168.1.10"] --> R1["Домашний роутер
NAT + Wi-Fi"] R1 --> ISP["Роутер
провайдера"] ISP --> BB["Магистральные
маршрутизаторы"] BB --> R2["Роутер
дата-центра"] R2 --> S["Сервер
93.184.216.34"] S -.ответ той же дорогой (или другой).-> B
Ключевая черта 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 и путь запроса в браузере — тема следующей статьи, чтобы не размазывать; тут нам важно, что прикладной протокол просто отдаёт свои байты вниз транспортному уровню и не думает о пакетах, маршрутах и потерях — за него всё сделал стек.
Инкапсуляция: матрёшка из заголовков
Как эти уровни физически стыкуются? Через инкапсуляцию (см. схему выше). Когда данные спускаются по стеку у отправителя, каждый уровень оборачивает пакет соседа своим заголовком — как вкладывают конверт в конверт:
- Браузер формирует HTTP-запрос — просто текст с данными.
- Транспорт добавляет спереди TCP-заголовок (порты, номера, ACK) → получается сегмент.
- Межсетевой добавляет IP-заголовок (IP-адреса откуда/куда, TTL) → получается пакет.
- Канальный добавляет Ethernet-заголовок (MAC соседа) и контрольную сумму → кадр.
- Физический превращает кадр в поток битов и вещает в среду.
У получателя всё идёт в обратном порядке: сетевая карта собирает биты в кадр, снимает 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 в адресной строке и появлением страницы. Об этом — следующая статья трека.