This commit is contained in:
Alan K 2014-03-17 19:04:51 -04:00
Родитель 7e22558b8a
Коммит c3a8ac0a5f
11 изменённых файлов: 5071 добавлений и 1 удалений

@ -1 +0,0 @@
Subproject commit f750484c56c49fb0383d488a9751c466590004b3

2
tests/sockets/p2p/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,2 @@
node_modules
ssl

Просмотреть файл

@ -0,0 +1,98 @@
# WebRTC peer-to-peer
This is a browser JS library that makes it easy to manage RTC peer connections, streams and data channels.
It's currently used in [emscripten](http://github.com/kripken/emscripten) to provide data transport for the posix sockets implementation.
## Requirements
You will need either Firefox [Nightly](http://nightly.mozilla.org/), or Chrome [Canary](https://www.google.com/intl/en/chrome/browser/canary.html).
You can also use Chrome [Dev Channel](http://www.chromium.org/getting-involved/dev-channel).
## What it does
* Firefox (nightly) and Chrome (dev/canary) supported
* Binary transport using arraybuffers (Firefox only!)
* Multiple connections
* Broker service (on nodejitsu), or run your own
* Connection timeouts
## What it doesn't do (yet!)
* Interoperability between Firefox and Chrome
* Peer brokering for establishing new connections through existing peer-to-peer
## Quick start
Setting up a peer is easy. The code below will create a new peer and listen for incoming connections.
The `onconnection` handler is called each time a new connection is ready.
````javascript
// Create a new Peer
var peer = new Peer(
'http://webrtcb.jit.su:80', // You can use this broker if you don't want to set one up
{
binaryType: 'arraybuffer',
video: false,
audio: false
}
);
// Listen for incoming connections
peer.listen();
var connections = {};
// Handle new connections
peer.onconnection = function(connection) {
// Store connections here so we can use them later
connections[connection.id] = connection; // Each connection has a unique ID
connection.ondisconnect = function(reason) {
delete connections[connection.id];
};
connection.onerror = function(error) {
console.error(error);
};
// Handle messages from this channel
// The label will be 'reliable' or 'unreliable', depending on how it was received
connection.onmessage = function(label, message) {
console.log(label, message);
};
// Sends a message to the other peer using the reliable data channel
connection.send('reliable', 'hi!');
// The connection exposes the underlying media streams
// You can attach them to DOM elements to get video/audio, if available
console.log(connection.streams.local, connection.streams.remote);
// Closes the connection
// This will cause `ondisconnect` to fire
connection.close();
};
// Print our route when it's available
peer.onroute = function(route) {
// This is our routing address from the broker
// It's used by peers who wish to connect with us
console.log('route:', route);
};
peer.onerror = function(error) {
console.log(error);
};
````
Another peer can connect easily to the one we made above by calling `connect()` on its routing address.
````javascript
peer.connect(route);
````
## Demo
There are some files in the `demo` directory that offer an example.
You can load it [here](http://js-platform.github.com/p2p/examples/data-demo.html) and open the `connect` URL in another window.
For this example, the `route` is added to the URL query string so that the other peer can parse it and connect when the page loads, so all you need to share is the URL.

Просмотреть файл

@ -0,0 +1 @@
../node_modules/.bin/uglifyjs

Просмотреть файл

@ -0,0 +1,231 @@
var crypto = require('crypto');
var fs = require('fs');
var https = require('https');
var SSL_KEY = 'ssl/ssl.key';
var SSL_CERT = 'ssl/ssl-unified.crt';
var PORT = 8080;
var sslSupported = false;
if(fs.existsSync(SSL_KEY) && fs.existsSync(SSL_CERT) && fs.statSync(SSL_KEY).isFile() && fs.statSync(SSL_CERT).isFile()) {
sslSupported = true;
}
function handler(req, res) {
res.writeHead(200);
res.end("p2p");
};
var app, port;
if(sslSupported) {
var sslopts = {
key: fs.readFileSync(SSL_KEY),
cert: fs.readFileSync(SSL_CERT)
};
sslopts.agent = new https.Agent(sslopts);
app = require('https').createServer(sslopts, handler);
port = 8081;
console.info('ssl mode enabled');
} else {
app = require('http').createServer(handler);
port = 8080;
console.info('ssl mode disabled');
}
console.info('listening on port', port);
var io = require('socket.io').listen(app, {
'log level': 2
});
app.listen(port);
var jsMime = {
type: 'application/javascript',
encoding: 'utf8',
gzip: true
};
io.static.add('/p2p-client.js', {
mime: jsMime,
file: 'client/p2p-client.js'
});
/*
io.static.add('/p2p-client.min.js', {
mime: jsMime,
file: 'client/p2p-client.min.js'
});
*/
function mkguid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
}).toUpperCase();
};
var peers = {};
var hosts = {};
var E = {
OK: 'ok'
, NOROUTE: 'no such route'
, ISNOTHOST: 'peer is not a host'
};
function Peer(socket) {
this.socket = socket;
this.host = null;
};
function Host(options) {
this.route = options['route'];
this.url = options['url'];
this.listed = (undefined !== options['listed']) ? options['listed'] : false;
this.metadata = options['metadata'] || {};
this.ctime = Date.now();
this.mtime = Date.now();
};
Host.prototype.update = function update(options) {
this.url = options['url'];
this.listed = (undefined !== options['listed']) ? options['listed'] : false;
this.metadata = options['metadata'] || {};
this.mtime = Date.now();
};
io.of('/peer').on('connection', function(socket) {
var route = crypto.createHash('md5').update(socket['id']).digest('hex');
socket.emit('route', route);
socket.on('disconnect', function() {
if(hosts[route]) {
var host = hosts[route];
changeList('remove', host);
}
delete hosts[route];
delete peers[route];
});
socket.on('send', function(message, callback) {
var to = message['to'];
if(!peers.hasOwnProperty(to)) {
callback({'error': E.NOROUTE});
return;
}
var from = route;
var data = message['data'];
peers[to].emit('receive', {
'from': from,
'data': data
});
});
socket.on('listen', function(options, callback) {
options['route'] = route;
if(hosts.hasOwnProperty(route)) {
hosts[route].update(options);
changeList('update', hosts[route]);
} else {
hosts[route] = new Host(options);
changeList('append', hosts[route]);
}
callback();
});
socket.on('ignore', function(message, callback) {
if(!hosts.hasOwnProperty(route)) {
callback({'error': E.ISNOTHOST});
return;
}
var host = hosts[route];
delete hosts[route];
changeList('remove', host);
callback();
});
peers[route] = socket;
});
function Filter(socket, options) {
this.options = options || {};
this.socket = socket;
};
Filter.prototype.test = function test(host) {
var filter = this.options;
var result;
if(filter['url'] && typeof host['url'] === 'string') {
try {
result = host['url'].match(filter['url']);
if(!result)
return true;
} catch(e) {
return true;
}
}
if(filter['metadata'] && host['metadata']) {
var metadataFilter = filter['metadata'];
var metadataHost = host['metadata'];
if(metadataFilter['name'] && typeof metadataHost['name'] === 'string') {
try {
result = metadataHost['name'].match(metadataFilter['name']);
if(!result)
return true;
} catch(e) {
return true;
}
}
}
return false;
};
var lists = {};
function changeList(operation, host) {
var clients = Object.keys(lists);
clients.forEach(function(client) {
var filter = lists[client];
if(!host['listed'])
return;
if(!filter.test(host)) {
var data = operation === 'remove' ? host['route'] : host;
filter.socket.emit(operation, data);
}
});
};
io.of('/list').on('connection', function(socket) {
var id = socket['id'];
socket.on('disconnect', function() {
delete lists[id];
});
socket.on('list', function(options) {
var filter = new Filter(socket, options);
var result = [];
var hostIds = Object.keys(hosts);
hostIds.forEach(function(hostId) {
var host = hosts[hostId];
if(!host['listed'])
return;
if(!filter.test(host))
result.push(host);
});
lists[id] = filter;
socket.emit('truncate', result);
});
});

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,16 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<div>instructions: open the connect link in a new window or tab; after a few seconds you should see a message in both instances indicating that they are connected</div>
<video id="local" autoplay></video>
<video id="remote" autoplay></video>
<div id="host"></div>
<pre id="chat"></pre>
<input type="text" id="chatinput">
</body>
<script src="../client/p2p-client.js"></script>
<script src="data-demo.js"></script>
</html>

