Сетевые протоколы

Network Protocols

Internet зарождался в недрах американских университетов начиная еще с 60-х годов прошлого века как исследование по заказу Министерства обороны США. Но та сеть, которую мы знаем сегодня, появилась в 80-х, после стандартизации таких протоколов как TCP/IP.

Разработка сетевых приложений достигла такого высокого уровня, что порой отсутствие даже базовых знаний о работе сети никак не мешает создавать веб и мобильные приложения. Но на самом деле, недостаточное или неполное понимание протекающих процессов не только станут преградой при решении внезапно возникшей проблемы, но и могут привести к серьезным ошибкам, допущенным при разработке архитектуры вашего приложения или системы. А в некоторых ситуациях отсутствие таковых знаний может привести к ситуации, когда разработчик не в состоянии выработать хоть какое-либо решение поставленной задачи.

Разбираться во всех протоколах и принципах, стоящих в основе сегодняшней сети Internet, - это непростая задача, но нам как разработчикам и не требуется знать на память все тонкости и нюансы, достаточно понимать основные механизмы.

Давайте поставим себе за цель разобраться, что происходит когда мы вбиваем в браузере адрес http://interlink-ua.com и, собственно, жмем Enter. Т.е. мы попытаемся поверхностно рассмотреть основные шаги, которые приводят к тому, что в итоге мы видим страничку нашей компании у себя в браузере.

Ethernet

Рассказ мы начнем почти с самых низов. Мы не будем рассматривать различные варианты передачи сигнала, будь то электричество, свет или радиоволны. Мы для простоты возьмем за пример самый распространенный вариант, который все из нас хоть раз но использовали, - это передача электрических сигналов по технологии Ethernet. Ethernet определяет разные проводные соединения (коаксиальный кабель, витая пара, оптоволоконный), опять же, самое привычное для нас - это витая пара.

Изначальная идея Ethernet напоминает радио, т.е. когда один хост вещает, все остальные его слышат. Если представить, что вы соединены некими трубками, то если бы ты говорил в свою трубку, то все бы тебя слышали на своих концах. Сейчас, конечно же, используются оптимизации этого процесса с помощью таких устройств как коммутатор или “свитч” (switch), или как еще раньше их называли на заре их появления - smart hub. Свитч просто оптимизирует распространение сообщений так, чтобы только адресат получал его, так повышается эффективность сети и появляется дополнительная безопасность. Но, сегодня нам не столь важны такие моменты, поэтому, опять же, для простоты мы представим, что у нас обычный хаб и все слышат друг друга.

Итак, Ethernet определяет как передавать сигнал на физическом уровне и задает понятие пакета информации - Ethernet frame. Поскольку нельзя передавать свое сообщение, пока это делает кто-то другой, введены ограничения на время использования среды вещания, что приводит к тому, что ethernet frame обычно достаточно мал, чаще всего в локальных сетях используются пакеты размером 1522 байт, что является максимум по стандарту Ethernet и позволяет передавать 1500 байт полезной нагрузки (так называемый payload), но в особых случаях применяются так называемые Jumbo-frames, которые позволяют передавать до 16 000 байт payload.

Если мы представим Ethernet frame как обычное письмо, то у него должны быть адреса отправителя и получателя. В Ethernet такой адрес - это 6 байт, который называется MAC address (Media Access Control) или, как его еще называют, Hardware Address. Ethernet предполагает, что все устройства-участники сети имеют уникальный адрес. Первые 3 байта выделены для кода производителя устройства. Вон посмотрите на свои А4, там у вас указаны ваши MAC адреса, т.е. в нашем случае устройствами-участниками являются сетевые карты на ваших компьютерах.

Чтобы (вот, к примеру, ты с браузером) отправить сообщение кому-либо в твоей локальной сети ты на конверте пишешь свой MAC адрес и MAC адрес получателя, а в сам конверт вкладываешь свое сообщение.

То, что мы пишем на письме, служебная информация, адреса, различные статусы и состояния, т.е. все то, что не есть само сообщение - это обычно называется заголовком (header) в любом протоколе. В целом все, что мы передаем, это просто какая-то последовательность байтов, это с письмом нам все понятно: снаружи - адреса, внутри - сообщение; а здесь получатель просто знает, что по стандарту первые N байт - это заголовок, и он ожидает соответствующее оформление заголовка, к примеру в Ethernet frame первые 6 байт - это адрес получателя, а следующие 6 байт - адрес отправителя.

IP

Хорошо, в одной локальной сети мы научились передавать сообщения, но как же нам достучаться в ту другую сеть к нашему серверу. Вот такой вопрос когда-то и возник, т.е. возникла необходимость собрать все существующие локальные сети в одну большую - Internet.

