mirror of https://github.com/lukechilds/node.git
Browse Source
- Adds new dgram module, for all data-gram type transports - Supports both UDP client and servers - Supports Unix Daemon sockets in DGRAM mode too (think syslog) - Uses a shared Buffer and slices that as needed to be reasonably performant. - One supplied test program so far, test-dgram-pingpong - Passes test cases on osx 10.6 and ubuntu 9.10uv0.7.4-release
Paul Querna
15 years ago
committed by
Ryan Dahl
4 changed files with 425 additions and 0 deletions
@ -0,0 +1,184 @@ |
|||||
|
var sys = require("sys"); |
||||
|
var fs = require("fs"); |
||||
|
var events = require("events"); |
||||
|
var dns = require('dns'); |
||||
|
|
||||
|
var Buffer = require('buffer').Buffer; |
||||
|
var IOWatcher = process.IOWatcher; |
||||
|
var binding = process.binding('net'); |
||||
|
var socket = binding.socket; |
||||
|
var bind = binding.bind; |
||||
|
var recvfrom = binding.recvfrom; |
||||
|
var sendto = binding.sendto; |
||||
|
var close = binding.close; |
||||
|
var ENOENT = binding.ENOENT; |
||||
|
|
||||
|
function isPort (x) { return parseInt(x) >= 0; } |
||||
|
var pool = null; |
||||
|
|
||||
|
function getPool() { |
||||
|
/* TODO: this effectively limits you to 8kb maximum packet sizes */ |
||||
|
var minPoolAvail = 1024 * 8; |
||||
|
|
||||
|
var poolSize = 1024 * 64; |
||||
|
|
||||
|
if (pool === null || (pool.used + minPoolAvail > pool.length)) { |
||||
|
pool = new Buffer(poolSize); |
||||
|
pool.used = 0; |
||||
|
} |
||||
|
|
||||
|
return pool; |
||||
|
} |
||||
|
|
||||
|
function Socket (listener) { |
||||
|
events.EventEmitter.call(this); |
||||
|
var self = this; |
||||
|
|
||||
|
if (listener) { |
||||
|
self.addListener('message', listener); |
||||
|
} |
||||
|
|
||||
|
self.watcher = new IOWatcher(); |
||||
|
self.watcher.host = self; |
||||
|
self.watcher.callback = function () { |
||||
|
while (self.fd) { |
||||
|
var p = getPool(); |
||||
|
var rinfo = recvfrom(self.fd, p, p.used, p.length - p.used, 0); |
||||
|
|
||||
|
if (!rinfo) return; |
||||
|
|
||||
|
self.emit('message', p.slice(p.used, p.used + rinfo.size), rinfo); |
||||
|
|
||||
|
p.used += rinfo.size; |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
sys.inherits(Socket, events.EventEmitter); |
||||
|
exports.Socket = Socket; |
||||
|
|
||||
|
exports.createSocket = function (listener) { |
||||
|
return new Socket(listener); |
||||
|
}; |
||||
|
|
||||
|
Socket.prototype.bind = function () { |
||||
|
var self = this; |
||||
|
if (self.fd) throw new Error('Server already opened'); |
||||
|
|
||||
|
if (!isPort(arguments[0])) { |
||||
|
/* TODO: unix path dgram */ |
||||
|
self.fd = socket('unix_dgram'); |
||||
|
self.type = 'unix_dgram'; |
||||
|
var path = arguments[0]; |
||||
|
self.path = path; |
||||
|
// unlink sockfile if it exists
|
||||
|
fs.stat(path, function (err, r) { |
||||
|
if (err) { |
||||
|
if (err.errno == ENOENT) { |
||||
|
bind(self.fd, path); |
||||
|
process.nextTick(function() { |
||||
|
self._startWatcher(); |
||||
|
}); |
||||
|
} else { |
||||
|
throw r; |
||||
|
} |
||||
|
} else { |
||||
|
if (!r.isFile()) { |
||||
|
throw new Error("Non-file exists at " + path); |
||||
|
} else { |
||||
|
fs.unlink(path, function (err) { |
||||
|
if (err) { |
||||
|
throw err; |
||||
|
} else { |
||||
|
bind(self.fd, path); |
||||
|
process.nextTick(function() { |
||||
|
self._startWatcher(); |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} else if (!arguments[1]) { |
||||
|
// Don't bind(). OS will assign a port with INADDR_ANY.
|
||||
|
// The port can be found with server.address()
|
||||
|
self.type = 'udp4'; |
||||
|
self.fd = socket(self.type); |
||||
|
bind(self.fd, arguments[0]); |
||||
|
process.nextTick(function() { |
||||
|
self._startWatcher(); |
||||
|
}); |
||||
|
} else { |
||||
|
// the first argument is the port, the second an IP
|
||||
|
var port = arguments[0]; |
||||
|
dns.lookup(arguments[1], function (err, ip, addressType) { |
||||
|
if (err) { |
||||
|
self.emit('error', err); |
||||
|
} else { |
||||
|
self.type = addressType == 4 ? 'udp4' : 'udp6'; |
||||
|
self.fd = socket(self.type); |
||||
|
bind(self.fd, port, ip); |
||||
|
process.nextTick(function() { |
||||
|
self._startWatcher(); |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
Socket.prototype._startWatcher = function () { |
||||
|
this.watcher.set(this.fd, true, false); |
||||
|
this.watcher.start(); |
||||
|
this.emit("listening"); |
||||
|
}; |
||||
|
|
||||
|
Socket.prototype.address = function () { |
||||
|
return getsockname(this.fd); |
||||
|
}; |
||||
|
|
||||
|
Socket.prototype.send = function(port, addr, buffer, offset, length) { |
||||
|
var self = this; |
||||
|
|
||||
|
if (!isPort(arguments[0])) { |
||||
|
if (!self.fd) { |
||||
|
self.type = 'unix_dgram'; |
||||
|
self.fd = socket(self.type); |
||||
|
} |
||||
|
sendto(self.fd, buffer, offset, length, 0, port, addr); |
||||
|
} |
||||
|
else { |
||||
|
dns.lookup(arguments[1], function (err, ip, addressType) { |
||||
|
if (err) { |
||||
|
self.emit('error', err); |
||||
|
} else { |
||||
|
if (!self.fd) { |
||||
|
self.type = addressType == 4 ? 'udp4' : 'udp6'; |
||||
|
self.fd = socket(self.type); |
||||
|
process.nextTick(function() { |
||||
|
self._startWatcher(); |
||||
|
}); |
||||
|
} |
||||
|
sendto(self.fd, buffer, offset, length, 0, port, ip); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
Socket.prototype.close = function () { |
||||
|
var self = this; |
||||
|
|
||||
|
if (!self.fd) throw new Error('Not running'); |
||||
|
|
||||
|
self.watcher.stop(); |
||||
|
|
||||
|
close(self.fd); |
||||
|
self.fd = null; |
||||
|
|
||||
|
if (self.type === "unix_dgram") { |
||||
|
fs.unlink(self.path, function () { |
||||
|
self.emit("close"); |
||||
|
}); |
||||
|
} else { |
||||
|
self.emit("close"); |
||||
|
} |
||||
|
}; |
@ -0,0 +1,83 @@ |
|||||
|
require("../common"); |
||||
|
var Buffer = require('buffer').Buffer; |
||||
|
var dgram = require("dgram"); |
||||
|
|
||||
|
var tests_run = 0; |
||||
|
|
||||
|
function pingPongTest (port, host) { |
||||
|
var N = 500; |
||||
|
var count = 0; |
||||
|
var sent_final_ping = false; |
||||
|
|
||||
|
var server = dgram.createSocket(function (msg, rinfo) { |
||||
|
puts("connection: " + rinfo.address + ":"+ rinfo.port); |
||||
|
|
||||
|
puts("server got: " + msg); |
||||
|
|
||||
|
if (/PING/.exec(msg)) { |
||||
|
var buf = new Buffer(4); |
||||
|
buf.write('PONG'); |
||||
|
server.send(rinfo.port, rinfo.address, buf, 0, buf.length); |
||||
|
} |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
server.addListener("error", function (e) { |
||||
|
throw e; |
||||
|
}); |
||||
|
|
||||
|
server.bind(port, host); |
||||
|
|
||||
|
server.addListener("listening", function () { |
||||
|
puts("server listening on " + port + " " + host); |
||||
|
|
||||
|
var buf = new Buffer(4); |
||||
|
buf.write('PING'); |
||||
|
|
||||
|
var client = dgram.createSocket(); |
||||
|
|
||||
|
client.addListener("message", function (msg, rinfo) { |
||||
|
puts("client got: " + msg); |
||||
|
assert.equal("PONG", msg.toString('ascii')); |
||||
|
|
||||
|
count += 1; |
||||
|
|
||||
|
if (count < N) { |
||||
|
client.send(port, host, buf, 0, buf.length); |
||||
|
} else { |
||||
|
sent_final_ping = true; |
||||
|
client.send(port, host, buf, 0, buf.length); |
||||
|
process.nextTick(function() { |
||||
|
client.close(); |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
client.addListener("close", function () { |
||||
|
puts('client.close'); |
||||
|
assert.equal(N, count); |
||||
|
tests_run += 1; |
||||
|
server.close(); |
||||
|
}); |
||||
|
|
||||
|
client.addListener("error", function (e) { |
||||
|
throw e; |
||||
|
}); |
||||
|
|
||||
|
client.send(port, host, buf, 0, buf.length); |
||||
|
count += 1; |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
/* All are run at once, so run on different ports */ |
||||
|
pingPongTest(20989, "localhost"); |
||||
|
pingPongTest(20990, "localhost"); |
||||
|
pingPongTest(20988); |
||||
|
pingPongTest(20997, "::1"); |
||||
|
//pingPongTest("/tmp/pingpong.sock");
|
||||
|
|
||||
|
process.addListener("exit", function () { |
||||
|
assert.equal(4, tests_run); |
||||
|
puts('done'); |
||||
|
}); |
Loading…
Reference in new issue