Что лучше использовать при разработке софта — нативные или веб-технологии? Холивар по этому поводу закончится ещё не скоро, но мало кто станет спорить, что нативные функции полезно продублировать для использования в браузерах или WebView. И если когда-то приложения для звонков существовали исключительно отдельно от браузера, то теперь их легко реализовать и в вебе. Разработчик Григорий Кузнецов объяснил, как пользоваться технологией WebRTC для P2P-соединений.


— Как вы все знаете, в последнее время появляется довольно много приложений, в основу которых заложен прямой обмен данными между двумя браузерами, то есть P2P. Это всевозможные мессенджеры, чаты, звонилки, видеоконференции. Также это могут быть приложения, которые производят какие-то распределенные вычисления. Пределы фантазии никак не ограничиваются.

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

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

В первую очередь — старая умирающая технология Adobe Flash. Она действительно умирает, и компания Adobe прекратит ее поддержку уже в 2020 году. Технология действительно позволит вам получить доступ к вашим медиаустройствам, внутри нее вы сможете реализовать всю необходимую механику, чтобы помочь браузерам соединиться, чтобы они начали передавать информацию P2P, но вы опять изобретете свой велосипед, потому что нет единого стандарта, единого подхода к реализации данного способа передачи данных.

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

И есть технология WebRTC — с помощью нее работают Google Hangouts, Facebook Messenger. Компания Voximplant использует ее, чтобы вы могли совершать свои звонки. Давайте на ней остановимся подробнее. Это новая развивающаяся технология, она появилась в 2011 году и продолжает развиваться. Что же она позволяет делать? Получить доступ к камере и микрофону. Установить P2P-соединение между двумя компьютерами, двумя браузерами. Естественно, она позволяет передавать медиапотоки в режиме реального времени. Кроме того, она позволяет передавать информацию, то есть любую бинарную дату вы тоже можете передавать P2P, можете сделать свою систему распределенных вычислений.

Важный момент: WebRTC не предоставляет браузерам способ найти друг друга. Мы можем сформировать всю необходимую метаинформацию о нас любимых, но как одному браузеру узнать о существовании другого? Как их соединить? Рассмотрим пример.



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

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

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

Сигнальный сервер по этому идентификатору устанавливает, какому именно клиенту нужно переадресовать нашу метаинформацию, и пересылает ее. Второй клиент берет ее, использует, устанавливает себе, формирует ответ, и с помощью сигнального механизма пересылает ее на сигнальный сервер, тот в свою очередь ретранслирует ее первому клиенту. Таким образом оба клиента в данный момент обладают всей необходимой датой и метаинформацией, чтобы установить P2P-соединение. Готово.

Давайте чуть подробнее рассмотрим, чем же именно обмениваются клиенты, они обмениваются SDP-датаграммой, Session Description Protocol.



Это, по сути, текстовый файл, который содержит всю необходимую информацию, чтобы установить соединение. Там есть информация об IP-адресе, о портах, которые используются, о том, какая именно информация гоняется между клиентами, что это такое — аудио, видео, какие кодеки используются. Все, что нам необходимо, там есть.

Обратите внимание на вторую строчку. Там указан IP-адрес клиента, 192.168.0.15. Очевидно, это IP-адрес компьютера, который находится в какой-то локальной сети. Если у нас два компьютера, каждый из которых находится в локальной сети, каждый из которых знает свой IP-адрес в рамках этой сети, хотят созвониться. Получится ли у них сделать это с такой датаграммой? Очевидно, нет, они же не знают внешних IP-адресов. Как быть?



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



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

Как жить, если есть NAT, если компьютеры торчат наружу под одним IP-адресом, а внутри знают о друг друге по другим?

На помощь приходит фреймфорк ICE — Internet Connectivity Establishment. Он описывает способы обхода NAT, способы установки соединения, если у нас есть NAT.

Этот фреймворк использует приписывание так называемого STUN-сервера.



Это такой специальный сервер, обращаясь к которому, вы можете узнать свой внешний IP-адрес. Таким образом, в процессе установки P2P соединения, каждый из клиентов должен сделать по запросу к этому STUN-серверу, чтобы узнать свой IP-адрес, и сформировать дополнительную информацию, IceCandidate, и с помощью сигнального механизма также этим IceCandidate обменяться. Тогда клиенты будут знать друг о друге с правильными IP-адресами, и смогут установить P2P соединение.

