Datagram socket refactor. Add tests and documentation.
Support setTTL() and setBroadcast() socket options.
This commit is contained in:
parent
e7c4f8cdaa
commit
4e50197e53
264
doc/api.markdown
264
doc/api.markdown
@ -2351,97 +2351,6 @@ initialDelay will leave the value unchanged from the default
|
||||
|
||||
|
||||
|
||||
## dgram
|
||||
|
||||
This class is used to create datagram sockets, for sending and receiving UDP
|
||||
and UNIX daemon sockets.
|
||||
|
||||
An server listening for UDP packets on port 8125:
|
||||
|
||||
var dgram = require('dgram');
|
||||
var server = dgram.createSocket(function (msg, rinfo) {
|
||||
console.log("connection from " + rinfo.address + ":"+ rinfo.port);
|
||||
console.log("server got: " + msg);
|
||||
});
|
||||
server.bind(8125, 'localhost');
|
||||
|
||||
To listen on the socket `'/tmp/echo.sock'`, change the last line:
|
||||
|
||||
server.bind('/tmp/echo.sock');
|
||||
|
||||
A client which sends UDP packets to port 8125:
|
||||
|
||||
var dgram = require("dgram");
|
||||
|
||||
var Buffer = require('buffer').Buffer;
|
||||
var buf = new Buffer('hello');
|
||||
|
||||
var client = dgram.createSocket();
|
||||
client.send(8125, 'localhost', buf, 0, buf.length);
|
||||
|
||||
Note the use of a `Buffer` rather than a string object.
|
||||
|
||||
|
||||
### Event: 'listening'
|
||||
|
||||
`function () {}`
|
||||
|
||||
Emitted when a server has finished its bind and is ready to receive data.
|
||||
|
||||
### Event: 'message'
|
||||
|
||||
`function (msg, rinfo) {}`
|
||||
|
||||
Emitted when a socket receives data. msg is a `Buffer`, not a string. rinfo.port and rinfo.address contains the sender's port and IP.
|
||||
|
||||
### Event: 'error'
|
||||
|
||||
`function (exception) { }`
|
||||
|
||||
An error on the socket will emit this event.
|
||||
|
||||
### Event: 'close'
|
||||
|
||||
`function () {}`
|
||||
|
||||
Emitted when the socket closes.
|
||||
|
||||
|
||||
### dgram.createSocket(messageListener)
|
||||
|
||||
Creates a new dgram socket. The `messageListener` argument is
|
||||
automatically set as a listener for the `'message'` event.
|
||||
|
||||
|
||||
### socket.bind(port, host=null)
|
||||
|
||||
Begin accepting connections on the specified `port` and `host`. If the
|
||||
`host` is omitted, the server will accept connections directed to any
|
||||
IPv4 address (`INADDR_ANY`).
|
||||
|
||||
|
||||
### socket.bind(path)
|
||||
|
||||
Start a UNIX socket server listening for connections on the given `path`.
|
||||
|
||||
|
||||
### socket.send(port, addr, buffer, offset, length)
|
||||
|
||||
Send a packet over the socket to a port and host or IP. `port` and `addr` define the destination, `buffer` should be a `Buffer` object, and offset and length give the start bytes and total bytes to send in this packet.
|
||||
|
||||
|
||||
### socket.send(path, _, buffer, offset, length)
|
||||
|
||||
Send a packet over the socket to a UNIX daemon socket. `path` defines the destination, and the second argument is unused. `buffer` should be a `Buffer` object, and offset and length give the start bytes and total bytes to send in this packet.
|
||||
|
||||
|
||||
### socket.close()
|
||||
|
||||
Close the socket. This function is asynchronous, the server is finally closed
|
||||
when the server emits a `'close'` event.
|
||||
|
||||
|
||||
|
||||
## Crypto
|
||||
|
||||
Use `require('crypto')` to access this module.
|
||||
@ -2648,6 +2557,177 @@ Each DNS query can return an error code.
|
||||
- `dns.BADQUERY`: the query is malformed.
|
||||
|
||||
|
||||
## dgram
|
||||
|
||||
Datagram sockets are available through `require('dgram')`. Datagrams are most commonly
|
||||
handled as IP/UDP messages, but they can also be used over Unix domain sockets.
|
||||
|
||||
### Event: 'message'
|
||||
|
||||
`function (msg, rinfo) { }`
|
||||
|
||||
Emitted when a new datagram is available on a socket. `msg` is a `Buffer` and `rinfo` is
|
||||
an object with the sender's address information and the number of bytes in the datagram.
|
||||
|
||||
### Event: 'listening'
|
||||
|
||||
`function () { }`
|
||||
|
||||
Emitted when a socket starts listening for datagrams. This happens as soon as UDP sockets
|
||||
are created. Unix domain sockets do not start listening until calling `bind()` on them.
|
||||
|
||||
### Event: 'close'
|
||||
|
||||
`function () { }`
|
||||
|
||||
Emitted when a socket is closed with `close()`. No new `message` events will be emitted
|
||||
on this socket.
|
||||
|
||||
### dgram.createSocket(type [, callback])
|
||||
|
||||
Creates a datagram socket of the specified types. Valid types are:
|
||||
`udp4`, `udp6`, and `unix_dgram`.
|
||||
|
||||
Takes an optional callback which is added as a listener for `message` events.
|
||||
|
||||
### dgram.send(buf, offset, length, path [, callback])
|
||||
|
||||
For Unix domain datagram sockets, the destination address is a pathname in the filesystem.
|
||||
An optional callback may be supplied that is invoked after the `sendto` call is completed
|
||||
by the OS. It is not safe to re-use `buf` until the callback is invoked. Note that
|
||||
unless the socket is bound to a pathname with `bind()` there is no way to receive messages
|
||||
on this socket.
|
||||
|
||||
Example of sending a message to syslogd on OSX via Unix domain socket `/var/run/syslog`:
|
||||
|
||||
var dgram = require('dgram'),
|
||||
Buffer = require('buffer').Buffer,
|
||||
client, message;
|
||||
|
||||
message = new Buffer("A message to log.");
|
||||
client = dgram.createSocket("unix_dgram");
|
||||
client.send(message, 0, message.length, "/var/run/syslog",
|
||||
function (err, bytes) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
console.log("Wrote " + bytes + " bytes to socket.");
|
||||
});
|
||||
|
||||
### dgram.send(buf, offset, length, port, address [, callback])
|
||||
|
||||
For UDP sockets, the destination port and IP address must be specified. A string
|
||||
may be supplied for the `address` parameter, and it will be resolved with DNS. An
|
||||
optional callback may be specified to detect any DNS errors and when `buf` may be
|
||||
re-used. Note that DNS lookups will delay the time that a send takes place, at
|
||||
least until the next tick. The only way to know for sure that a send has taken place
|
||||
is to use the callback.
|
||||
|
||||
Example of sending a UDP packet to a random port on `localhost`;
|
||||
|
||||
var dgram = require('dgram'),
|
||||
Buffer = require('buffer').Buffer,
|
||||
client, message;
|
||||
|
||||
message = new Buffer("Some bytes");
|
||||
client = dgram.createSocket("udp4");
|
||||
client.send(message, 0, message.length, 41234, "localhost");
|
||||
client.close();
|
||||
|
||||
### dgram.bind(path)
|
||||
|
||||
For Unix domain datagram sockets, start listening for incoming datagrams on a
|
||||
socket specified by `path`. Note that clients may `send()` without `bind()`,
|
||||
but no datagrams will be received without a `bind()`.
|
||||
|
||||
Example of a Unix domain datagram server that echoes back all messages it receives:
|
||||
|
||||
var Buffer = require("buffer").Buffer,
|
||||
dgram = require("dgram"), server
|
||||
server_path = "/tmp/dgram_server_sock";
|
||||
|
||||
server = dgram.createSocket("unix_dgram");
|
||||
server.on("message", function (msg, rinfo) {
|
||||
console.log("got: " + msg + " from " + rinfo.address);
|
||||
server.send(msg, 0, msg.length, rinfo.address);
|
||||
});
|
||||
server.on("listening", function () {
|
||||
console.log("server listening " + server.address().address);
|
||||
})
|
||||
server.bind(server_path);
|
||||
|
||||
Example of a Unix domain datagram client that talks to this server:
|
||||
|
||||
var Buffer = require("buffer").Buffer,
|
||||
dgram = require("dgram"),
|
||||
server_path = "/tmp/dgram_server_sock",
|
||||
client_path = "/tmp/dgram_client_sock", client, message;
|
||||
|
||||
message = new Buffer("A message at " + (new Date()));
|
||||
|
||||
client = dgram.createSocket("unix_dgram");
|
||||
client.on("message", function (msg, rinfo) {
|
||||
console.log("got: " + msg + " from " + rinfo.address);
|
||||
});
|
||||
client.on("listening", function () {
|
||||
console.log("client listening " + client.address().address);
|
||||
client.send(message, 0, message.length, server_path);
|
||||
});
|
||||
client.bind(client_path);
|
||||
|
||||
### dgram.bind(port [, address])
|
||||
|
||||
For UDP sockets, listen for datagrams on a named `port` and optional `address`. If
|
||||
`address` is not specified, the OS will try to listen on all addresses.
|
||||
|
||||
Example of a UDP server listening on port 41234:
|
||||
|
||||
var Buffer = require("buffer").Buffer,
|
||||
dgram = require("dgram"), server,
|
||||
message_to_send = new Buffer("A message to send");
|
||||
|
||||
server = dgram.createSocket("udp4");
|
||||
server.on("message", function (msg, rinfo) {
|
||||
console.log("server got: " + msg + " from " +
|
||||
rinfo.address + ":" + rinfo.port);
|
||||
});
|
||||
server.on("listening", function () {
|
||||
var address = server.address();
|
||||
console.log("server listening " +
|
||||
address.address + ":" + address.port);
|
||||
});
|
||||
server.bind(41234);
|
||||
// server listening 0.0.0.0:41234
|
||||
|
||||
|
||||
### dgram.close()
|
||||
|
||||
Close the underlying socket and stop listening for data on it. UDP sockets
|
||||
automatically listen for messages, even if they did not call `bind()`.
|
||||
|
||||
### dgram.address()
|
||||
|
||||
Returns an object containing the address information for a socket. For UDP sockets,
|
||||
this object will contain `address` and `port`. For Unix domain sockets, it will contain
|
||||
only `address`.
|
||||
|
||||
### dgram.setBroadcast(flag)
|
||||
|
||||
Sets or clears the `SO_BROADCAST` socket option. When this option is set, UDP packets
|
||||
may be sent to a local interface's broadcast address.
|
||||
|
||||
### dgram.setTTL(ttl)
|
||||
|
||||
Sets the `IP_TTL` socket option. TTL stands for "Time to Live," but in this context it
|
||||
specifies the number of IP hops that a packet is allowed to go through. Each router or
|
||||
gateway that forwards a packet decrements the TTL. If the TTL is decremented to 0 by a
|
||||
router, it will not be forwarded. Changing TTL values is typically done for network
|
||||
probes or when multicasting.
|
||||
|
||||
The argument to `setTTL()` is a number of hops between 1 and 255. The default on most
|
||||
systems is 64.
|
||||
|
||||
|
||||
## Assert
|
||||
|
||||
This module is used for writing unit tests for your applications, you can
|
||||
@ -3157,4 +3237,4 @@ All Node addons must export a function called `init` with this signature:
|
||||
extern 'C' void init (Handle<Object> target)
|
||||
|
||||
For the moment, that is all the documentation on addons. Please see
|
||||
<http://github.com/ry/node_postgres> for a real example.
|
||||
<http://github.com/ry/node_postgres> for a real example.
|
||||
|
247
lib/dgram.js
247
lib/dgram.js
@ -7,12 +7,9 @@ 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;
|
||||
var setBroadcast = binding.setBroadcast;
|
||||
|
||||
function isPort (x) { return parseInt(x) >= 0; }
|
||||
var pool = null;
|
||||
@ -31,18 +28,19 @@ function getPool() {
|
||||
return pool;
|
||||
}
|
||||
|
||||
function Socket (broadcast, listener) {
|
||||
function Socket (type, listener) {
|
||||
events.EventEmitter.call(this);
|
||||
var self = this;
|
||||
|
||||
if (typeof(broadcast) != 'boolean') {
|
||||
listener = broadcast;
|
||||
broadcast = false;
|
||||
self.type = type;
|
||||
if (type === "unix_dgram" || type === "udp4" || type === "udp6") {
|
||||
self.fd = socket(self.type);
|
||||
} else {
|
||||
throw new Error("Bad socket type specified. Valid types are: unix_dgram, udp4, udp6");
|
||||
}
|
||||
self.broadcast = broadcast;
|
||||
|
||||
if (listener) {
|
||||
self.addListener('message', listener);
|
||||
if (typeof listener === 'function') {
|
||||
self.on('message', listener);
|
||||
}
|
||||
|
||||
self.watcher = new IOWatcher();
|
||||
@ -59,156 +57,173 @@ function Socket (broadcast, listener) {
|
||||
p.used += rinfo.size;
|
||||
}
|
||||
};
|
||||
|
||||
if (self.type === "udp4" || self.type === "udp6") {
|
||||
self._startWatcher();
|
||||
}
|
||||
}
|
||||
|
||||
sys.inherits(Socket, events.EventEmitter);
|
||||
exports.Socket = Socket;
|
||||
|
||||
exports.createSocket = function (broadcast, listener) {
|
||||
return new Socket(broadcast, listener);
|
||||
exports.createSocket = function (type, listener) {
|
||||
return new Socket(type, 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;
|
||||
}
|
||||
if (this.type === "unix_dgram") { // bind(path)
|
||||
if (typeof arguments[0] !== "string") {
|
||||
throw new Error("unix_dgram sockets must be bound to a path in the filesystem");
|
||||
}
|
||||
this.path = arguments[0];
|
||||
|
||||
fs.unlink(this.path, function (err) { // unlink old file, OK if it doesn't exist
|
||||
if (err && err.errno !== ENOENT) {
|
||||
throw err;
|
||||
} 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();
|
||||
});
|
||||
}
|
||||
});
|
||||
try {
|
||||
binding.bind(self.fd, self.path);
|
||||
self._startWatcher();
|
||||
self.emit("listening");
|
||||
} catch (err) {
|
||||
console.log("Error in unix_dgram bind of " + self.path);
|
||||
console.log(err.stack);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
});
|
||||
} 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 if (this.type === "udp4" || this.type === "udp6") { // bind(port, [address])
|
||||
if (arguments[1] === undefined) {
|
||||
// Not bind()ing a specific address. Use INADDR_ANY and OS will pick one.
|
||||
// The address can be found with server.address()
|
||||
binding.bind(self.fd, arguments[0]);
|
||||
this.emit("listening");
|
||||
} else {
|
||||
// the first argument is the port, the second an address
|
||||
this.port = arguments[0];
|
||||
if (dns.isIP(arguments[1])) {
|
||||
this.address = arguments[1];
|
||||
binding.bind(self.fd, port, arguments[1]);
|
||||
this.emit("listening");
|
||||
} else {
|
||||
self.type = addressType == 4 ? 'udp4' : 'udp6';
|
||||
self.fd = socket(self.type);
|
||||
bind(self.fd, port, ip);
|
||||
process.nextTick(function() {
|
||||
self._startWatcher();
|
||||
dns.lookup(arguments[1], function (err, ip, addressType) {
|
||||
// TODO - only look up proper record for address family
|
||||
if (err) {
|
||||
self.emit('error', err);
|
||||
} else {
|
||||
self.ip = ip;
|
||||
binding.bind(self.fd, self.port, ip);
|
||||
self.emit("listening");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Socket.prototype._startWatcher = function () {
|
||||
this.watcher.set(this.fd, true, false);
|
||||
this.watcher.start();
|
||||
this.emit("listening");
|
||||
if (! this._watcherStarted) {
|
||||
this.watcher.set(this.fd, true, false); // listen for read ready, not write ready
|
||||
this.watcher.start();
|
||||
this._watcherStarted = true;
|
||||
}
|
||||
};
|
||||
|
||||
Socket.prototype.address = function () {
|
||||
return getsockname(this.fd);
|
||||
return binding.getsockname(this.fd);
|
||||
};
|
||||
|
||||
Socket.prototype.send = function(port, addr, buffer, offset, length) {
|
||||
Socket.prototype.setBroadcast = function(arg) {
|
||||
if (arg) {
|
||||
return binding.setBroadcast(this.fd, 1);
|
||||
} else {
|
||||
return binding.setBroadcast(this.fd, 0);
|
||||
}
|
||||
};
|
||||
|
||||
Socket.prototype.setTTL = function(arg) {
|
||||
var newttl = parseInt(arg);
|
||||
|
||||
if (newttl > 0 && newttl < 256) {
|
||||
return binding.setTTL(this.fd, newttl);
|
||||
} else {
|
||||
throw new Error("New TTL must be between 1 and 255");
|
||||
}
|
||||
};
|
||||
|
||||
// translate arguments from JS API into C++ API, after optional DNS lookup
|
||||
Socket.prototype.send = function(buffer, offset, length) {
|
||||
var self = this;
|
||||
|
||||
var lastArg = arguments[arguments.length - 1];
|
||||
var callback = typeof lastArg === 'function' ? lastArg : null;
|
||||
if (typeof offset !== "number" || typeof length !== "number") {
|
||||
throw new Error("send takes offset and length as args 2 and 3");
|
||||
}
|
||||
|
||||
if (!isPort(arguments[0])) {
|
||||
try {
|
||||
if (!self.fd) {
|
||||
self.type = 'unix_dgram';
|
||||
self.fd = socket(self.type);
|
||||
}
|
||||
var bytes = sendto(self.fd, buffer, offset, length, 0, port, addr);
|
||||
} catch (e) {
|
||||
if (callback) callback(e);
|
||||
return;
|
||||
if (this.type === "unix_dgram") { // send(buffer, offset, length, path [, callback])
|
||||
if (typeof arguments[3] !== "string") {
|
||||
throw new Error("unix_dgram sockets must send to a path in the filesystem");
|
||||
}
|
||||
|
||||
if (callback) callback(null, bytes);
|
||||
|
||||
} else {
|
||||
dns.lookup(arguments[1], function (err, ip, addressType) {
|
||||
// DNS error
|
||||
if (err) {
|
||||
if (callback) callback(err);
|
||||
self.emit('error', err);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (!self.fd) {
|
||||
self.type = addressType == 4 ? 'udp4' : 'udp6';
|
||||
self.fd = socket(self.type);
|
||||
setBroadcast(self.fd, self.broadcast);
|
||||
process.nextTick(function() {
|
||||
self._startWatcher();
|
||||
});
|
||||
self.sendto(buffer, offset, length, arguments[3], null, arguments[4]);
|
||||
} else if (this.type === "udp4" || this.type === "udp6") { // send(buffer, offset, length, port, address [, callback])
|
||||
if (typeof arguments[4] !== "string") {
|
||||
throw new Error(this.type + " sockets must send to port, address");
|
||||
}
|
||||
|
||||
if (dns.isIP(arguments[4])) {
|
||||
self.sendto(arguments[0], arguments[1], arguments[2], arguments[3], arguments[4], arguments[5]);
|
||||
} else {
|
||||
var port = arguments[3],
|
||||
callback = arguments[5];
|
||||
|
||||
// TODO - only look up proper record for address family
|
||||
dns.lookup(arguments[4], function (err, ip, addressType) {
|
||||
if (err) { // DNS error
|
||||
if (callback) {
|
||||
callback(err);
|
||||
}
|
||||
self.emit('error', err);
|
||||
return;
|
||||
}
|
||||
var bytes = sendto(self.fd, buffer, offset, length, 0, port, ip);
|
||||
} catch (err) {
|
||||
// socket creation, or sendto error.
|
||||
if (callback) callback(err);
|
||||
return;
|
||||
}
|
||||
self.sendto(buffer, offset, length, port, ip, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (callback) callback(null, bytes);
|
||||
});
|
||||
Socket.prototype.sendto = function(buffer, offset, length, port, addr, callback) {
|
||||
try {
|
||||
var bytes = binding.sendto(this.fd, buffer, offset, length, 0, port, addr);
|
||||
} catch (err) {
|
||||
if (callback) {
|
||||
callback(err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback(null, bytes);
|
||||
}
|
||||
};
|
||||
|
||||
Socket.prototype.close = function () {
|
||||
var self = this;
|
||||
|
||||
if (!self.fd) throw new Error('Not running');
|
||||
if (!this.fd) throw new Error('Not running');
|
||||
|
||||
self.watcher.stop();
|
||||
this.watcher.stop();
|
||||
this._watcherStarted = false;
|
||||
|
||||
close(self.fd);
|
||||
self.fd = null;
|
||||
close(this.fd);
|
||||
this.fd = null;
|
||||
|
||||
if (self.type === "unix_dgram") {
|
||||
fs.unlink(self.path, function () {
|
||||
if (this.type === "unix_dgram" && this.path) {
|
||||
fs.unlink(this.path, function () {
|
||||
self.emit("close");
|
||||
});
|
||||
} else {
|
||||
self.emit("close");
|
||||
this.emit("close");
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -71,7 +71,7 @@ var channel = new dns.Channel({SOCK_STATE_CB: function (socket, read, write) {
|
||||
updateTimer();
|
||||
}});
|
||||
|
||||
|
||||
exports.isIP = dns.isIP;
|
||||
|
||||
exports.resolve = function (domain, type_, callback_) {
|
||||
var type, callback;
|
||||
|
@ -355,6 +355,7 @@ do { \
|
||||
int port; \
|
||||
struct sockaddr_in *a4; \
|
||||
struct sockaddr_in6 *a6; \
|
||||
struct sockaddr_un *au; \
|
||||
switch ((address_storage).ss_family) { \
|
||||
case AF_INET6: \
|
||||
a6 = (struct sockaddr_in6*)&(address_storage); \
|
||||
@ -370,6 +371,13 @@ do { \
|
||||
(info)->Set(address_symbol, String::New(ip)); \
|
||||
(info)->Set(port_symbol, Integer::New(port)); \
|
||||
break; \
|
||||
case AF_UNIX: \
|
||||
au = (struct sockaddr_un*)&(address_storage); \
|
||||
char un_path[105]; \
|
||||
strncpy(un_path, au->sun_path, au->sun_len-2); \
|
||||
un_path[au->sun_len-2] = 0; \
|
||||
(info)->Set(address_symbol, String::New(un_path)); \
|
||||
break; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
@ -971,22 +979,6 @@ static Handle<Value> SetNoDelay(const Arguments& args) {
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
static Handle<Value> SetBroadcast(const Arguments& args) {
|
||||
int flags, r;
|
||||
HandleScope scope;
|
||||
|
||||
FD_ARG(args[0])
|
||||
|
||||
flags = args[1]->IsFalse() ? 0 : 1;
|
||||
r = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (void *)&flags, sizeof(flags));
|
||||
|
||||
if (r < 0) {
|
||||
return ThrowException(ErrnoException(errno, "setsockopt"));
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
|
||||
static Handle<Value> SetKeepAlive(const Arguments& args) {
|
||||
int r;
|
||||
HandleScope scope;
|
||||
@ -1018,6 +1010,53 @@ static Handle<Value> SetKeepAlive(const Arguments& args) {
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
static Handle<Value> SetBroadcast(const Arguments& args) {
|
||||
int flags, r;
|
||||
HandleScope scope;
|
||||
|
||||
FD_ARG(args[0])
|
||||
|
||||
flags = args[1]->IsFalse() ? 0 : 1;
|
||||
r = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (void *)&flags, sizeof(flags));
|
||||
|
||||
if (r < 0) {
|
||||
return ThrowException(ErrnoException(errno, "setsockopt"));
|
||||
} else {
|
||||
return scope.Close(Integer::New(flags));
|
||||
}
|
||||
}
|
||||
|
||||
static Handle<Value> SetTTL(const Arguments& args) {
|
||||
HandleScope scope;
|
||||
|
||||
if (args.Length() != 2) {
|
||||
return ThrowException(Exception::TypeError(
|
||||
String::New("Takes exactly two arguments: fd, new TTL")));
|
||||
}
|
||||
|
||||
FD_ARG(args[0]);
|
||||
|
||||
if (! args[1]->IsInt32()) {
|
||||
return ThrowException(Exception::TypeError(
|
||||
String::New("Argument must be a number")));
|
||||
}
|
||||
|
||||
int newttl = args[1]->Int32Value();
|
||||
if (newttl < 1 || newttl > 255) {
|
||||
return ThrowException(Exception::TypeError(
|
||||
String::New("new TTL must be between 1 and 255")));
|
||||
}
|
||||
|
||||
int r = setsockopt(fd, IPPROTO_IP, IP_TTL, (void *)&newttl, sizeof(newttl));
|
||||
|
||||
if (r < 0) {
|
||||
return ThrowException(ErrnoException(errno, "setsockopt"));
|
||||
} else {
|
||||
return scope.Close(Integer::New(newttl));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// G E T A D D R I N F O
|
||||
//
|
||||
@ -1241,6 +1280,7 @@ void InitNet(Handle<Object> target) {
|
||||
NODE_SET_METHOD(target, "toRead", ToRead);
|
||||
NODE_SET_METHOD(target, "setNoDelay", SetNoDelay);
|
||||
NODE_SET_METHOD(target, "setBroadcast", SetBroadcast);
|
||||
NODE_SET_METHOD(target, "setTTL", SetTTL);
|
||||
NODE_SET_METHOD(target, "setKeepAlive", SetKeepAlive);
|
||||
NODE_SET_METHOD(target, "getsockname", GetSockName);
|
||||
NODE_SET_METHOD(target, "getpeername", GetPeerName);
|
||||
|
@ -4,85 +4,82 @@ var dgram = require("dgram");
|
||||
|
||||
var tests_run = 0;
|
||||
|
||||
|
||||
function pingPongTest (port, host) {
|
||||
var callbacks = 0;
|
||||
var N = 500;
|
||||
var count = 0;
|
||||
var sent_final_ping = false;
|
||||
|
||||
var server = dgram.createSocket(function (msg, rinfo) {
|
||||
console.log("connection: " + rinfo.address + ":"+ rinfo.port);
|
||||
|
||||
console.log("server got: " + msg);
|
||||
var server = dgram.createSocket("udp4", function (msg, rinfo) {
|
||||
console.log("server got: " + msg + " from " + rinfo.address + ":" + rinfo.port);
|
||||
|
||||
if (/PING/.exec(msg)) {
|
||||
var buf = new Buffer(4);
|
||||
buf.write('PONG');
|
||||
server.send(rinfo.port, rinfo.address, buf, 0, buf.length, function (err, sent) {
|
||||
server.send(buf, 0, buf.length, rinfo.port, rinfo.address, function (err, sent) {
|
||||
callbacks++;
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
server.addListener("error", function (e) {
|
||||
server.on("error", function (e) {
|
||||
throw e;
|
||||
});
|
||||
|
||||
server.bind(port, host);
|
||||
|
||||
server.addListener("listening", function () {
|
||||
server.on("listening", function () {
|
||||
console.log("server listening on " + port + " " + host);
|
||||
|
||||
var buf = new Buffer(4);
|
||||
buf.write('PING');
|
||||
|
||||
var client = dgram.createSocket();
|
||||
var buf = new Buffer('PING'),
|
||||
client = dgram.createSocket("udp4");
|
||||
|
||||
client.addListener("message", function (msg, rinfo) {
|
||||
console.log("client got: " + msg);
|
||||
console.log("client got: " + msg + " from " + rinfo.address + ":" + rinfo.port);
|
||||
assert.equal("PONG", msg.toString('ascii'));
|
||||
|
||||
count += 1;
|
||||
|
||||
if (count < N) {
|
||||
client.send(port, host, buf, 0, buf.length);
|
||||
client.send(buf, 0, buf.length, port, "localhost");
|
||||
} else {
|
||||
sent_final_ping = true;
|
||||
client.send(port, host, buf, 0, buf.length);
|
||||
client.send(buf, 0, buf.length, port, "localhost");
|
||||
process.nextTick(function() {
|
||||
client.close();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
client.addListener("close", function () {
|
||||
console.log('client.close');
|
||||
client.on("close", function () {
|
||||
console.log('client has closed, closing server');
|
||||
assert.equal(N, count);
|
||||
tests_run += 1;
|
||||
server.close();
|
||||
assert.equal(N-1, callbacks);
|
||||
});
|
||||
|
||||
client.addListener("error", function (e) {
|
||||
client.on("error", function (e) {
|
||||
throw e;
|
||||
});
|
||||
|
||||
client.send(port, host, buf, 0, buf.length);
|
||||
console.log("Client sending to " + port + ", localhost " + buf);
|
||||
client.send(buf, 0, buf.length, port, "localhost", function (err, bytes) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
console.log("Client sent " + bytes + " bytes");
|
||||
});
|
||||
count += 1;
|
||||
});
|
||||
|
||||
server.bind(port, host);
|
||||
}
|
||||
|
||||
/* All are run at once, so run on different ports */
|
||||
// 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);
|
||||
assert.equal(3, tests_run);
|
||||
console.log('done');
|
||||
});
|
||||
|
52
test/simple/test-dgram-udp4.js
Normal file
52
test/simple/test-dgram-udp4.js
Normal file
@ -0,0 +1,52 @@
|
||||
require("../common");
|
||||
|
||||
var Buffer = require("buffer").Buffer,
|
||||
fs = require("fs"),
|
||||
dgram = require("dgram"), server, client,
|
||||
server_port = 20989,
|
||||
message_to_send = new Buffer("A message to send"),
|
||||
timer;
|
||||
|
||||
server = dgram.createSocket("udp4");
|
||||
server.on("message", function (msg, rinfo) {
|
||||
console.log("server got: " + msg + " from " + rinfo.address + ":" + rinfo.port);
|
||||
assert.strictEqual(rinfo.address, "127.0.0.1");
|
||||
assert.strictEqual(msg.toString(), message_to_send.toString());
|
||||
server.send(msg, 0, msg.length, rinfo.port, rinfo.address);
|
||||
});
|
||||
server.on("listening", function () {
|
||||
var address = server.address();
|
||||
console.log("server is listening on " + address.address + ":" + address.port);
|
||||
client = dgram.createSocket("udp4");
|
||||
client.on("message", function (msg, rinfo) {
|
||||
console.log("client got: " + msg + " from " + rinfo.address + ":" + address.port);
|
||||
assert.strictEqual(rinfo.address, "127.0.0.1");
|
||||
assert.strictEqual(rinfo.port, server_port);
|
||||
assert.strictEqual(msg.toString(), message_to_send.toString());
|
||||
client.close();
|
||||
server.close();
|
||||
});
|
||||
client.send(message_to_send, 0, message_to_send.length, server_port, "localhost", function (err, bytes) {
|
||||
if (err) {
|
||||
console.log("Caught error in client send.");
|
||||
throw err;
|
||||
}
|
||||
console.log("client wrote " + bytes + " bytes.");
|
||||
assert.strictEqual(bytes, message_to_send.length);
|
||||
});
|
||||
client.on("close", function () {
|
||||
if (server.fd === null) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
});
|
||||
});
|
||||
server.on("close", function () {
|
||||
if (client.fd === null) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
});
|
||||
server.bind(server_port);
|
||||
|
||||
timer = setTimeout(function () {
|
||||
throw new Error("Timeout");
|
||||
}, 200);
|
56
test/simple/test-dgram-unix-anon.js
Normal file
56
test/simple/test-dgram-unix-anon.js
Normal file
@ -0,0 +1,56 @@
|
||||
require("../common");
|
||||
|
||||
var Buffer = require("buffer").Buffer,
|
||||
fs = require("fs"),
|
||||
dgram = require("dgram"), server, client,
|
||||
server_path = "/tmp/dgram_server_sock",
|
||||
messages_to_send = [
|
||||
new Buffer("First message to send"),
|
||||
new Buffer("Second message to send"),
|
||||
new Buffer("Third message to send"),
|
||||
new Buffer("Fourth message to send")
|
||||
],
|
||||
timer;
|
||||
|
||||
server = dgram.createSocket("unix_dgram");
|
||||
server.bind(server_path);
|
||||
server.messages = [];
|
||||
server.on("message", function (msg, rinfo) {
|
||||
console.log("server got: " + msg);
|
||||
assert.strictEqual(rinfo.address, ""); // anon client sending
|
||||
server.messages.push(msg.toString());
|
||||
if (server.messages.length === messages_to_send.length) {
|
||||
server.messages.forEach(function (m, i) {
|
||||
assert.strictEqual(m, messages_to_send[i].toString());
|
||||
});
|
||||
server.close();
|
||||
client.close();
|
||||
}
|
||||
});
|
||||
server.on("listening", function () {
|
||||
console.log("server is listening");
|
||||
client = dgram.createSocket("unix_dgram");
|
||||
messages_to_send.forEach(function (m) {
|
||||
client.send(m, 0, m.length, server_path, function (err, bytes) {
|
||||
if (err) {
|
||||
console.log("Caught error in client send.");
|
||||
throw err;
|
||||
}
|
||||
console.log("client wrote " + bytes + " bytes.");
|
||||
});
|
||||
});
|
||||
client.on("close", function () {
|
||||
if (server.fd === null) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
});
|
||||
});
|
||||
server.on("close", function () {
|
||||
if (client.fd === null) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
});
|
||||
|
||||
timer = setTimeout(function () {
|
||||
throw new Error("Timeout");
|
||||
}, 200);
|
55
test/simple/test-dgram-unix.js
Normal file
55
test/simple/test-dgram-unix.js
Normal file
@ -0,0 +1,55 @@
|
||||
require("../common");
|
||||
|
||||
var Buffer = require("buffer").Buffer,
|
||||
fs = require("fs"),
|
||||
dgram = require("dgram"), server, client,
|
||||
server_path = "/tmp/dgram_server_sock",
|
||||
client_path = "/tmp/dgram_client_sock",
|
||||
message_to_send = new Buffer("A message to send"),
|
||||
timer;
|
||||
|
||||
server = dgram.createSocket("unix_dgram");
|
||||
server.on("message", function (msg, rinfo) {
|
||||
console.log("server got: " + msg + " from " + rinfo.address);
|
||||
assert.strictEqual(rinfo.address, client_path);
|
||||
assert.strictEqual(msg.toString(), message_to_send.toString());
|
||||
server.send(msg, 0, msg.length, rinfo.address);
|
||||
});
|
||||
server.on("listening", function () {
|
||||
console.log("server is listening");
|
||||
client = dgram.createSocket("unix_dgram");
|
||||
client.on("message", function (msg, rinfo) {
|
||||
console.log("client got: " + msg + " from " + rinfo.address);
|
||||
assert.strictEqual(rinfo.address, server_path);
|
||||
assert.strictEqual(msg.toString(), message_to_send.toString());
|
||||
client.close();
|
||||
server.close();
|
||||
});
|
||||
client.on("listening", function () {
|
||||
console.log("client is listening");
|
||||
client.send(message_to_send, 0, message_to_send.length, server_path, function (err, bytes) {
|
||||
if (err) {
|
||||
console.log("Caught error in client send.");
|
||||
throw err;
|
||||
}
|
||||
console.log("client wrote " + bytes + " bytes.");
|
||||
assert.strictEqual(bytes, message_to_send.length);
|
||||
});
|
||||
});
|
||||
client.on("close", function () {
|
||||
if (server.fd === null) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
});
|
||||
client.bind(client_path);
|
||||
});
|
||||
server.on("close", function () {
|
||||
if (client.fd === null) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
});
|
||||
server.bind(server_path);
|
||||
|
||||
timer = setTimeout(function () {
|
||||
throw new Error("Timeout");
|
||||
}, 200);
|
Loading…
x
Reference in New Issue
Block a user