Просмотреть файл

@ -0,0 +1,103 @@
function log(msg) {
console.log(msg);
document.getElementById("chat").appendChild(document.createTextNode(msg + "\n"));
}
/*
var localvideo = document.getElementById("local");
var remotevideo = document.getElementById("remote");
*/
function bindStream(stream, element) {
if ("mozSrcObject" in element) {
element.mozSrcObject = stream;
} else {
element.src = webkitURL.createObjectURL(stream);
}
element.play();
};
var brokerSession = null;
var brokerUrl = 'https://mdsw.ch:8080';
var hosting = true;
var options;
if(window.location.search) {
var params = window.location.search.substring(1).split('&');
for(var i = 0; i < params.length; ++ i) {
if(params[i].match('^webrtc-session')) {
brokerSession = params[i].split('=')[1];
hosting = false;
} else if(params[i].match('^webrtc-broker')) {
brokerUrl = params[i].split('=')[1];
}
}
}
console.log('broker', brokerUrl);
var peer = new Peer(brokerUrl, {video: false, audio: false});
window.connections = {};
peer.onconnection = function(connection) {
log('connected: ' + connection.id);
connections[connection.id] = connection;
connection.ondisconnect = function() {
log('disconnected: ' + connection.id);
delete connections[connection.id];
};
connection.onerror = function(error) {
console.error(error);
};
//bindStream(connection.streams['local'], localvideo);
//bindStream(connection.streams['remote'], remotevideo);
connection.onmessage = function(label, msg) {
log('<other:' + connection.id + '> ' + msg.data);
};
/*
var buff = new Uint8Array([1, 2, 3, 4]);
connection.send('reliable', buff.buffer);
*/
};
peer.onerror = function(error) {
console.error(error);
};
if(hosting) {
console.log('hosting');
peer.listen({metadata:{name:'data-demo'}});
peer.onroute = function(route) {
var url = window.location.toString().split('?');
url[1] = url[1] || '';
var params = url[1].split('&');
params.push('webrtc-session=' + route);
url[1] = params.join('&');
var div = document.getElementById('host');
div.innerHTML = '<a href="' + url.join('?') + '">connect</a>';
}
} else {
peer.connect(brokerSession);
}
window.onbeforeunload = function() {
var ids = Object.keys(connections);
ids.forEach(function(id) {
connections[id].close();
});
peer.close();
};
document.getElementById("chatinput").addEventListener("keyup", function(e) {
if (e.keyCode == 13) {
var ci = document.getElementById("chatinput");
log("<self> " + ci.value);
var ids = Object.keys(connections);
ids.forEach(function(id) {
connections[id].send('reliable', ci.value);
});
ci.value = "";
}
});

Просмотреть файл

@ -0,0 +1,112 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<div id="list">
</div>
</body>
<script src="http://wrtcb.jit.su/socket.io/socket.io.js"></script>
<script>
var brokerUrl = 'https://mdsw.ch:8080';
if(window.location.search) {
var params = window.location.search.substring(1).split('&');
for(var i = 0; i < params.length; ++ i) {
if(params[i].match('^webrtc-broker')) {
brokerUrl = params[i].split('=')[1];
}
}
}
console.log(brokerUrl);
var hosts = {};
var filter = {
'metadata': {
'name': '.*'
}
};
var socket = io.connect(brokerUrl + '/list');
socket.on('connect', function() {
socket.emit('list', filter);
});
socket.on('error', function(error) {
console.error(error);
});
socket.on('truncate', function(list) {
clear();
append(list);
});
socket.on('append', function(host) {
append([host]);
});
socket.on('update', function(host) {
update([host]);
});
socket.on('remove', function(route) {
remove([route]);
});
function setQuery(url, item) {
var urlParts = url.split('?');
if(urlParts.length < 2) {
urlParts[1] = item;
} else {
var query = urlParts[1].split('&');
query.push(item);
urlParts[1] = query.join('&');
}
return urlParts.join('?');
};
function clear() {
hosts = {};
var div = document.getElementById('list');
var parent = div.parentNode;
parent.removeChild(div);
div = document.createElement('div');
div.id = 'list';
parent.appendChild(div);
};
function append(list) {
list = list || [];
list.forEach(function(host) {
hosts[host['route']] = host;
var div = document.getElementById('list');
var element = document.createElement('div');
host.element = element;
var name = host['metadata']['name'] || '';
var url = setQuery(host['url'], 'webrtc-session=' + host['route']);
element.innerHTML = new Date(host['ctime']).toString() + ' | ' + name + ' | ' + '<a href="' + url + '">join</a>';
div.appendChild(element);
});
};
function update(list) {
list = list || [];
list.forEach(function(host) {
if(hosts[host['route']]) {
var element = hosts[host['route']].element;
var name = host['metadata']['name'] || '';
var url = setQuery(host['url'], 'webrtc-session=' + host['route']);
element.innerHTML = new Date(host['ctime']).toString() + ' | ' + name + ' | ' + '<a href="' + url + '">join</a>';
host.element = element;
hosts[host['route']] = host;
}
});
};
function remove(list) {
list = list || [];
list.forEach(function(route) {
var host = hosts[route];
var element = host.element;
element.parentNode.removeChild(element);
delete hosts[route];
});
};
</script>
</html>

1
tests/sockets/p2p/forever Symbolic link
Просмотреть файл

@ -0,0 +1 @@
node_modules/.bin/forever

Просмотреть файл

@ -0,0 +1,22 @@
{
"name": "p2p",
"version": "0.0.1-21",
"private": true,
"scripts": {
"start": "node broker/p2p-broker.js",
"install-windows-service": "winser -i",
"uninstall-windows-service": "winser -r"
},
"dependencies": {
"lodash": "~1.0.1",
"forever": "~0.10.0",
"socket.io": "~0.9.13",
"uglify-js": "~2.2.5",
"winser": "~0.1.3"
},
"engines": {
"node": "0.8.x",
"npm": "1.1.x"
},
"subdomain": "wrtcb"
}