Однако бывают более сложные случаи. Например, когда компьютер скрыт за двойным NAT. В этом случае фреймворк ICE предписывает использование TURN-сервера.



Это такой специальный сервер, который превращает соединение клиент-клиент, P2P, в соединение клиент-сервер-клиент, то есть выступает в роли ретранслятора. Хорошая новость для разработчиков в том, что независимо от того, по какому из трех сценариев пошла установка соединения, находимся ли мы в локальной сети, нужно ли обратиться к STUN- или TURN-серверу, АPI-технология для нас будет идентичной. Мы просто в начале указываем конфигурацию ICE- и TURN-серверов, указываем, как к ним обратиться, и после этого технология делает для нас все под капотом.



Небольшое резюме. Чтобы установить соединение, нужно выбрать и реализовать некий механизм сигнализации, некого посредника, который будет помогать нам пересылать метаинформацию. WebRTC даст нам всю необходимую мету для этого.

Нам предстоит побороться с NAT, это наш главный враг на этом этапе. Но чтобы его обойти, используем STUN-сервер, чтобы узнать свой внешний IP-адрес, и TURN-сервер используем в качестве ретранслятора.

Что же именно мы передаем? Про медиа-потоки.



Медиапотоки — это такие каналы, которые внутри себя содержат треки. Треки внутри медиа-потока синхронизированы. Аудио и видео не будет расходиться, они будут идти с единым таймингом. Вы можете внутри медиапотока сделать любое количество треков, треками можно управлять по отдельности, например, вы можете приглушить аудио, оставив только картинку. Также вы можете передавать любое количество медиа-потоков, что позволяет вам, например, реализовать конференцию.

Как же получить доступ к медиа из браузера? Поговорим про API.



Есть метод getUserMedia, который принимает на вход набор констрейнтов. Это специальный объект, где вы указываете, к каким именно устройствам вы хотите получить доступ, к какой именно камере, к какому микрофону. Указываете характеристики, которые хотите иметь, какое именно разрешение, и есть также два аргумента — successCallback и errorCallback, который вызывается в случае успеха или неудачи. В более современных реализациях технологии используются промисы.

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



Центральным объектом в API служит RTCPeerConnection. Когда мы выполняем соединение, то берем класс RTCPeerConnection, который возвращает объект peerConnection. В качестве конфигурации мы указываем набор ICE-серверов, то есть STUN- и TURN-серверов, к которым мы будем обращаться в процессе установки. И есть важный ивент onicecandidate, который триггерится каждый раз, когда нам нужна помощь нашего сигнального механизма. То есть технология WebRTC сделала запрос, например, к STUN-серверу, мы узнали свой внешний IP-адрес, появился новый сформированный ICECandidate, и нам нужно переслать его с помощью стороннего механизма, ивент стриггерился.



Когда мы устанавливаем соединение и хотим проинициализировать вызов, мы используем метод createOffer(), чтобы сформировать начальную SDP, offer SDP, ту самую мета-информацию, которую нужно переслать партнеру.

Чтобы установить ее в PeerConnection, мы используем метод setLocalDescription(). Собеседник получает эту информацию по сигнальному механизму, устанавливает ее себе с помощью метода setRemoteDescription() и формирует ответ с помощью метода createAnswer(), который также с помощью сигнального механизма пересылается первому клиенту.



Когда мы получили доступ к медиа, получили медиапоток, мы его передаем в наше P2P-соединение с помощью метода addStream, а наш собеседник узнает об этом, у него стриггерится ивент onaddstream. Он получит наш поток и сможет его отобразить.



Также вы можете работать с дата-потоками. Очень похоже на формирование обычного peerConnection, просто указываете RtpDataChannels: true и вызываете метод createDataChannel(). Подробно на этом останавливаться не буду, потому что такая работа очень похожа на работу с веб-сокетами.

Пару слов о безопасности. WebRTC работает только по HTTPS, ваш сайт должен быть подписан сертификатом. Медиапотоки тоже шифруются, используется DTLS. Технология не требует установки ничего дополнительного, никаких плагинов, и это хорошо. И не получится сделать шпионское приложение, сайт не будет подслушивать или подсматривать за пользователем, он покажет пользователю специальный промт, запросит у него доступ и получит его, только если пользователь разрешит доступ к аудио- и медиаустройствам.



