Простые каналы видео, голоса и данных WebRTC
Среднее прочтение 5 мин.
ARCHITECTURE peer to peer client - server
Установка
npm install simple-peer
Использование
Этот пакет работает в браузере с browserify . Если вы не используете упаковщик, вы можете использовать simplepeer.min.js
автономный скрипт непосредственно в <script>
теге. Это экспортирует SimplePeer
конструктор в window
. Везде, где вы видите Peer
в приведенных ниже примерах, замените это на SimplePeer
.
Давайте создадим html-страницу, которая позволит вам вручную подключить два узла:
<html>
<body>
<style>
#outgoing {
width: 600px;
word-wrap: break-word;
white-space: normal;
}
</style>
<form>
<textarea id="incoming"></textarea>
<button type="submit">submit</button>
</form>
<pre id="outgoing"></pre>
<script src="simplepeer.min.js"></script>
<script>
const p = new SimplePeer({
initiator: location.hash === '#1',
trickle: false
})
p.on('error', err => console.log('error', err))
p.on('signal', data => {
console.log('SIGNAL', JSON.stringify(data))
document.querySelector('#outgoing').textContent = JSON.stringify(data)
})
document.querySelector('form').addEventListener('submit', ev => {
ev.preventDefault()
p.signal(JSON.parse(document.querySelector('#incoming').value))
})
p.on('connect', () => {
console.log('CONNECT')
p.send('whatever' + Math.random())
})
p.on('data', data => {
console.log('data: ' + data)
})
</script>
</body>
</html>
Посещение index.html#1
из одного браузера (инициатора) и index.html
из другого браузера (получателя).
«Предложение» будет сгенерировано инициатором. Вставьте это в форму получателя и нажмите «Отправить». Получатель генерирует «ответ». Вставьте это в форму инициатора и нажмите «Отправить».
Теперь у вас есть прямое P2P-соединение между двумя браузерами!
Более простой пример
В этом примере создаются два одноранговых узла на одной и той же веб-странице .
В реальном приложении вы бы никогда этого не сделали . Экземпляры отправителя и получателя Peer
будут существовать в разных браузерах. «Сервер сигнализации» (обычно реализованный с помощью веб-сокетов) будет использоваться для обмена сигнальными данными между двумя браузерами до тех пор, пока не будет установлено одноранговое соединение.
каналы данных
var Peer = require('simple-peer')
var peer1 = new Peer({ initiator: true })
var peer2 = new Peer()
peer1.on('signal', data => {
// when peer1 has signaling data, give it to peer2 somehow
peer2.signal(data)
})
peer2.on('signal', data => {
// when peer2 has signaling data, give it to peer1 somehow
peer1.signal(data)
})
peer1.on('connect', () => {
// wait for 'connect' event before using the data channel
peer1.send('hey peer2, how is it going?')
})
peer2.on('data', data => {
// got a data channel message
console.log('got a message from peer1: ' + data)
})
видео/голос
Видео/голос тоже супер просто! В этом примере узел 1 отправляет видео узлу 2.
var Peer = require('simple-peer')
// get video/voice stream
navigator.mediaDevices.getUserMedia({
video: true,
audio: true
}).then(gotMedia).catch(() => {})
function gotMedia (stream) {
var peer1 = new Peer({ initiator: true, stream: stream })
var peer2 = new Peer()
peer1.on('signal', data => {
peer2.signal(data)
})
peer2.on('signal', data => {
peer1.signal(data)
})
peer2.on('stream', stream => {
// got remote video stream, now let's show it in a video tag
var video = document.querySelector('video')
if ('srcObject' in video) {
video.srcObject = stream
} else {
video.src = window.URL.createObjectURL(stream) // for older browsers
}
video.play()
})
}
Для двустороннего видео просто передайте stream
параметр в оба Peer
конструктора. Простой!
Обратите внимание, что getUserMedia
работает только на страницах, загруженных через https .
динамическое видео/голос
Также можно сначала установить соединение только для передачи данных, а затем, при желании, добавить видео/голосовой поток.
var Peer = require('simple-peer') // create peer without waiting for media
var peer1 = new Peer({ initiator: true }) // you don't need streams here
var peer2 = new Peer()
peer1.on('signal', data => {
peer2.signal(data)
})
peer2.on('signal', data => {
peer1.signal(data)
})
peer2.on('stream', stream => {
// got remote video stream, now let's show it in a video tag
var video = document.querySelector('video')
if ('srcObject' in video) {
video.srcObject = stream
} else {
video.src = window.URL.createObjectURL(stream) // for older browsers
}
video.play()
})
function addMedia (stream) {
peer1.addStream(stream) // <- add streams to peer dynamically
}
// then, anytime later...
navigator.mediaDevices.getUserMedia({
video: true,
audio: true
}).then(addMedia).catch(() => {})
в узле
Чтобы использовать эту библиотеку в узле, передайте ее в opts.wrtc
в качестве параметра
var Peer = require('simple-peer')
var wrtc = require('wrtc')
var peer1 = new Peer({ initiator: true, wrtc: wrtc })
var peer2 = new Peer({ wrtc: wrtc })
API
peer = new Peer([opts])
Создайте новое одноранговое соединение WebRTC.
Всегда устанавливается «канал данных» для текстового/двоичного обмена, потому что это дешево и часто полезно. Для видео/голосовой связи передайте stream
опцию.
Если opts
указано, то параметры по умолчанию (показаны ниже) будут переопределены.
{
initiator: false,
channelConfig: {},
channelName: '<random string>',
config: { iceServers: [{ urls: 'stun:stun.l.google.com:19302' }, { urls: 'stun:global.stun.twilio.com:3478?transport=udp' }] },
offerOptions: {},
answerOptions: {},
sdpTransform: function (sdp) { return sdp },
stream: false,
streams: [],
trickle: true,
allowHalfTrickle: false,
wrtc: {}, // RTCPeerConnection/RTCSessionDescription/RTCIceCandidate
objectMode: false
}
Опции делают следующее:
initiator
— установить,true
если это инициирующий пирchannelConfig
— пользовательская конфигурация канала данных webrtc (используетсяcreateDataChannel
)channelName
— пользовательское имя канала данных webrtcconfig
— пользовательская конфигурация webrtc (используетсяRTCPeerConnection
конструктором)offerOptions
— настраиваемые параметры предложения (используетсяcreateOffer
методом)answerOptions
— настраиваемые варианты ответов (используемыеcreateAnswer
методом)sdpTransform
— функция преобразования сгенерированных данных сигнализации SDP (для опытных пользователей)stream
— если требуется видео/голос, передать поток, возвращенный изgetUserMedia
streams
— массив MediaStreams, возвращенный изgetUserMedia
trickle
— установите,false
чтобы отключить Trickle ICE и получить одиночное «сигнальное» событие (медленнее)wrtc
— собственная реализация webrtc, в основном полезная в узле для указания в пакете wrtc . Содержит объект со свойствами:RTCPeerConnection
RTCSessionDescription
RTCIceCandidate
objectMode
— установите дляtrue
создания потока в объектном режиме . В этом режиме входящие строковые данные не преобразуются автоматически вBuffer
объекты.
peer.signal(data)
Вызывайте этот метод всякий раз, когда удаленный одноранговый узел создает peer.on('signal')
событие.
Будет data
ин капсулировать предложение webrtc, ответ или ледяной кандидат. Эти сообщения помогают одноранговым узлам в конечном итоге установить прямое соединение друг с другом. Содержимое этих строк является деталью реализации, которую пользователь этого модуля может игнорировать; просто передайте данные из событий «сигнала» удаленному узлу и позвоните peer.signal(data)
, чтобы подключиться.
peer.send(data)
Отправлять текстовые/двоичные данные удаленному узлу. data
может быть любого из нескольких типов: String
, Buffer
, ArrayBufferView
( и т. д Uint8Array
.) ArrayBuffer
, или Blob
(в браузерах, которые его поддерживают).
Примечание. Если этот метод вызывается до того, как peer.on('connect')
событие сработает, будет выдано исключение. Используйте peer.write(data)
(который унаследован от интерфейса дуплексного потока node.js ), если вы хотите, чтобы эти данные вместо этого были буферизованы.
peer.addStream(stream)
Добавьте MediaStream
к соединению.
peer.removeStream(stream)
Удалите a MediaStream
из соединения.
peer.addTrack(track, stream)
Добавьте MediaStreamTrack
к соединению. Также необходимо передать то MediaStream
, к чему вы хотите его прикрепить.
peer.removeTrack(track, stream)
Удалите a MediaStreamTrack
из соединения. Также должен пройти тот , к MediaStream
которому он был прикреплен.
peer.replaceTrack(oldTrack, newTrack, stream)
Замените на MediaStreamTrack
другой трек. Также необходимо пройти тот , к MediaStream
которому был привязан старый трек.
peer.addTransceiver(kind, init)
Добавьте RTCRtpTransceiver
к соединению. Может использоваться для добавления приемопередатчиков перед добавлением дорожек. Автоматически вызывается по мере необходимости addTrack
.
peer.destroy([err])
Уничтожьте и очистите это одноранговое соединение.
Если передан необязательный err
параметр, он будет передан как 'error'
событие в потоке.
Peer.WEBRTC_SUPPORT
Обнаружение встроенной поддержки WebRTC в среде javascript.
var Peer = require('simple-peer')
if (Peer.WEBRTC_SUPPORT) {
// webrtc support!
} else {
// fallback
}
дуплексный поток
Peer
объекты являются экземплярами stream.Duplex
. Они ведут себя очень похоже на модуль net.Socket
ядра узла . net
Дуплексный поток читает/записывает в канал данных.
var peer = new Peer(opts)
// ... signaling ...
peer.write(new Buffer('hey'))
peer.on('data', function (chunk) {
console.log('got a chunk', chunk)
})
События
Peer
объекты являются экземпляром EventEmitter
. Взгляните на документацию по событиям nodejs для получения дополнительной информации.
Пример удаления всех зарегистрированных прослушивателей close -event:
peer.removeAllListeners('close')
peer.on('signal', data => {})
Запускается, когда одноранговый узел хочет отправить сигнальные данные удаленному одноранговому узлу.
Ответственность за передачу этих данных другому узлу лежит на разработчике приложения (это вы!). Обычно это влечет за собой использование сервера сигнализации веб-сокетов. Эти данные представляют собой Object
, поэтому не забудьте сначала вызвать JSON.stringify(data)
их для сериализации. Затем просто позвоните peer.signal(data)
удаленному узлу.
(Обязательно прослушайте это событие немедленно, чтобы не пропустить его. Для initiator: true
одноранговых узлов оно срабатывает сразу. Для initatior: false
одноранговых узлов оно срабатывает при получении удаленного предложения.)
peer.on('connect', () => {})
Запускается, когда одноранговое соединение и канал данных готовы к использованию.
peer.on('data', data => {})
Получено сообщение от удаленного партнера (по каналу данных).
data
будет либо a, String
либо a Buffer/Uint8Array
.
peer.on('stream', stream => {})
Получен удаленный видеопоток, который можно отобразить в теге видео:
peer.on('stream', stream => {
var video = document.querySelector('video')
if ('srcObject' in video) {
video.srcObject = stream
} else {
video.src = window.URL.createObjectURL(stream)
}
video.play()
})
peer.on('track', (track, stream) => {})
Получена удаленная аудио/видео дорожка. Потоки могут содержать несколько дорожек.
peer.on('close', () => {})
Вызывается, когда одноранговое соединение закрыто.
peer.on('error', (err) => {})
Запускается при возникновении фатальной ошибки. Обычно это означает, что от удаленного партнера были получены неверные сигнальные данные.
err
является Error
объектом.
коды ошибок
Ошибки, возвращаемые error
событием, имеют err.code
свойство, указывающее на источник сбоя.
Возможные коды ошибок:
ERR_WEBRTC_SUPPORT
ERR_CREATE_OFFER
ERR_CREATE_ANSWER
ERR_SET_LOCAL_DESCRIPTION
ERR_SET_REMOTE_DESCRIPTION
ERR_ADD_ICE_CANDIDATE
ERR_ICE_CONNECTION_FAILURE
ERR_SIGNALING
ERR_DATA_CHANNEL
ERR_CONNECTION_FAILURE
подключение более 2 пиров?
Самый простой способ сделать это — создать полносвязную топологию. Это означает, что каждый пир открывает соединение с каждым другим узлом. Проиллюстрировать:

