Last active
August 29, 2015 13:57
-
-
Save richtr/9661441 to your computer and use it in GitHub Desktop.
BroadcastWebSocket (#1): Pure P2P advertisement, discovery and establishment of a full-duplex messaging channel between previously isolated web pages within a local network (updated strawman proposal can be found at https://gist.github.com/richtr/9683905)
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Pure P2P messaging channel establishment: Advertiser</title> | |
</head> | |
<body> | |
<h1>Pure P2P messaging channel establishment: Advertiser</h1> | |
<script> | |
// Initiate a web socket that also broadcasts its availability in the local network. | |
// | |
// The JavaScript interface is identical to the standard WebSocket interface | |
// but just takes longer to transition 'readyState' from CONNECTING to OPEN due | |
// to the requirement that a consumer must connect for the WebSocket to then be | |
// considered opened. | |
// | |
// The network advertisement broadcast of this BroadcastWebSocket service could | |
// potentially be agnostic to the underlying Discovery Protocol used. | |
// i.e. it could be broadcast over SSDP, Zeroconf and/or DIAL by the UA | |
// as this is not exposed in the JavaScript API. | |
// | |
// When the first consumer connects to this BroadcastWebSocket service then network | |
// service advertisement of this instance of a BroadcastWebSocket service stops. | |
// | |
// As consumer.html could be running on a remote host within the local network we | |
// build on top of the WebSocket interface rather than MessageChannel or MessagePort | |
// interfaces which do not have well-defined network communication or serialization | |
// features. | |
// | |
var ws = new BroadcastWebSocket('myservicename'); | |
// Triggered when consumer.html opens a web socket connection back to this object | |
ws.onopen = function(evt) { | |
console.log('consumer.html is now connected'); | |
}; | |
// Triggered when consumer.html sends a message through the established WebSocket | |
// connection. | |
ws.onmessage = function(evt) { | |
console.log('consumer.html sent us a message'); | |
ws.send('send a message back to consumer.html'); | |
}; | |
// | |
// Triggered if/when consumer.html closes the websocket connection | |
// or when consumer.html is closed or its network is disconnected or | |
// if/when we call ws.close() from this web page (advertiser.html) | |
// | |
ws.onclose = function(evt) { | |
console.log('consumer.html is now disconnected'); | |
}; | |
</script> | |
</body> | |
</html> |
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Pure P2P messaging channel establishment: Consumer</title> | |
</head> | |
<body> | |
<h1>Pure P2P messaging channel establishment: Consumer</h1> | |
<script> | |
// Look for services with a given network service identifier using the | |
// Network Service Discovery API: | |
// | |
// https://dvcs.w3.org/hg/dap/raw-file/tip/discovery-api/Overview.html | |
// | |
// Exact BroadcastWebSocket network service syntax is to be discussed) | |
// | |
navigator.getNetworkServices('web+broadcast:myservicename').then(function(services) { | |
if(services.length <= 0) return; // no network services found so abort | |
// opening a new websocket channel to advertiser which triggers a 'connect' event on advertiser.html. | |
var ws = new WebSocket(services[0].url); // services[0].url is the receiving web socket url | |
// provided from the call to getNetworkServices(...) | |
// (e.g. ws://192.168.0.2:5000/myservicename - but this URL could also be obfuscated) | |
// Communication channel is established with advertiser.html | |
ws.onopen = function(evt) { | |
ws.send('we are connected to advertiser.html'); | |
}; | |
// Triggered when advertiser.html sends a message through the established | |
// WebSocket connection. | |
ws.onmessage = function(evt) { | |
console.log('message received from advertiser.html'); | |
ws.send('send a message back to advertiser.html'); | |
} | |
// | |
// Triggered if/when advertiser.html closes the connection or | |
// advertiser.html is closed or its network is disconnected or if/when we | |
// call ws.close() from this web page (consumer.html) | |
// | |
ws.onclose = function(evt) { | |
console.log('advertiser.html is now disconnected'); | |
}; | |
}); | |
</script> | |
</body> | |
</html> |
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>DEMO: Simple pure P2P WebRTC local setup example</title> | |
</head> | |
<body> | |
<h1>DEMO: Simple pure P2P WebRTC local setup example</h1> | |
<script> | |
// Local (pure P2P) signalling channel setup | |
var advertiserSignallingChannel = new BroadcastWebSocket('my_example_webrtc_app'); | |
advertiserSignallingChannel.onmessage = remoteDescReceived; | |
var consumerSignallingChannel; | |
navigator.getNetworkServices('web+broadcast:my_example_webrtc_app').then(function(broadcastSockets) { | |
if(broadcastSockets.length <= 0) return; | |
advertiserSignallingChannel.close(); | |
consumerSignallingChannel = new WebSocket(broadcastSockets[0].url); | |
consumerSignallingChannel.onmessage = remoteDescReceived; | |
}); | |
// WebRTC connection setup | |
var configuration = { "iceServers": [{ "url": "stun:stun.example.org" }] }; | |
var pc; | |
// call start() to initiate | |
function start() { | |
pc = new RTCPeerConnection(configuration); | |
// send any ice candidates to the other peer | |
pc.onicecandidate = function (evt) { | |
if (evt.candidate) | |
signalingChannel.send(JSON.stringify({ "candidate": evt.candidate })); | |
}; | |
// let the "negotiationneeded" event trigger offer generation | |
pc.onnegotiationneeded = function () { | |
pc.createOffer(localDescCreated, logError); | |
} | |
// once remote stream arrives, show it in the remote video element | |
pc.onaddstream = function (evt) { | |
remoteView.src = URL.createObjectURL(evt.stream); | |
}; | |
// get a local stream, show it in a self-view and add it to be sent | |
navigator.getUserMedia({ "audio": true, "video": true }, function (stream) { | |
selfView.src = URL.createObjectURL(stream); | |
pc.addStream(stream); | |
}, logError); | |
} | |
function localDescCreated(desc) { | |
pc.setLocalDescription(desc, function () { | |
signalingChannel.send(JSON.stringify({ "sdp": pc.localDescription })); | |
}, logError); | |
} | |
function remoteDescReceived(evt) { | |
if (!pc) | |
start(); | |
var message = JSON.parse(evt.data); | |
if (message.sdp) | |
pc.setRemoteDescription(new RTCSessionDescription(message.sdp), function () { | |
// if we received an offer, we need to answer | |
if (pc.remoteDescription.type == "offer") | |
pc.createAnswer(localDescCreated, logError); | |
}, logError); | |
else | |
pc.addIceCandidate(new RTCIceCandidate(message.candidate)); | |
}; | |
function logError(error) { | |
log(error.name + ": " + error.message); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment