From 4e50197e5330b7f1f603981f6dc817cac7f9a4f9 Mon Sep 17 00:00:00 2001 From: Matt Ranney Date: Mon, 5 Jul 2010 13:38:13 -0700 Subject: [PATCH] Datagram socket refactor. Add tests and documentation. Support setTTL() and setBroadcast() socket options. --- doc/api.markdown | 264 ++++++++++++++++++---------- lib/dgram.js | 249 ++++++++++++++------------ lib/dns.js | 2 +- src/node_net.cc | 72 ++++++-- test/simple/test-dgram-pingpong.js | 49 +++--- test/simple/test-dgram-udp4.js | 52 ++++++ test/simple/test-dgram-unix-anon.js | 56 ++++++ test/simple/test-dgram-unix.js | 55 ++++++ 8 files changed, 547 insertions(+), 252 deletions(-) create mode 100644 test/simple/test-dgram-udp4.js create mode 100644 test/simple/test-dgram-unix-anon.js create mode 100644 test/simple/test-dgram-unix.js diff --git a/doc/api.markdown b/doc/api.markdown index 85774dd582..1a937ab263 100644 --- a/doc/api.markdown +++ b/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 target) For the moment, that is all the documentation on addons. Please see - for a real example. \ No newline at end of file + for a real example. diff --git a/lib/dgram.js b/lib/dgram.js index ed9eec716b..9c27c8b4d8 100644 --- a/lib/dgram.js +++ b/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) { - var self = this; +Socket.prototype.setBroadcast = function(arg) { + if (arg) { + return binding.setBroadcast(this.fd, 1); + } else { + return binding.setBroadcast(this.fd, 0); + } +}; - var lastArg = arguments[arguments.length - 1]; - var callback = typeof lastArg === 'function' ? lastArg : null; +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"); + } +}; - 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; - } +// translate arguments from JS API into C++ API, after optional DNS lookup +Socket.prototype.send = function(buffer, offset, length) { + var self = this; - if (callback) callback(null, bytes); + if (typeof offset !== "number" || typeof length !== "number") { + throw new Error("send takes offset and length as args 2 and 3"); + } - } else { - dns.lookup(arguments[1], function (err, ip, addressType) { - // DNS error - if (err) { - if (callback) callback(err); - self.emit('error', err); - 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"); + } - 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"); } }; - diff --git a/lib/dns.js b/lib/dns.js index 52aa034272..afd5cb1fcc 100644 --- a/lib/dns.js +++ b/lib/dns.js @@ -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; diff --git a/src/node_net.cc b/src/node_net.cc index 1f2ae7ca44..41a50d474c 100644 --- a/src/node_net.cc +++ b/src/node_net.cc @@ -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 SetNoDelay(const Arguments& args) { return Undefined(); } -static Handle 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 SetKeepAlive(const Arguments& args) { int r; HandleScope scope; @@ -1018,6 +1010,53 @@ static Handle SetKeepAlive(const Arguments& args) { return Undefined(); } +static Handle 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 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 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); diff --git a/test/simple/test-dgram-pingpong.js b/test/simple/test-dgram-pingpong.js index 88e61cca7c..67aa83c359 100644 --- a/test/simple/test-dgram-pingpong.js +++ b/test/simple/test-dgram-pingpong.js @@ -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'); }); diff --git a/test/simple/test-dgram-udp4.js b/test/simple/test-dgram-udp4.js new file mode 100644 index 0000000000..65840ac3bb --- /dev/null +++ b/test/simple/test-dgram-udp4.js @@ -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); diff --git a/test/simple/test-dgram-unix-anon.js b/test/simple/test-dgram-unix-anon.js new file mode 100644 index 0000000000..1d93ffa102 --- /dev/null +++ b/test/simple/test-dgram-unix-anon.js @@ -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); diff --git a/test/simple/test-dgram-unix.js b/test/simple/test-dgram-unix.js new file mode 100644 index 0000000000..7249369a21 --- /dev/null +++ b/test/simple/test-dgram-unix.js @@ -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);