Чтобы передать сообщение, просто переберите все одноранговые узлы и вызовите peer.send
.
Итак, скажем, у вас есть 3 сверстника. Затем, когда одноранговый узел хочет отправить какие-то данные, он должен отправить их 2 раза, по одному разу каждому из других одноранговых узлов. Так что вам нужно быть немного осторожным с размером данных, которые вы отправляете.
Полноячеистые топологии плохо масштабируются при очень большом числе одноранговых узлов. Общее количество ребер в сети будет равно n
количеству пиров.
Для ясности, вот код для соединения 3 пиров вместе:
Peer 1
// These are peer1's connections to peer2 and peer3
var peer2 = new Peer({ initiator: true })
var peer3 = new Peer({ initiator: true })
peer2.on('signal', data => {
// send this signaling data to peer2 somehow
})
peer2.on('connect', () => {
peer2.send('hi peer2, this is peer1')
})
peer2.on('data', data => {
console.log('got a message from peer2: ' + data)
})
peer3.on('signal', data => {
// send this signaling data to peer3 somehow
})
peer3.on('connect', () => {
peer3.send('hi peer3, this is peer1')
})
peer3.on('data', data => {
console.log('got a message from peer3: ' + data)
})
Peer 2
// These are peer2's connections to peer1 and peer3
var peer1 = new Peer()
var peer3 = new Peer({ initiator: true })
peer1.on('signal', data => {
// send this signaling data to peer1 somehow
})
peer1.on('connect', () => {
peer1.send('hi peer1, this is peer2')
})
peer1.on('data', data => {
console.log('got a message from peer1: ' + data)
})
peer3.on('signal', data => {
// send this signaling data to peer3 somehow
})
peer3.on('connect', () => {
peer3.send('hi peer3, this is peer2')
})
peer3.on('data', data => {
console.log('got a message from peer3: ' + data)
})
Peer 3
// These are peer3's connections to peer1 and peer2
var peer1 = new Peer()
var peer2 = new Peer()
peer1.on('signal', data => {
// send this signaling data to peer1 somehow
})
peer1.on('connect', () => {
peer1.send('hi peer1, this is peer3')
})
peer1.on('data', data => {
console.log('got a message from peer1: ' + data)
})
peer2.on('signal', data => {
// send this signaling data to peer2 somehow
})
peer2.on('connect', () => {
peer2.send('hi peer2, this is peer3')
})
peer2.on('data', data => {
console.log('got a message from peer2: ' + data)
})
использование памяти
Если вы вызываете peer.send(buf)
, simple-peer
не сохраняет ссылку buf
и не отправляет буфер в какой-то более поздний момент времени. Мы сразу вызываем channel.send()
на канал данных. Так что это должно быть нормально, чтобы изменить буфер сразу после этого.
Однако имейте в виду, что peer.write(buf)
(метод потока с возможностью записи) не имеет такого же контракта. Он потенциально будет буферизовать данные и вызывать channel.send()
их в будущем, поэтому определенно не думайте, что изменять буфер безопасно.
соединение не работает в некоторых сетях?
В случае сбоя прямого подключения, в частности, из-за обхода NAT и/или межсетевых экранов, WebRTC ICE использует промежуточный (релейный) TURN-сервер. Другими словами, ICE сначала будет использовать STUN с UDP для прямого подключения пиров и, если это не удастся, вернется к серверу ретрансляции TURN.
Чтобы использовать сервер TURN, вы должны указать config
опцию Peer
конструктору. См. документы API выше.
Кто использует simple-peer
?
- WebTorrent — потоковый торрент-клиент в браузере
- Virus Cafe — Заведи друга за 2 минуты
- Instant.io — безопасная, анонимная, потоковая передача файлов
- Zencastr — легко записывайте удаленные интервью подкастов в студийном качестве.
- Друзья — одноранговый чат на базе Интернета
- Socket.io-p2p — официальная коммуникационная библиотека Socket.io P2P
- ScreenCat — совместное использование экрана + приложение для удаленной совместной работы
- WebCat — P2P-канал через Интернет с использованием закрытого/открытого ключа Github для аутентификации
- RTCCat — сетевой кот WebRTC
- PeerNet — одноранговая сеть сплетен, использующая рандомизированные алгоритмы .
- PusherTC — Видеочат с помощью Pusher. См . руководство .
- lxjs-chat — сайт видеочата, похожий на Omegle
- Белая доска — доска P2P на основе WebRTC и WebTorrent
- Peer Calls — групповые видеозвонки WebRTC. Создайте комнату. Поделитесь ссылкой.
- Netsix — отправляйте видео своим друзьям с помощью WebRTC, чтобы они сразу же могли их просмотреть.
- Stealthy — Stealthy — это децентрализованное приложение для p2p-чата со сквозным шифрованием.
- oorja.io — удобный видео-голосовой чат с функциями совместной работы в реальном времени. Расширяемый с использованием реагирующих компонентов🙌
- TalktoMe — альтернатива Skype для аудио/видеоконференций на базе WebRTC, но без потери пакетов.
- CDNBye — CDNBye реализует канал данных WebRTC для масштабирования потоковой передачи видео в реальном времени / видео через одноранговую сеть с использованием протокола, подобного битторренту.
- Detox — оверлейная сеть для распределенных анонимных P2P-коммуникаций полностью в браузере.
- Metastream — смотрите потоковое мультимедиа с друзьями.
- firepeer — безопасная сигнализация и аутентификация с использованием базы данных Firebase в реальном времени
- Genet — наложение Fat-tree для масштабирования количества одновременных WebRTC-подключений к одному источнику ( бумага ).
- Тестирование соединения WebRTC — быстро протестируйте прямое соединение между всеми парами участников ( демонстрация ).
- Firstdate.co — онлайн-видео знакомства для реальных встреч с людьми, а не просто для обмена сообщениями
- TensorChat — Это просто — Создавайте. Делиться. Чат.
- On/Office — просмотр рабочего стола в среде на основе WebVR
- Cyph — криптографически безопасный сервис для обмена сообщениями и социальных сетей, обеспечивающий высочайший уровень конфиденциальности в сочетании с лучшей в своем классе простотой использования.
- Ciphora — одноранговое приложение для обмена сообщениями со сквозным шифрованием.
- Whisthub — карточная онлайн-игра Color Whist с возможностью начать видеочат во время игры.
- Brie.fi/ng — Безопасный анонимный видеочат
- Peer.School — простой виртуальный класс, начиная с 1-го класса, включая видеочат и интерактивную доску в реальном времени.
- FileFire — Передавайте большие файлы и папки на высокой скорости без ограничений по размеру.
- safeShare — легко передавайте файлы с помощью текстового и голосового общения.
- CubeChat — Вечеринка в 3D🎉
- Homely School — Виртуальная система обучения
- AnyDrop — кроссплатформенная альтернатива AirDrop с приложением для Android, доступным в Google Play.
- Share-Anywhere — кроссплатформенная передача файлов
- QuaranTime.io — настольная игра Activity в видео!
- Trango — кроссплатформенное решение для звонков и обмена файлами.
- P2PT — используйте трекеры WebTorrent в качестве сигнальных серверов для установления соединений WebRTC.
- Dots — Многопользовательская онлайн игра Dots & Boxes. Играй здесь!
- simple-peer-files — простая библиотека для простой передачи файлов через WebRTC. Имеет функцию возобновления передачи файлов после прерывания загрузчика.
- WebDrop.Space — делитесь файлами и сообщениями на разных устройствах. Кроссплатформенность, отсутствие альтернативы установки AirDrop, Xender. Исходный код
- Speakrandom — социальная сеть голосового чата, использующая simple-peer для создания аудиоконференций!
- Deskreen — настольное приложение, которое поможет вам превратить любое устройство в дополнительный экран для вашего компьютера. Он использует простой одноранговый доступ для совместного использования всего экрана компьютера с любым устройством с веб-браузером.