Last active
March 8, 2018 17:59
-
-
Save TikiTDO/ccc2189813a11a8cc0472d977f28aaab to your computer and use it in GitHub Desktop.
Rewrite of the WebRTC DataChannel Basic example, with all the operations grouped together for clarity. Now with links to relevant places in the spec.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. | |
* | |
* Use of this source code is governed by a BSD-style license | |
* that can be found in the LICENSE file in the root of the source | |
* tree. | |
*/ | |
// Original used from: https://github.com/webrtc/samples/blob/750b0ec7675b3326831bb95261b33d0842bc2b39/src/content/datachannel/basic/js/main.js | |
'use strict'; | |
// DOM Buttons | |
var startButton = document.querySelector('button#startButton'); | |
var sendButton = document.querySelector('button#sendButton'); | |
var closeButton = document.querySelector('button#closeButton'); | |
// DOM Data Source/Receiver | |
var local_data_textarea = document.querySelector('textarea#dataChannelSend'); | |
var remote_data_textarea = document.querySelector('textarea#dataChannelReceive'); | |
// RTC Objects | |
var local_connection; | |
var remote_connection; | |
var local_channel; | |
var remote_channel; | |
// RTC Configuration | |
var rtc_connection_configuration; | |
var data_channel_configuration; | |
// Functions to simulate abstract data exchange | |
function send_to_remote(callback) {callback()} | |
function send_to_local(callback) {callback()} | |
// Send data from one element to another | |
sendButton.onclick = function () { | |
var data = local_data_textarea.value; | |
local_channel.send(data); | |
trace('Sent Data: ' + data); | |
}; | |
// Close all the channels, connections, and return to initial state | |
closeButton.onclick = function closeDataChannels() { | |
// First close the channels created on the connections | |
local_channel.close(); | |
remote_channel.close(); | |
local_channel = null, remote_channel = null; | |
trace('Closed Channels'); | |
// Second, close the connections | |
local_connection.close(); | |
remote_connection.close(); | |
local_connection = null, remote_connection = null; | |
trace('Closed Connections'); | |
// Finally, clean up the buttons and textareas | |
startButton.disabled = false, sendButton.disabled = true, closeButton.disabled = true; | |
local_data_textarea.disabled = true, local_data_textarea.value = '', remote_data_textarea.value = ''; | |
trace('Reset Button States'); | |
}; | |
// Create a connection between the two elements | |
startButton.onclick = function () { | |
// Init | |
local_data_textarea.placeholder = ''; | |
// To be extra clear - STUN/TURN Servers are ICE Servers | |
// See RTC Connection Configuration https://w3c.github.io/webrtc-pc/#rtcconfiguration-dictionary | |
rtc_connection_configuration = { | |
// iceServers: [server1, server2], // Array of {urls: ['server uri'], username: 'string', | |
// // credential: 'string', credentialType: 'password|token'} | |
// iceTransportPolicy: 'relay|all', // (Default: all) May force using TURN servers to prevent leaking IP data | |
// iceCandidatePool: integer, // (Default: 0) How many ICE candidates to wait for befor trying to connect | |
// rtcpMuxPolicy: 'negotiate|require' // (Default: 'require') May force RTC to run on a single UDP port | |
// peerIdentity: 'string', // Securely identify a connection, to prevent hijacking | |
// certificates: [certificate, certificate], // Certificates used to authenticate connection. Only set once. | |
// bundlePolicy: 'balanced|max-compat|max-bundle', // (Default: 'balanced') How to combine media streams | |
}; | |
// See Data Channel Configuration https://w3c.github.io/webrtc-pc/#idl-def-rtcdatachannelinit | |
data_channel_configuration = { | |
// order: boolean, // (Default: true) Whether data must be delivered in order | |
// maxPacketLifeTime: int, // Maximum amount of time that client will re-transmit data without ack | |
// maxRetransmits: int, // Maximum number ot times client will re-transmit data without ack | |
// protocol: 'string', // (Default: '') Sub-protocol for this channel | |
// negotiated : boolean, // (Default: false) Whether remote data channels are automatically negotiated | |
// id: int // Override the numberic identifier for this channel | |
}; | |
// Initialize the RCP Connections | |
initializeConnections(); | |
// Initialize the RCP channels | |
initializeLocalDataChannel(); | |
initializeRemoteDataChannel(); | |
// Connect channels to each other | |
connectLocalToRemote(); | |
// Put buttons into connected mode | |
startButton.disabled = true; | |
closeButton.disabled = false; | |
}; | |
// Configure the local and remote connections with the minimum functional parameters | |
function initializeConnections () { | |
// Start local and remote RCP channel. Nominally this is done in two different browser contexts | |
window.local_connection = new RTCPeerConnection(rtc_connection_configuration); | |
trace('Local Connection has been created'); | |
window.remote_connection = new RTCPeerConnection(rtc_connection_configuration); | |
trace('Remote Connection has been created'); | |
// See RTC Connection Intereface definition https://w3c.github.io/webrtc-pc/#interface-definition | |
// Exchange ICE server (STUN/TURN) responses between local and remote. This should be done with a signalling server | |
local_connection.onicecandidate = function (event) { | |
send_to_remote(function () { | |
if (!event.candidate) return; // The last event from the server will not have a candidate | |
remote_connection.addIceCandidate(event.candidate, onAddIceCandidateSuccess, onAddIceCandidateError); | |
trace('Sent a Local Connection ICE candidate to the Remote Connection.') | |
}); | |
}; | |
remote_connection.onicecandidate = function (event) { | |
send_to_local(function () { | |
if (!event.candidate) return; // The last event from the server will not have a candidate | |
local_connection.addIceCandidate(event.candidate, onAddIceCandidateSuccess, onAddIceCandidateError); | |
trace('Sent a Remote Connection ICE candidate to the Local Connection.') | |
}); | |
} | |
} | |
// Initialize the local channel, then listen on that channel for open/close events | |
function initializeLocalDataChannel () { | |
// Initialize local RCP channel | |
local_channel = local_connection.createDataChannel('example_channel_label', data_channel_configuration); | |
trace('Local Connection has created a Local Channel'); | |
// See Data Channel Interface definition https://w3c.github.io/webrtc-pc/#rtcdatachannel | |
// Configure local RCP channel | |
local_channel.onopen = function () { | |
sendButton.disabled = false, closeButton.disabled = false; | |
local_data_textarea.disabled = false; | |
local_data_textarea.focus(); | |
trace('Local Channel state is: ' + local_channel.readyState); | |
}; | |
local_channel.onclose = function () { | |
local_data_textarea.disabled = true; | |
sendButton.disabled = true, closeButton.disabled = true; | |
trace('Local Channel state is: ' + local_channel.readyState); | |
}; | |
local_channel.onmessage = function (event) { | |
trace('Local Channel received a message'); | |
} | |
}; | |
// Watch the remote connection for a data channel, then listen on that channel for messages, or open/close events | |
function initializeRemoteDataChannel() { | |
// See Data Channel Interface definition https://w3c.github.io/webrtc-pc/#rtcdatachannel | |
// Initialize remote RCP channel, once it is connected to local channel by the connectLocalToRemote function | |
remote_connection.ondatachannel = function (event) { | |
remote_channel = event.channel; | |
trace('Remote Connection has created a Remote Channel'); | |
// Configure remote message receiver | |
remote_channel.onmessage = function (event) { | |
remote_data_textarea.value = event.data; | |
trace('Remote Channel received a message'); | |
}; | |
// Handle remote open/close | |
remote_channel.onopen = remote_channel.onclose = function () { | |
trace('Remote Channel state is: ' + remote_channel.readyState); | |
}; | |
}; | |
} | |
// Offer the local channel to the remote channel | |
function connectLocalToRemote() { | |
var local_offer_promise; | |
// Create an offer to connect to local connection | |
local_offer_promise = local_connection.createOffer(); | |
local_offer_promise.catch(onCreateSessionDescriptionError); | |
local_offer_promise.then(function (offer_description) { | |
local_connection.setLocalDescription(offer_description); | |
trace('Created Offer from Local Connection'); | |
}); | |
// "Send" the offer description to remote | |
local_offer_promise.then(receiveOfferFromLocal); | |
} | |
// Connect the remote channel to the local channel | |
function receiveOfferFromLocal(offer_description) { | |
var remote_answer_promise; | |
// Save local offer in remote host | |
remote_connection.setRemoteDescription(offer_description); | |
trace('Offer received by Remote Connection: \n' + offer_description.sdp); | |
// Create answer to send back to local host | |
remote_answer_promise = remote_connection.createAnswer(); | |
remote_answer_promise.catch(onCreateSessionDescriptionError); | |
remote_answer_promise.then(function (answer_description) { | |
remote_connection.setLocalDescription(answer_description); | |
trace('Created Answer from Remote Connection'); | |
}); | |
// "Send" the answer back to local | |
remote_answer_promise.then(receiveAnswerFromRemote); | |
} | |
// Connect the local channel to the remote channel | |
function receiveAnswerFromRemote(answer_description) { | |
local_connection.setRemoteDescription(answer_description); | |
trace('Answer received by Local Connection: \n' + answer_description.sdp); | |
} | |
// Handle errors during offer or answer creation | |
function onCreateSessionDescriptionError(error) { | |
trace('Failed to create session description: ' + error.toString()); | |
} | |
// Signals that an ICE candidate has been added to a connection's DB | |
function onAddIceCandidateSuccess() { | |
trace('AddIceCandidate success.'); | |
} | |
// Signals that an ICE candidate could not be added to a connection's DB | |
function onAddIceCandidateError(error) { | |
trace('Failed to add Ice Candidate: ' + error.toString()); | |
} |
This example is meant purely to illustrate the steps that happen when connecting two peers together. It does not actually use any ICE servers. If you wish to use ICE features like STUN and TURN then you will need to refer to examples such as these:
- Getting info out of ICE servers: https://github.com/webrtc/samples/blob/gh-pages/src/content/peerconnection/trickle-ice/js/main.js
- Using ICE servers for connection management: https://github.com/webrtc/samples/blob/gh-pages/src/content/peerconnection/restart-ice/js/main.js
It's cool, thank you very much.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
But how to implement this basic with a signaler server? No need turn, stun and like? Only null?