Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

Commit

Permalink
Datagram socket refactor. Add tests and documentation.
Browse files Browse the repository at this point in the history
Support setTTL() and setBroadcast() socket options.
  • Loading branch information
mranney authored and ry committed Jul 15, 2010
1 parent e7c4f8c commit 4e50197
Show file tree
Hide file tree
Showing 8 changed files with 547 additions and 252 deletions.
264 changes: 172 additions & 92 deletions doc/api.markdown
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.

8 comments on commit 4e50197

@akaspin
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Build fails under Debian Squeeze x64, 2-4 cores:

[56/69] cxx: src/node_io_watcher.cc -> build/default/src/node_io_watcher_4.o
/usr/bin/g++ -DHAVE_OPENSSL=1 -DEV_MULTIPLICITY=0 -pthread -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -DHAVE_FDATASYNC=1 -DPLATFORM=linux -DNDEBUG -g -O3 -Idefault/src -I../src -Idefault/deps/libeio -I../deps/libeio -Idefault/deps/http_parser -I../deps/http_parser -Idefault/deps/v8/include -I../deps/v8/include -Idefault/deps/libev -I../deps/libev -Idefault/deps/c-ares -I../deps/c-ares -Idefault/deps/c-ares/linux-x86_64 -I../deps/c-ares/linux-x86_64 -Ideps/v8/include ../src/node_io_watcher.cc -c -o default/src/node_io_watcher_4.o
../src/node_net.cc: In function ‘v8::Handle<v8::Value> node::GetSockName(const v8::Arguments&)’:
../src/node_net.cc:401: error: ‘struct sockaddr_un’ has no member named ‘sun_len’
../src/node_net.cc:401: error: ‘struct sockaddr_un’ has no member named ‘sun_len’
../src/node_net.cc: In function ‘v8::Handle<v8::Value> node::GetPeerName(const v8::Arguments&)’:
../src/node_net.cc:423: error: ‘struct sockaddr_un’ has no member named ‘sun_len’
../src/node_net.cc:423: error: ‘struct sockaddr_un’ has no member named ‘sun_len’
../src/node_net.cc: In function ‘v8::Handle<v8::Value> node::Accept(const v8::Arguments&)’:
../src/node_net.cc:478: error: ‘struct sockaddr_un’ has no member named ‘sun_len’
../src/node_net.cc:478: error: ‘struct sockaddr_un’ has no member named ‘sun_len’
../src/node_net.cc: In function ‘v8::Handle<v8::Value> node::RecvFrom(const v8::Arguments&)’:
../src/node_net.cc:595: error: ‘struct sockaddr_un’ has no member named ‘sun_len’
../src/node_net.cc:595: error: ‘struct sockaddr_un’ has no member named ‘sun_len’
Waf: Leaving directory `/home/locadmin/src/node/build'
Build failed:  -> task failed (err #1):
        {task: cxx node_net.cc -> node_net_4.o}
make: *** [all] Error 1

@mranney
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry about that. I'll take a look.

@kapouer
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found this interesting post about sockaddr_un structure :
http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=187391#114

@mranney
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sadly, sun_path is not null terminated on OSX, but it is on Linux. I've almost got a fix for this.

@kapouer
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feel free to make us test it :)

@mranney
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just pushed a fix to my repo that compiles and makes tests pass on OSX and Linux:

http://github.com/mranney/node/commit/47b4718105ac36668c194036c721ba3e2f0061b6

Hopefully will be picked up soon. Sorry for the hassle.

@kapouer
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, works well for me (debian/sid)

@akaspin
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. This works. But when fix be implemented to upstream?

Please sign in to comment.