diff --git a/js/demo/UDP_Worker.js b/js/demo/UDP_Worker.js new file mode 100644 index 0000000..9a87dfe --- /dev/null +++ b/js/demo/UDP_Worker.js @@ -0,0 +1,113 @@ +// UDP Hole Punching +// usage UDP_Worker [] + +var dgram = require('dgram'); +var net = require('net'); + +var clientName = process.argv[3]; +var remoteName = process.argv[4]; + +var rendezvous = { + address: process.argv[2], + port: 10001 +}; + +var http = { + address:'134.102.219.3', + port:80 +} +var client = { + ack: false, + connection: {} +}; + +var udp = dgram.createSocket('udp4'); + +var getNetworkIP = function(callback) { + var socket = net.createConnection(http.port, http.address||rendezvous.address); + socket.on('connect', function() { + callback(undefined, socket.address().address); + socket.end(); + }); + socket.on('error', function(e) { + callback(e, 'error'); + }); +} + +udp.on("listening", function() { + var linfo = { port: udp.address().port }; + getNetworkIP(function(error, ip) { + if (error) return console.log("! Unable to obtain connection information!"); + linfo.address = ip; + console.log('# listening as %s@%s:%s', clientName, linfo.address, linfo.port); + send(rendezvous, { type: 'register', name: clientName, linfo: linfo }, function() { + if (remoteName) { + send(rendezvous, { type: 'connect', from: clientName, to: remoteName }); + } + }); + }); +}); + +udp.on('message', function(data, rinfo) { + try { + data = JSON.parse(data); + } catch (e) { + console.log('! Couldn\'t parse data(%s):\n%s', e, data); + return; + } + if (data.type == 'connection') { + console.log('# connecting with %s@[%s:%s | %s:%s]', data.client.name, + data.client.connections.local.address, data.client.connections.local.port, data.client.connections.public.address, data.client.connections.public.port); + remoteName = data.client.name; + client.ack = false; + var punch = { type: 'punch', from: clientName, to: remoteName }; + for (var con in data.client.connections) { + doUntilAck(1000, function() { + send(data.client.connections[con], punch); + }); + } + } else if (data.type == 'punch' && data.to == clientName) { + var ack = { type: 'ack', from: clientName }; + console.log("# got punch, sending ACK"); + send(rinfo, ack); + } else if (data.type == 'ack' && !client.ack) { + client.ack = true; + client.connection = rinfo; + console.log("# got ACK, sending MSG"); + send(client.connection, { + type: 'message', + from: clientName, + msg: 'Hello World, '+remoteName+'!' + }); + } else if (data.type == 'message') { + console.log('> %s [from %s@%s:%s]', data.msg, data.from, rinfo.address, rinfo.port) + } else if (data.type == 'cack') { + console.log('# got cack [from %s@%s:%s]', data.from, rinfo.address, rinfo.port) + + } +}); + + +function send(connection, msg, cb) { + var data = new Buffer(JSON.stringify(msg)); + + udp.send(data, 0, data.length, connection.port, connection.address, function(err, bytes) { + if (err) { + udp.close(); + console.log('# stopped due to error: %s', err); + } else { + console.log('# sent %s to %s:%s', msg.type, connection.address, connection.port); + if (cb) cb(); + } + }); +} + +function doUntilAck(interval, fn) { + if (client.ack) return; + fn(); + setTimeout(function() { + doUntilAck(interval, fn); + }, interval); +} + +udp.bind();