Так почему бы не собрать все Ethernet сети в одну большую Ethernet сеть? Тут вон даже адресация предполагает много участников, аж 6 байт под адрес выделено. Но Ethernet создавался для стандартизации передачи в физической среде, он не создавался для глобальной сети. И вспомните, что один вещает - остальные слушают, т.е. Представьте как все миллиарды хостов в Internet будут слушать одного :) Одну Ethernet сеть еще называют доменом коллизий, это когда больше чем один хост вещают одновременно, такое случается и стандарт описывает поведение хостов, чтобы выйти из этой ситуации. Плюс, Ethernet не рассчитан на большие физические расстояния между хостами, ибо затухание сигнала и задержки доставки до отдаленных хостов будут приводить к еще большим коллизиям и проблемам работы в сети.

В связи с чем мы пришли к новому протоколу для создания глобальной сети, его так и назвали - Internet Protocol (межсетевой протокол, для связи между разными сетями). Этот протокол абстрагируется от канального уровня, т.е. как именно вы передаете сигнал в каждой локальной сети, будь то Ethernet или другой формат. Его задача уникально идентифицировать любой хост в глобальной сети и заложить принципы маршрутизации пакетов от точки А до точки Б.

Итак, этот протокол вводит новую адресацию, все мы знаем это под именем IP Address. Этот прокол уже давно имеет вторую версию - IPv6, первая теперь называется IPv4. Мы для простоты будем разбирать первую версию, вторая появилась для решения проблемы с опустошением пула адресов (да, их уже начинает не хватать), решения других проблем, которые присутствуют в IPv4, дополнение новшествами и т.п. Итого, в IPv4 адрес - это 4 байта, обычно мы видим представление этих байт в виде четырех десятичных чисел, к примеру 192.168.0.1.

Давайте вернемся к нашему серверу и клиенту, видите, у каждого из них свой IP адрес, но как же нам передать IP пакет серверу, если IP возлагает вопросы передачи данных в конкретной локальной сети на другие протоколы? В нашем случае мы продолжим использовать Ethernet для передачи данных в каждой сети, но должна быть какая-то связь между нашими двумя сетями. Для этого появляются особые участники сети - маршрутизаторы. Основная задача маршрутизатора - это при получении IP пакета, в котором он не являются получателем, передать его дальше на другой маршрутизатор, который лучше знает куда пакету идти дальше, чтобы в итоге попасть в точку назначения, в нашем случае сервер interlink-ua.com. Маршрутизаторы, конечно же, не угадывают маршруты, маршруты прописываются статически или такая информация динамически производится и обновляется в сети.

Да, но это как-то не особо помогло нам, да? На самом деле, при работе с IP каждый хост имеет свою таблицу маршрутизации. Естественно, что нам не надо хранить огромную таблицу всех возможных маршрутов, вот, к примеру, в нашем случае, хост с браузером имеет достаточно простую таблицу, в которой сказано, что все адреса, начинающиеся на 1.0.0 являются локальными и с ними он может общаться напрямую без какого-либо маршрутизатора, а за любым другим адресом ему надо обращаться к так называемому маршрутизатору по умолчанию, который открывает этому хосту пути к любому другому хосту в Internet, ну, или не открывает, если что-то запрещено или не подключено :)

Вот ты смотришь, что тебе надо отправить сообщение на сервер с адресом 2.0.0.2, ты смотришь в свою таблицу маршрутизации и видишь, что для связи с этим хостом тебе надо отправлять это сообщение на маршрутизатор с адресом 1.0.0.1. Вроде как все понятно, но, как вы помните, для передачи в этой локальной сети мы используем Ethernet, и у него своя адресация - MAC адреса. Чтобы отправить Ethernet frame нашему маршрутизатору нам сперва надо узнать его MAC адрес.

И тут в игру вступает специальный протокол - ARP, Address Resolution Protocol. Суть его - это спросить у всех участников Ethernet сети: “Какой MAC адрес у хоста с таким-то IP?”. Для этого отправляется широковещательный Ethernet frame, в таком случае адрес получателя устанавливается в FF:FF:FF:FF:FF:FF, таково соглашение по стандарту. В итоге все, кто получают такой frame являются получателями и они будут читать содержание, в котором находится ARP пакет с соответствующим вопросом. Вот ты отправляешь такой пакет всем в своей сети. Вот все читают твое сообщение и не знают чем ответить, т.е. они по сути игнорируют его, а вот наш маршрутизатор откликается, ибо он сам и есть 1.0.0.1, о котором мы спрашиваем. Вот он шлет ответный frame, внутри которого содержится ARP ответ с искомым MAC адресом. Для того, чтобы каждый раз не задаваться вопросом у кого какой MAC адрес хосты обычно кешируют эту информацию, правда, ненадолго, ибо MAC адреса могут меняться и т.п.

