Использование каналов данных в WebRTC
Как только WebRTC соединение установлено, используя интерфейс RTCPeerConnection
, приложение в состоянии отправлять и получать медиаданные между двумя узлами в соединении. Но от WebRTC можно получить больше. В этом руководстве мы изучим то, как добавить канал данных в соединение, который будет использован для безопасной передачи произвольных данных (данных любого типа, в любом формате).
Примечание: Поскольку все компоненты WebRTC требуют использования кодирования, любые данные, передаваемые через RTCDataChannel
автоматически защищаются, используя Datagram Transport Layer Security (DTLS). Смотри Security ниже для подробной информации.
Создание канала данных
Основной транспорт передачи данных, использующийся объектом типа RTCDataChannel
может быть создан двумя способами:
- Позволить WebRTC создать транспорт и сообщить об этом удалённому узлу (вызвав у него событие типа
datachannel
). Это простой способ, и он подходит для многих случаев, но не достаточно гибок для широких нужд. - Написать свои скрипты по согласованию транспорта данных, и сигнализированию другому узлу о необходимости присоединения к новому каналу данных.
Разберём оба случая, начиная с первого, как с наиболее распространённого.
Автоматический режим согласования
Зачастую, разработчик может позволить объекту соединения обработать согласование RTCDataChannel
соединения за него. Для этого нужно вызвать метод createDataChannel()
без определения значения свойства negotiated
, или определить свойство значением false
. Это автоматически активирует RTCPeerConnection
на обработку согласования соединения за разработчика, вызывая событие создание канала данных у удалённого узла, связывая два узла вместе по сети.
Вызов метода createDataChannel()
немедленно возвращает объект типа RTCDataChannel
. Подписываясь на событие open
, можно будет точно определить когда соединение успешно откроется.
let dataChannel = pc.createDataChannel("MyApp Channel");
dataChannel.addEventListener("open", (event) => {
beginTransmission(dataChannel);
});
Ручной режим согласования
Для ручного согласования соединения, сначала необходимо создать новый объект типа RTCDataChannel
, используя метод createDataChannel()
объекта RTCPeerConnection
, определяя свойство negotiated
в значение true
. Это сигнализирует объекту соединения не пытаться согласовать соединение автоматически.
Затем нужно согласовать соединение, используя веб сервер или иные средства коммуникации. Этот процесс должен сигнализировать удалённому узлу, что нужно создать собственный объект типа RTCDataChannel
со свойством negotiated
, установленным в значение true
, используя тот же идентификатор канала id
. Это свяжет два объекта типа RTCDataChannel
через объект типа RTCPeerConnection
.
let dataChannel = pc.createDataChannel("MyApp Channel", {
negotiated: true,
});
dataChannel.addEventListener("open", (event) => {
beginTransmission(dataChannel);
});
requestRemoteChannel(dataChannel.id);
В данном примере канал создаётся установкой значения свойства negotiated
в true
, затем вызывается функция requestRemoteChannel()
, запуская согласование соединения для создания удалённого канала с тем же идентификатором как у локального канала. Таким образом создание каналов данных позволяет использовать различные свойства, создавая их декларативно, используя одно и тоже значение идентификатора канала id
.
Буферизация
Каналы данных WebRTC поддерживают буферизацию исходящих данных. Это работает автоматически. Несмотря на то, что нет способа контролировать размер буфера, вы можете узнать, сколько данных в настоящее время буферизуется, и вы можете выбрать уведомление о событии, когда в буфере начинают заканчиваться данные в очереди. Это облегчает написание эффективных подпрограмм, которые гарантируют, что всегда есть данные, готовые к отправке, без чрезмерного использования памяти или полного переполнения канала.
Ограничения размеров сообщений
Для любых данных, передаваемых по сети, существуют ограничения по размеру. На фундаментальном уровне отдельные сетевые пакеты не могут быть больше определённого значения (точное число зависит от сети и используемого транспортного уровня). На уровне приложения, то есть в пределах user agent's реализация WebRTC, в которой работает ваш код, реализует функции поддержки сообщений, размер которых превышает максимальный размер пакета на транспортном уровне сети.
Это может усложнить ситуацию, поскольку вы не знаете, каковы ограничения по размеру для различных пользовательских агентов и как они реагируют на отправку или получение сообщения большего размера. Даже когда пользовательские агенты совместно используют одну и ту же базовую библиотеку для обработки данных протокола управления потоком (SCTP), могут существовать различия в зависимости от того, как используется библиотека. Например, и Firefox, и Google Chrome используют библиотеку usrsctp
для реализации SCTP, но все ещё существуют ситуации, в которых передача данных по RTCDataChannel
каналу может завершиться сбоем из-за различий в том, как они вызывают библиотеку и обрабатывают ошибки, которые она возвращает.
Когда два пользователя, использующие Firefox, обмениваются данными по каналу данных, ограничение размера сообщения намного больше, чем когда Firefox и Chrome обмениваются данными, потому что Firefox реализует устаревшую технику для отправки больших сообщений в нескольких сообщениях SCTP, чего нет в Chrome. Вместо этого Chrome увидит серию сообщений, которые он считает завершёнными, и доставит их получающему RTCDataChannel
каналу в виде нескольких сообщений
Сообщения размером менее 16 КБ могут отправляться без проблем, поскольку все основные пользовательские агенты обрабатывают их одинаково.
Проблемы с большими сообщениями
В настоящее время нецелесообразно использовать RTCDataChannel
для сообщений размером более 64 КБ (16 КБ, если вы хотите поддерживать кросс-браузерный обмен данными). Проблема возникает из-за того факта, что SCTP - протокол, используемый для отправки и получения данных по RTCDataChannel
- изначально был разработан для использования в качестве протокола сигнализации. Ожидалось, что сообщения будут относительно небольшими. Поддержка сообщений, превышающих размер сетевого уровня MTU, была добавлена в качестве запоздалой мысли, в случае, если сигнальные сообщения должны были быть больше, чем MTU. Эта функция требует, чтобы каждый фрагмент сообщения имел последовательные порядковые номера, поэтому они должны передаваться один за другим, без каких-либо других данных, чередующихся между ними.
В конечном итоге это стало проблемой. Со временем различные приложения (в том числе внедряющие WebRTC) начали использовать SCTP для передачи больших и больших сообщений. В конце концов стало ясно, что когда сообщения становятся слишком большими, передача большого сообщения может блокировать все другие передачи данных в этом канале данных, включая критические сообщения сигнализации.
Это станет проблемой, когда браузеры будут должным образом поддерживать текущий стандарт поддержки больших сообщений - флаг конца записи (EOR), который указывает, когда сообщение является последним в серии, которое следует рассматривать как одну полезную нагрузку. Это реализовано в Firefox 57, но ещё не реализовано в Chrome (см. Chromium Bug 7774). С поддержкой EOR полезная нагрузка RTCDataChannel
может быть намного больше (официально до 256 КБ, но реализация Firefox ограничивает их колоссальным 1 ГБ). Даже при 256 кБ этого достаточно, чтобы вызвать заметные задержки при обработке срочного трафика.
Чтобы решить эту проблему, была разработана новая система планировщиков потоков (обычно называемая «спецификацией данных SCTP»), позволяющая чередовать сообщения, отправленные в разных потоках, включая потоки, используемые для реализации каналов данных WebRTC. Это предложение предложение все ещё находится в черновой форме IETF, но после его реализации оно позволит отправлять сообщения практически без ограничений по размеру, поскольку уровень SCTP автоматически чередует лежащие в основе под-сообщения, чтобы обеспечить возможность получения данных каждого канала.
Поддержка Firefox для ndata находится в процессе реализации. Команда Chrome отслеживает реализацию поддержки ndata в Chrome Bug 5696.
Большая часть информации в этом разделе частично основана на блоге Demystifyijng WebRTC's Data Channel Message Size Limitations, написанный Леннартом Гралем. Там он немного подробнее рассказывает, но поскольку браузеры были обновлены с тех пор, некоторые посты могут быть устаревшими. Кроме того, со временем поддержки будет становиться все больше, особенно после того, как EOR и поддержка ndata будут полностью интегрированы в основные браузеры.
Безопасность
Все данные, переданные с помощью WebRTC, зашифрованы на основе Transport Layer Security (TLS). Поскольку TLS используется для защиты каждого HTTPS-соединения, любые данные, которые вы отправляете по каналу данных, так же безопасны, как и любые другие данные, отправляемые или получаемые браузером пользователя.
Поскольку WebRTC является одноранговым соединением между двумя пользовательскими агентами, данные никогда не проходят через веб-сервер или сервер приложений, что снижает возможность перехвата данных.