D:\sideБлогPeer-to-peer и WebRTC в частности

⚠️ Обращайте внимание на даты.
Этот блог больше не ведётся с 17 января 2023, и на тот момент с написания этой страницы (28.04.2014) прошло 8 лет.

Если вы это читаете, то скорее всего играли в онлайн-игру хотя бы однажды. И если не по этой, то по другой причине, знаете, что в наше время большинство применений сети выполнено по шаблону “клиент-сервер”. Все коммуникации и тяжёлую работу выполняет сервер, а клиент занимается только общением с ним. В случае с играми объяснение вполне простое — важно, чтобы все игроки были равны (а некоторые “чуть равнее”). Чаще всего удобно считать, что клиент в любой момент может быть разобран, расшифрован и использован против вас, поэтому и планировать его так, чтобы никаких вредоносных действий с него было выполнить в принципе нельзя.

Если идти в чуть более общие дали — на клиент-серверной архитектуре построены самые используемые службы интернета, за редким исключением. В случае с играми понятно, из-за чего. Но туда же добавляются мессенджеры (ICQ?), браузеры (это клиенты для веб-серверов, внезапно), службы файлообмена (всякие диски, дропбоксы и драйвы). Чем удобны такие службы? Их проще разрабатывать и держать под контролем. Но есть задачи, где это не очень важно.

Разумеется, это не единственный вариант. Но если объяснять на пальцах, то альтернатива — архитектура клиент-клиент. Но здесь есть неувязочка — клиент бывает только чиьм-то. А здесь он сам по себе, сам (?) связывается с другими клиентами. Так что у нас это не клиент, а пользователь. Такая архитектура называется peer-to-peer или коротко p2p. Основная её особенность — сама работа приложения происходит по соединениям не с крупной центральной машиной (принадлежащей часто фирме-разработчику приложения), а непосредственно с другим пользователем.

Много ли таких приложений? Вы удивитесь — полно.

Это верхушка айсберга, о которой слышали, пожалуй, все.

Как это реализуется? Основных способов два. И использовать, как правило, приходится старый-добрый клиент-сервер. С той лишь разницей, что использоваться он будет только для установления соединения, после чего он не участвует в отношениях между клиентами. За одним исключением, о котором расскажу дальше.

Но сначала о более простом. Есть такая штука, как UDP. Это сетевой протокол, довольно низкого уровня. Для сравнения: HTTP работает под протоколом TCP, альтернативой (в каком-то смысле) которому является UDP. И одна из особенностей UDP — доставка с его помощью сообщений не гарантируется. Это как Почта России — отправил и думаешь, дойдёт или нет (это единственное, чем UDP на неё похож, но…). Эта особенность лежит под интересной фичей сети — широковещанием. Клиент может отправить сообщение на специальный адрес, и маршрутизирующие средства раскидают по экземпляру такого сообщения всем в ближайшей подсети (т. е. если у вашего компьютера адрес 192.168.11.25, сообщение получат все, у кого адрес похож на 192.168.11.*). Никто не гарантирует, что оно вообще кому-нибудь придёт. Но если кто-нибудь такое сообщение ждёт, он может узнать адрес того, кто сообщение прислал (отправитель всё-таки подписывается). Так клиенты могут образовать сеть (уровня приложения) между собой без всяких серверов. Проблема в том, что пределы подсети они скорее всего не покинут.

Поэтому появилось то, чем вы наверняка пользовались хоть раз у торрентов — трекеры. Они собирают IP-адреса тех, кто уже был на торренте и скачивал некоторую раздачу, и раздают эти адреса другим. Но и это не панацея, этого часто недостаточно для полноценной двухсторонней связи. Дело вот в чём: многие компьютеры выходят в сеть не сами, а при помощи некоторого шлюза. Поэтому они сами не имеют доступного в интернете IP-адреса, его имеет только шлюз. Такая ситуация требует NAT: трансляции сетевых адресов. Поэтому часты ситуации, когда из двух клиентов только один видит другого. В этом случае используется “пассивный режим”. Один клиент подключается к другому, как к серверу. За удержание соединения отвечает клиент с адресом, а передача пакетов обратно производится по установленному постоянному соединению.