Вот, ты теперь можешь собрать свое IP сообщение в Ethernet frame и отправить это маршрутизатору. Маршрутизатор, в свою очередь, при получении твоего frame извлекает из него IP пакет и увидит, что сообщение не адресовано ему, но изучив свою таблицу маршрутизации он видит, что получатель находится в локальной сети, в которой находится сам же маршрутизатор, т.е. он не будет передавать этот IP пакет дальше на другой маршрутизатор, он сразу же отправит его конечному адресату. Ага, тут нам опять надо сначала использовать ARP запрос для определения MAC адреса, если он отстутствует в кеше маршрутизатора, и с помощью нового Ethernet frame отправить оригинальный IP пакет, который слал 1.0.0.3.

TCP

Достучаться то достучались, но теперь возникают вопросы гарантии доставки сообщений. Вот, ты, в курсе, получил ли сервер вообще твое сообщение? Получил ли он его в том же виде, что ты и отправлял? Может были где-то помехи в передаче и сообщение исказилось? Также возникают и другие вопросы, к примеру, ну получил сервер сообщение, а для какой именно программы оно адресовалось? Ведь, как мы знаем, на одном компьютере может работать несколько сетевых приложений (вспомните свой браузер, скайп, почтовик и т.п. работающие одновременно). Как минимум эти два вопроса решает совсем другой протокол - TCP, Transmission Control Protocol.

Это достаточно сложный протокол, но нам не надо вдаваться во все детали, чтобы понять основные принципы его работы. Основная задача этого протокола - это гарантировать, что цепочка байтов, которую вы отправляете другому хосту будет принята на той стороне в точности и в той же последовательности. Если случается ошибка передачи или потеря пакетов, то такие “кусочки” отправляются повторно.

А для определения программы-отправителя и получателя используется специальная адресация - это TCP port. Это целое число, под которое в протоколе TCP выделено два байта, т.е. номер порта варьируется от 0 до 65535. Вы, возможно, встречали или слышали такие порты как: 80, 8080, 443. Многие приложения используют свой стандартный порт по умолчанию, хотя порт можно изменить, к примеру, HTTP сервер по умолчанию использует порт 80, а если соединение предполагает использование TLS для шифрования, то по умолчанию используется порт 443.

Прежде, чем вы начнете передачу информации вам надо создать соединение (connection). Для этого происходит обмен тремя пакетами - так называемый three-way handshake. В заголовке TCP пакета отведено место под флаги, которые подчеркивают некую особенность, вот некоторые из них: SYN (synchronize), FIN (finalize), ACK (acknowledge). Итак, три начальных пакета таковы: от клиента идет SYN пакет, от севера идет ACK + SYN, и от клиента ACK, т.е. каждая сторона подтверждает SYN другой стороны своим пакетом с ACK флагом.

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

Вот, теперь у нас есть способ, который гарантирует доставку сообщений.

HTTP

Ну, а в нашем случае само сообщение - это запрос или ответ по протоколу HTTP. Сам этот протокол достаточно стар и является текстовым, как и многие другие старые протоколы. HTTP запрос представляет собой набор строк, где сначала идет request line, потом строки-заголовки, затем идет разделитель в виде пустой строки, а затем идет тело запроса, если оно имеется. HTTP response в том же духе начинается со status line, продолжается строками-заголовками, и через разделитель в виде пустой строки идет тело ответа.

DNS

А теперь у нас остался один нерешенный момент. Как вы помните, по нашей задаче мы вбиваем некий адрес в браузер, мы же не вбиваем туда IP адрес, что возможно, но для человека не свойственно помнить числовые адреса всех интересных ему сайтов в Интернет, да и представьте себе бигбоард с рекламой, где красуется что-то аля http://1.2.3.4/ - вызывает смех, да? :) Но в любом случае, как вы уже видите, для того, чтобы отправить запрос на веб сервер, нам надо знать его IP адрес. В связи с этими вопросами и появилась система доменных имен DNS (Domain Name System), которая позволяет дать IP адресам легко запоминающиеся имена-клички, коих может быть больше чем один на один IP адрес.

Для работы этой системы нужны DNS сервера, с которыми мы взаимодействуем по специальному DNS протоколу, который позволяет послать запрос типа “А какой IP адрес стоит за доменным именем interlink-ua.com?”, на что сервер нам отвечает, указывая соответствующий IP адрес. Вот давайте посмотрим на эти шаблоны запросов на А4.

Так что, прежде чем наш браузер начнет как-то связываться с сервером, ему сначала надо узнать его IP адрес, т.е. в первую очередь хост обратится к одному из DNS серверов.

Сам DNS протокол, также как и HTTP, не занимается вопросами доставки сообщений, в итоге DNS используют поверх TCP или UDP, чаще встречается использование UDP, ибо этот протокол передачи данных не гарантирует доставку сообщений, что приводит к упрощению взаимодействия, меньшему количеству переданных служебных байт и, соответственно, меньшим задержкам. В UDP, также как и в TCP, имеется своя адресация - UDP port, что позволяет определить какое именно приложение отправляет и, соответственно, получает сообщение.

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

Last updated