Skip to content

Instantly share code, notes, and snippets.

@richtr
Last active August 29, 2015 13:57
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save richtr/9661441 to your computer and use it in GitHub Desktop.
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)
<!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>
<!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>
<!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