Что касается поддержки браузеров — IE остается и останется красным. В конце прошлого года добавилась поддержка Safari, то есть уже все современные браузеры умеют работать с этой технологией и мы можем смело ее использовать.

Хочу поделиться набором всяких полезностей, которые помогут вам, если вы желаете работать с WebRTC. В первую очередь это adapter. Технологии все время развиваются, и есть разница в браузерных API. Библиотека adapter нивелирует эту разницу и облегчает работу. Удобная библиотека для работы с дата-потоками — Peerjs. Также можете посмотреть на открытые реализации STUN- и TURN-сервера. Большой набор туториалов, примеров, статей находится на страничке awesome-webrtc, очень рекомендую.

Последняя незаменимая при дебаге полезность — webrtc-internals. Во время разработки вы можете в адресной строке набрать специальную команду — например, в браузере Chrome это Chrome://webrtc-internals. У вас откроется страница со всей информацией о вашем текущем WebRTC-соединении. Там будут и последовательности вызовов в методах, и все датаграммы, которыми обмениваются браузеры, и графики, которые каким-то характеризуют образом ваше соединение. В общем, там будет вся информация, которая понадобится при дебаге и разработке. Спасибо за внимание.

Комментарии (28)


  1. nitro2005
    12.08.2018 19:26
    +1

    > Тогда клиенты будут знать друг о друге с правильными IP-адресами, и смогут установить P2P соединение.

    = не очень понятно, как все-таки предлагается установить р2р соединение между двумя клиентами за NAT (без ретранслятора). Ну знаете вы их внешние IP-адреса, дальше что? Внешние порты, которые будут назначены при NAT, заранее все равно неизвестны.


    1. korniltsev
      12.08.2018 23:06

      Внешний назначенный ip + порт stun отправляет обратным пакетом в сторону клиента, в последствии на этот же порт отправляются данные от других клиентов. Выглядит это приблизительно так:
      1. клиент отправляет пакет стану c целью «пробить дырку» в нате и узнать свой порт + ip
      2. стан получает пакет, и отправляет реальный внешний порт + ip обратно.
      3. клиент получает свои пробитые порт + ip (реальные) формирует ice кандидата и отправляет кандидата через сигналинг другому пиру


      1. nitro2005
        12.08.2018 23:30

        Проблема в том, что этот определенный STUN-ом внешний порт будет действителен только для обмена между клиентом и STUN-сервером. Когда это все пойдет сигнальному серверу или противоположному пиру, внешний порт будет уже другим. Обычно же сейчас в conntrack сессия идентифицируется 4 значениями — src IP+port, dst IP+port…
        Более того, в больших NAT-ах и внешний IP также может выдаваться разный, из пула…


        1. Chupaka
          13.08.2018 10:56
          +1

          Если говорить о Linux — то да, нужны будут дополнительные костыли. Во всяких Cisco включается Full Cone NAT — и пакеты уже с любого IP на ваш внешний IP:Port (в варианте Port-Restricted) либо просто IP пробрасываются вам внутрь.


          1. nitro2005
            13.08.2018 10:59

            Понятно, спасибо! Т.е. как ни крути нужен определенный более мягкий тип NAT…


        1. ivan386
          13.08.2018 12:12

          Не знаю какой у моего провайдера NAT но похоже что для всех UDP пакетов с одного порта он выдаёт один и тот-же внешний порт вне зависимости от адреса назначения. Более того. Это тот же порт который я задал в клиенте.


          Попробовал сменить порт на более популярный. Результат тот же.


    1. ReklatsMasters
      12.08.2018 23:06

      Если NAT назначит не тот же порт, что и клиент, то пакеты не дойдут и вывалится ошибка по таймауту. И будет предложено использовать turn сервер. Если не ошибаюсь, оно так работает.


    1. Hixon10
      13.08.2018 00:42

      Есть группа способов, как обеспечить p2p соединение без транслятора:
      1) UPnP и NAT-PMP.
      2) en.wikipedia.org/wiki/UDP_hole_punching
      3) en.wikipedia.org/wiki/TCP_hole_punching


  1. grey_rat
    12.08.2018 20:57

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


    1. ivan386
      12.08.2018 21:00

      Почему?


      1. grey_rat
        12.08.2018 21:17

        Потому что есть более удобные способы.


        1. ivan386
          12.08.2018 21:26

          С просмотром видео из интернета в браузере тоже раньше были проблемы но доработали и теперь это удобней скачивания или просмотра в отдельном плеере.


          Думаю и эту технологию доработают до уровня когда ей приятно будет пользоваться.


          1. grey_rat
            12.08.2018 21:44

            Тут если доработают, то всё равно заблокируют, так как должен быть контроль со стороны спецслужб, а в P2P такого нет. Приятно ей будет пользоваться, когда она станет доступна и в оффлайн режиме (когда один из собеседников случайно выйдет и передаваемая инфа сохранится на стороннем сервере), а это уже участие третьей стороны.
            А видео из торрентов или DC в браузере так же можно было бы пускать, если б сделали, например с начала воспроизведения последовательно шла загрузка с сервера или самого быстрого пира (с выделением P2P-клиентом определённой полосы пропускания, что бы просмотр не опережал загрузку), а с конца записи и в начало с P2P пиров. После встречи этих двух кусков, просмотр переключался на загруженное с P2P.


            1. igordata
              13.08.2018 02:57

              Тут если доработают, то всё равно заблокируют, так как должен быть контроль со стороны спецслужб, а в P2P такого нет.
              ой, всё


        1. apapacy
          12.08.2018 23:46
          +1

          Какие способы Вы имеете в виду?


          1. grey_rat
            13.08.2018 10:28

            Например обычный телефон и/или вайбер.


  1. BugM
    12.08.2018 22:24
    +1

    А где опыт использования? В заголовке обещали.


    Обычное описание webrtc, которое есть на миллионе других сайтов. И при том не самое лучшее. Вот нафига оно нужно?


  1. ReklatsMasters
    12.08.2018 23:03
    +2

    Не могу не поделиться своей реализацией STUN на nodejs https://github.com/nodertc/stun. Там готовы примитивы и для создания TURN сервера. Он будет реализован после DTLS, который, кстати, тоже тут https://github.com/nodertc/dtls. Всё на чистом js, никаких нативных зависимостей.


  1. igordata
    13.08.2018 02:59

    Leono А Яндекс сейчас использует WebRTC где-то или пока только эксперименты?


  1. cy-ernado
    13.08.2018 08:12

    Тоже хочу поделиться своей имплементацией STUN, но уже на go:
    gortc/stun (сам протокол, клиент с поддержкой RTO, хорошо покрыт тестами, включая end-to-end и fuzzing), gortc/turn (уже для TURN, влючая ChannelData), ну и собствено сервер: gortc/gortcd (поддержка STUN, TURN, e2e тесты через relayed webrtc).


    А еще есть парсер RFC 4566 SDP.


  1. karantir
    13.08.2018 15:27

    Почему у Яндекса еще нет публичных STUN серверов? Все были бы вам благодарны.


  1. sena
    13.08.2018 15:52

    Но это же всё будет работать только дома или на своей симке, верно? В гостинице или аэропорту придётся опять доставать скайп, потому что в местной беспроводной сети все порты кроме 80 и 443 будут закрыты?


    1. cy-ernado
      13.08.2018 16:09
      +1

      Если TURN сервер будет на 443 порту и работать через TCP/TLS (на случай блока UDP), то должен подойти в качестве релея в таком случае.


      1. sena
        13.08.2018 17:39

        О! Это уже интересно. Если теперь научить HAProxy узнавать протокол TURN сервера (как протокол называется?), то может выйти что-то толковое!


        1. cy-ernado
          13.08.2018 18:18

          Можно просто отдельный TURN сервер поставить, не обязательно же мультиплексировать на 443 порту.


          Узнавать нужно будет STUN и TURN ChannelData.


          1. sena
            13.08.2018 18:46

            У меня на домашнем сервере, где я хочу это всё водружать, только один IP и только один 443 порт. Другие порты могут быть заблокированы в гостинице/аэропорту.
            Конечно, для себя лично я могу настроить vpn, но хотелось бы раздавать сервис и другим знакомым/друзьям без необходимости настраивать vpn.


  1. aylarov
    14.08.2018 10:16

    Если нет желания разбираться со всей этой низкоуровневой историей, то можно взять Voximplant, а освободившееся время потратить на более полезные вещи — UX-приложения, архитектуру решения и тд.


    1. BugM
      14.08.2018 12:15

      Проприетарная и платная против свободной бесплатной опенсорсоной.
      Что же выбрать. Я даже не знаю…