2015-03-09

О Сети

А давайте пройдемся по основам. Мой любимый вопрос на собеседовании: «Что происходит после того, как вы ввели что-то в адресную строку браузера и нажали Enter?» Имхо, человек, называющий себя веб разработчиком, может отвечать на этот вопрос не один час, в зависимости от степени детализации.
Internet
Хорошо, опустим тонкости работы GUI браузера. Сосредоточимся на аспектах сетевого взаимодействия. Сведем задачу до какого нибудь curl http://example.com.
% curl -v http://example.com
* Rebuilt URL to: http://example.com/
* Hostname was NOT found in DNS cache
*   Trying 2606:2800:220:1:248:1893:25c8:1946...
* Connected to example.com (2606:2800:220:1:248:1893:25c8:1946) port 80 (#0)
> GET / HTTP/1.1
> User-Agent: curl/7.35.0
> Host: example.com
> Accept: */*
>
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Cache-Control: max-age=604800
< Content-Type: text/html
< Date: Sun, 08 Mar 2015 09:50:54 GMT
< Etag: "359670651"
< Expires: Sun, 15 Mar 2015 09:50:54 GMT
< Last-Modified: Fri, 09 Aug 2013 23:54:35 GMT
* Server ECS (ewr/15BD) is not blacklisted
< Server: ECS (ewr/15BD)
< X-Cache: HIT
< x-ec-custom-error: 1
< Content-Length: 1270
<
<!doctype html>
<html>
<head>
    <title>Example Domain</title>
    <meta charset="utf-8" />
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<div>
    <h1>Example Domain</h1>
    <p>This domain is established to be used for illustrative examples in documents. You may use this
    domain in examples without prior coordination or asking for permission.</p>
    <p><a href="http://www.iana.org/domains/example">More information...</a></p>
</div>
</body>
</html>
* Connection #0 to host example.com left intact
Нехило так?
Я выкинул из этого примера определения CSS стилей, ибо вопросы рендеринга HTML в том же браузере тоже оставим до лучших времен.
Начинается все с URL, который является подмножеством URI, и за двойной слеш в котором извинялся сам Тим Бернерс-Ли. Согласно RFC 1738 мы имеем:
  • http — схема, в данном случае сообщающая нам, что доступ к ресурсу должен осуществляться по протоколу HTTP.
  • example.com — хост, его доменное имя, на котором расположен ресурс.
  • / — путь до ресурса на указанном хосте, в данном случае подразумевается корневой каталог, и заметьте, что Curl решил за нас дописать этот необязательный слеш.
Хорошо, нам надо подключиться к хосту по имени example.com. Человекопонятное имя из буковок — это доменное имя. Точнее даже FQDN — Fully Qualified Domain Name. А еще точнее, FQDN в данном случае должно бытьexample.com., с точкой в конце. Доменные имена выстраиваются в иерархию справа налево. И корнем этой иерархии, корневым доменом нулевого уровня, является именно точка.
Доменное имя нужно преобразовать в IP адрес. Потому что наши любимые интернеты все еще работают по Internet Protocol. IP в данном случае именно так расшифровывается, не путать с Intellectual Property, что тоже встречается в буржуйских документах. Протоколов интернета у нас нынче в ходу аж две штуки: четвертой и шестой версии. Пятую версию профукали на совсем другую область применения. IP адрес в IPv4 имеет длину в четыре байта (32 бита), и все адреса уже кончились. Еще бы, ведь в интернетах уже в два раза больше роботов, чем человеков. А вот в IPv6 адрес уже — 128 бит (16 байт), чего, в общем-то, хватит на половину атомов всей Вселенной.
Для преобразования доменных имен в числовые адреса есть DNS. Распределенная база данных, протокол и соответствующие сервера. Адрес ближайшего к вам DNS сервера выдается вам вашим провайдером при подключении к интернетам.
Посмотрим, что думает насчет адреса example.com гугловый DNS.
% dig +noall +answer @8.8.8.8 example.com
example.com.        13169   IN  A   93.184.216.34

% dig +noall +answer @2001:4860:4860::8888 example.com AAAA
example.com.        4398    IN  AAAA    2606:2800:220:1:248:1893:25c8:1946
DNS — это самый обычный протокол. Он сам может работать поверх UDP или TCP. И в том и другом случае сервер может вернуть и IPv4 (тип записи A), и IPv6 (тип записи AAAA) адрес.
Хорошо, IP адрес мы выяснили. Но каким образом пакетики от нашего локалхоста дойдут до сервера где-то там? Тут начинается магия маршрутизации. Каждый компьютер в Сети знает свой адрес. И знает, в какую сеть (подмножество близких адресов) он входит. Узлы, которые занимаются маршрутизацией, подключены к нескольким сетям (и имеют несколько IP адресов). И они знают, в какую дырку отправлять пакеты для каждой сети. Также они знают, что если пакет предназначен для сети, про которую они ничего не знают, его надо переслать вон тому маршрутизатору по умолчанию, он уже разберется, что делать дальше.
Посмотрим, где у нас находится example.com. MTR выдает нам список маршрутизаторов, через которые ходят пакеты, потери и задержки ответов.
% mtr -r example.com
Start: Mon Mar  9 11:44:06 2015
HOST: gelin-zenbook               Loss%   Snt   Last   Avg  Best  Wrst StDev
  1.|-- dynamic-2a02-2698-5422-0.  0.0%    10    1.2   1.9   1.1   4.1   0.8
  2.|-- 2a02:2698:5400::501        0.0%    10   11.7   6.0   2.0  13.0   4.5
  3.|-- 2a02:2698:5400::1e02       0.0%    10   18.3   3.9   1.5  18.3   5.2
  4.|-- ertelekom-ic-306585-sap-b  0.0%    10   51.7  47.2  45.4  51.7   2.4
  5.|-- sap-b2-link.telia.net      0.0%    10   45.7  46.8  45.6  49.4   1.3
  6.|-- nyk-b2-v6.telia.net        0.0%    10  166.1 160.6 154.5 179.0   7.1
  7.|-- edgecast-ic-156944-nyk-b2  0.0%    10  155.0 157.7 154.4 165.2   3.6
  8.|-- 2606:2800:220:1:248:1893: 10.0%    10  156.7 154.8 153.9 157.0   1.0
Ertelecom — это наш Дом.Ru. Telia — это европейский провайдер. EdgeCast — это американский CDN.
Так в Европе или Америке? Судя по задержкам, все же в Европе. Давайте заглянем в другую базу, сведений о регистрации доменных имен и IP адресов.
% whois example.com

Domain Name: EXAMPLE.COM
Registrar: RESERVED-INTERNET ASSIGNED NUMBERS AUTHORITY
Sponsoring Registrar IANA ID: 376
Whois Server: whois.iana.org
Referral URL: http://res-dom.iana.org
Name Server: A.IANA-SERVERS.NET
Name Server: B.IANA-SERVERS.NET
Status: clientDeleteProhibited http://www.icann.org/epp#clientDeleteProhibited
Status: clientTransferProhibited http://www.icann.org/epp#clientTransferProhibited
Status: clientUpdateProhibited http://www.icann.org/epp#clientUpdateProhibited
Updated Date: 14-aug-2014
Creation Date: 14-aug-1995
Expiration Date: 13-aug-2015
Домен принадлежит IANA. Той самой организации, которая занимается регистрацией всякого в интернетах.
А по IP?
% whois 2606:2800:220:1:248:1893:25c8:1946

NetRange:       2606:2800:: - 2606:2800:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF
CIDR:           2606:2800::/32
NetName:        EDGECAST-IPV6-1
NetHandle:      NET6-2606-2800-1
Parent:         NET6-2600 (NET6-2600-1)
NetType:        Direct Allocation
OriginAS:       AS15133
Organization:   EdgeCast Networks, Inc. (EDGEC-1)
RegDate:        2010-05-18
Updated:        2012-03-20
Ref:            http://whois.arin.net/rest/net/NET6-2606-2800-1

OrgName:        EdgeCast Networks, Inc.
OrgId:          EDGEC-1
Address:        2850 Ocean Park Blvd.
Address:        Suite 110
City:           Santa Monica
StateProv:      CA
PostalCode:     90405
Country:        US
RegDate:        2007-03-09
Updated:        2011-11-30
Ref:            http://whois.arin.net/rest/org/EDGEC-1
Опять EdgeCast. Ну и ладно. Заметьте, IPv6 адреса выдают чудовищно громадными диапазонами.
Хорошо, пакетики ходят. А дальше происходит подключение на 80-й порт. Что за подключение? Что за порт? Это уже протокол TCP. IP позволяет лишь передавать пакеты с адреса на адрес через маршрутизаторы. Внутрь пакета IP помещается пакет TCP. Он позволяет передавать поверх, в общем-то, самостоятельных и не связанных друг с другом IP пакетов, потоки байтов. Каждая порция байт пакуется в пакет, снабжается номером и отправляется по сети. Принимающая сторона может переупорядочить пакеты, если они перепутались, и запросить повторную передачу, если пакеты потерялись. С точки зрения приложения, которое использует сокеты, мы просто подключаемся, а затем шлем байты, почти как при записи в файл.
Порт — это лишь условный номер «дырки» на сервере и клиенте, чтобы иметь возможность держать несколько одновременных TCP соединений между данными сервером и клиентом. 16 битное число. Собственно, уникальным идентификатором TCP соединения является четверка: IP адрес сервера, TCP порт сервера, IP адрес клиента, TCP порт клиента. Порт клиента часто выбирается случайно. А порт сервера в случае HTTP — это 80, по стандарту.
Установка TCP соединения — это чудная штука под названием TCP handshake. Рукопожатие. Клиент говорит серверу: «А давай установим соединение?» (SYN). Сервер отвечает клиенту: «Понял. Давай установим соединение.» (SYN, ACK). Клиент отвечает серверу: «Понял. Поехали.» (ACK). Ну и дальше уже идет обмен пакетами с данными.
Дальше работает HTTP. Поверх TCP наш клиент Curl передает запрос:
GET / HTTP/1.1
User-Agent: curl/7.35.0
Host: example.com
Accept: */*
Клиент говорит, что хочет заполучить ресурс (GET), по пути /, используя протокол HTTP версии 1.1. При этом софт клиента (User-Agent) — это Curl версии 7.35.0. Запросить ресурс мы хотим у хоста example.com. На одном и том же сервере может находится несколько виртуальных хостов, и именно этот заголовок говорит, какой именно нужен. Curl готов принять любой тип контента, который предложит сервер (Accept).
Сервер отвечает заголовками:
HTTP/1.1 200 OK
Cache-Control: max-age=604800
Content-Type: text/html
Date: Sun, 08 Mar 2015 09:50:54 GMT
Etag: "359670651"
Expires: Sun, 15 Mar 2015 09:50:54 GMT
Last-Modified: Fri, 09 Aug 2013 23:54:35 GMT
X-Cache: HIT
Content-Length: 1270
Сервер сообщает, что документ он нашел, все хорошо (200 OK). Клиенту разрешается закэшировать этот документ на неделю (604800 секунд). Документ имеет MIME тип text/html, т.е. это текстовый документ в HTML разметке. На сервере сейчас 8 марта. Можно узнать, есть ли новая версия документа, указав Etag. А эта версия документа действительна до 15 марта. И последний раз документ менялся 9 августа 2013 года. Оказывается, мы сходили на сервер не напрямую, а через какой-то прозрачный кэширующий прокси, и этот документ оказался в его кэше (HIT). Длина документа: 1270 байт.
После заголовка идет пустая строка и тело документа. В общем-то, вот и весь HTTP в простейшем виде.
Мы забыли еще один важный этап передачи сетевых пакетов. Нельзя просто так взять и засунуть IP пакет в кабель, воткнутый в ваш компьютер. IP хорошо работает в пределах планеты Земля, а вот в более мелких масштабах его мало. Нужно еще договориться, как вообще передавать сигналы через этот кабель — это физический уровень. И как найти и передать пакеты комьютерам по соседству, в том числе и ближайшему интернет машрутизатору — это канальный уровень. Уровни модели OSI.
Физический уровень весьма разнообразен. Это может быть витая пара и штука под названием CSMA/CD для кодирования данных (впрочем, в гигабитном и быстрее все гораздо сложнее) в случае Ethernet. Это радиоволны и целый ворох не всегда совместимых друг с другом стандартов в случае Wi-Fi. Это телефонная «лапша» и очень хитрые многослойные ассиметричные протоколы и алгоритмы в случае xDSL. Если интересно, в ADSL по одной паре проводов передаются как обычный аналоговый телефонный разговор, так и входящий и исходящий потоки данных, поверх многоуровневого помехозащищенного кодирования с кучей разных параметров передаются ячейки ATM, со своими параметрами, и только потом — фреймы Ethernet. Тот еще ад.
Ну а на канальном уровне весьма часто используются фреймы Ethernet. Здесь каждый узел локальной сети идентифицируется 48-битным MAС адресом, который назначается производителем сетевого устройства. Каждая сетевая карта имеет уникальный MAC. Чтобы начать использовать IP достаточно узнать по какому MAC адресу находится нужный нам IP адрес. Для этого используются вспомогательные протоколы вроде ARP и широковещательные рассылки по локальной сети, вроде: «Эй, у кого тут IP адрес xx.xx.xx.xx?».
Сетевой уровень — это IP. Транспортный уровень — это TCP. А HTTP — это уже какой-то прикладной уровень.
В общем, имеем слои покруче, чем у огров. И хоть упомянутая модель OSI в чистом виде не применяется, она полезна для понимания вложенности протоколов.
  • Приложение просто хочет заполучить ресурс по URL.
  • Вступает в действие слой HTTP, где выясняется имя сервера и формируется запрос.
  • Работает TCP, устанавливается соединение, отсылается HTTP запрос.
  • Работает IP, выясняется уже адрес сервера (DNS запрос где-то сбоку), TCP заворачивается в IP пакеты и отправляется на сервер, а сервер отвечает, и все это через n маршрутизаторов.
  • Работает Ethernet или Wi-Fi, IP пакеты до ближайшего маршрутизатора заворачиваются в кадры Ethernet.
  • Вся эта беда передается по кабелю или по радио.
  • И по каким-то кабелям в каком-то датацентре это доходит и до сервера.
  • А сервер отвечает, может, фреймами Ethernet, а может, и чем-то более экзотическим, но более подходящем для датацентров, но внутри — IP пакеты.
  • И доходят IP пакеты от сервера, через маршрутизаторы датацентра, через весь Интернет, снова до нас.
  • Но в эти IP пакеты вложены пакеты TCP — ответный поток байт от сервера.
  • И этот поток представляет собой HTTP ответ: заголовок и HTML документ.
  • И Curl печатает нам этот документ.
Вот так вот.
P.S. Картинок что-то нет. Потому что Сети — это серьезно ;)