Так вернёмся к NAT, это же можно как-то использовать. Шлюзы могут по запросу выделить какой-нибудь порт на своём общедоступном адресе, и прикидываться, что там не шлюз, а клиент этого шлюза. Порт на шлюзе и на клиенте зачастую не совпадает, поэтому для установления обратной связи пользоваться данными, посланными только клиентом, нельзя — он просто не знает всех деталей о соединении. Другой клиент может постучаться на порт, который он ожидает, и получить отказ, потому что там никого нет. В этом случае клиенты всё ещё могут установить соединение без посторонней помощи — один обращается к другому, клиент под шлюзом общается как обычно, а клиент без шлюза смотрит, откуда идут сообщения, и их запоминает.

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

…и здесь я, пожалуй, начну рассказ о WebRTC. Если вы ещё не догадались, он непосредственно связан с peer-to-peer-приложениями. Для таких ситуаций WebRTC использует сторонний “сигнальный сервер” STUN. Выглядит его использование примерно следующим образом: два клиента обращаются к STUN-серверу, указав одну и ту же группу. STUN понимает, что их надо соединить между собой и обеспечивает пересылку их пакетов с данными о соединении. Так каждый из двух клиентов узнаёт о своём собеседнике и может установить прямое соединение, несмотря на наличие шлюзов, искажающих стандартные данные о соединении.

Но и этого может быть недостаточно! Изредка бывают ситуации, когда прямое соединение установить просто невозможно. Например, сетевая структура не позволяет некоторые виды связи, если интернет, скажем, проложен через прокси. В этом случае используется наружный прокси-сервер TURN, который прокачивает пакеты через себя, обеспечивая соединение. При этом архитектура peer-to-peer становится по сути идентичной клиент-серверной, и отличий вроде как уже и нет.

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

Но вернёмся к WebRTC. Совсем строго говоря, WebRTC не предписывает, как клиенты должны найти друг друга. Но чтобы это было проще сделать, можно использовать ICE, в который входят клиенты STUN и TURN. В общем случае WebRTC — это (сравнительно) новый стандарт обмена данными между браузерами в реальном времени. И самым явным кандидатом на роль данных стали аудио-видео потоки. Чтобы реализовать это, пришлось добавить в браузеры возможность тянуть данные с вебкамеры и микрофона.

Как же жили, когда этого не было? Тут два варианта. Первый — вне браузера, у обычной программы особенных ограничений на взаимодействие с системой нет. Второй вариант — использовать плагин к браузеру. Flash годится и используется до сих пор (в том числе мной, косвенно), когда-то ещё я видел реализацию на Java. Давно дело было, Java-апплеты уже давно перестали быть популярными в браузерах.

Что дал WebRTC? Возможность обмениваться аудио и видео между двумя произвольными клиентами без необходимости ставить дополнительное ПО — Chrome, Firefox или Opera в наше время можно найти почти везде. Идентичную функцию исполняет скайп, но его надо устанавливать, зато WebRTC не позволяет обмениваться файлами и сообщениями… или позволяет?

Вообще-то позволяет, но практика показывает, что файлы и сообщения из точки в точку лучше передавать через центральный узел. Если вы пользовались сообщениями Skype (или пересылкой файлов, которая работает так же), то знаете о спецэффектах его чата. К примеру, если один из собеседников не соединён с другим, то отправлять сообщения клиент не может, и вынужден их запоминать до момента, пока собеседник выйдет на связь. В итоге если собеседники выходят в сеть попеременно, сообщения никогда не будут доставлены. А уж какое веселье начинается, когда скайп запускается с нескольких устройств, ух-х… Разумеется, я должен упомянуть и о плюсах. Приватность. Соединение между клиентами может где-либо сохраняться разве что в виде шифрованного трафика, который часто близок к бесполезному, в то время как у центрального узла можно найти уязвимость и слить базу данных, где найти массу интересного. Теоретически.

WebRTC можно заставить передавать произвольные данные, делается это через DataConnection, и с его помощью иногда делают потрясающие вещи. Например, файлообменник, в котором отправитель делает файл доступным, а все желающие по ссылке загружают файл прямо у него (у отправителя, а не с сервера). Или сеть, которая занимается раздачей тяжёлых ресурсов сайта на манер торрента (были на Пикабу, видели “длиннопосты”?). И разумеется, на этом можно собрать чат, хотя это довольно примитивно и имеет свои минусы, изложенные выше.

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

Сейчас я пытаюсь заставить эту технологию работать в нужном мне направлении. Поскольку она довольно новая, это не очень-то и просто, над документацией проделано немало работы, но всё же недостаточно — документации определённо меньше, чем по… Rails, например. Но поскольку мне это понадобится на работе, можно ожидать, что я в скором времени вывешу прямо здесь (это же статический сайт, помните?) какой-нибудь примерчик, использующий общедоступный STUN. Будет весело!