socket.end([data],[encoding]) This event sends the FIN packet to the connected end of the socket, basically closing half the connection. The server can still send data if allowHalfOpen is set. You can specify data to send and an encoding, but both of those parameters are optional. socket.pause( ) This does exactly as you might expect: it pauses data being sent on the socket. socket.resume( ) This resumes the data transmission on a socket.
socket.setNoDelay([noDelay]) This determines whether or not the TCP connection will buffer data before sending the data off. This is known as the “Nagle algorithm” and the noDelay boolean parameter defaults to true.
socket.setKeepAlive([enable], [initialDelay])
This will enable or disable the keepalive functionality of the socket. This means there will be a keepalive probe sent after the last data packet is received and the initialDelay time, which defaults to zero. The enable boolean parameter defaults to false.
socket.unref( ) Calling this on the socket will check the Node.js event system, and if the socket is the only remaining socket in this system, it will be allowed to exit.
socket.ref( ) This, once set on a socket, will prevent the event system in Node.js from letting the program exit if it’s the only socket remaining. This is the opposite of the default behavior, which would let the program exit if it is the only remaining socket. socket.remotePort This is the port to which the socket is connected.
socket.localAddress This is the address from which the socket originated. socket.localPort This is the port from which the socket originates.
socket.bytesRead This gathers the number of bytes read from the data transmission. socket.bytesWritten This indicates the amount of bytes written.
Chapter 2 ■ NetworkiNg with Node.js
Listing 2-35. Tallying Bytes var net = require('net'); var PORT = 8181,
totalRead = 0, totalWritten = 0, connectionCount = 0;
var server = net.Server(connectionListener); function connectionListener(conn) {
//tally the bytes on end conn.on('end', function() { totalRead += conn.bytesRead; }); } server.listen(PORT); //Connect a socket
var socket = net.createConnection(PORT); socket.on('connect', function() {
// plan on writing the data more than once connectionCount++; // My = 2 Bytes socket.write('My', function () { // Precious = 8 Bytes socket.end('Precious'); }); });
// tally the bytes written on end socket.on('end', function() {
totalWritten += socket.bytesWritten; });
socket.on('close', function() {
// Each time we should get +=10 bytes Read and Written console.log('total read: ' + totalRead);
console.log('total written: ' + totalWritten); // We're gonna do this a few times
if (connectionCount < 5) { socket.connect(PORT); } else { server.close(); } });
Chapter 2 ■ NetworkiNg with Node.js
You now have access to the amount of bytes that are being sent between your server and your connections. This is terrific, but now you want to be able to reveal the details of where this server resides and where the sockets are coming from. To do this, you use the socket properties, remoteAddress, and remotePort (Listing 2-36). You can add these into the example above by adding a line in the connectionListener callback function and another line in the socket.on('connect') event.
Listing 2-36. Adding Some Address and Port Sniffers
console.log(socket.remoteAddress + ":" + socket.remotePort);
How It Works
Getting information about connected servers is actually quite easy. These data points that tell you how many bytes have been sent or received can be quite valuable when creating a Node.js application. How does Node.js build these data and present them to the net module for your consumption? If you examine the net module source code you will find that the bytesRead value is always set to zero when a new socket handle is created, as you might expect. This value is then incremented by the length of the buffer that is read during the buffer handle’s onread function (shown in Listing 2-37).
Listing 2-37. onread Event—Increments bytesRead by Length function onread(buffer, offset, length) {
var handle = this; var self = handle.owner;
assert(handle === self._handle, 'handle != self._handle'); timers.active(self);
var end = offset + length;
debug('onread', process._errno, offset, length, end); if (buffer) {
debug('got data'); // read success.
// In theory (and in practice) calling readStop right now // will prevent this from being called again until _read() gets // called again.
// if we didn't get any bytes, that doesn't necessarily mean EOF. // wait for the next one.
if (offset === end) {
debug('not any data, keep waiting'); return;
}
// if it's not enough data, we'll just call handle.readStart() // again right away.
Chapter 2 ■ NetworkiNg with Node.js
49
// Optimization: emit the original buffer with end points var ret = true;
if (self.ondata) self.ondata(buffer, offset, end); else ret = self.push(buffer.slice(offset, end)); if (handle.reading && !ret) {
handle.reading = false; debug('readStop'); var r = handle.readStop(); if (r) self._destroy(errnoException(process._errno, 'read')); }
} else if (process._errno == 'EOF') { debug('EOF');
if (self._readableState.length === 0) self.readable = false;
if (self.onend) self.once('end', self.onend); // push a null to signal the end of data. self.push(null);
// internal end event so that we know that the actual socket // is no longer readable, and we can start the shutdown // procedure. No need to wait for all the data to be consumed. self.emit('_socketEnd'); } else { debug('error', process._errno); // Error self._destroy(errnoException(process._errno, 'read')); } }
Fetching the bytesWritten value is not quite as straightforward as incrementing a value by the length parameter that is passed into the onread function. In fact, as can be seen in Listing 2-38, the bytesWritten parameter is generated by reading the chunk length of a buffer, or the actual byte length itself.
Listing 2-38. bytesWritten
Socket.prototype.__defineGetter__('bytesWritten', function() { var bytes = this._bytesDispatched,
state = this._writableState, data = this._pendingData, encoding = this._pendingEncoding; state.buffer.forEach(function(el) { if (Buffer.isBuffer(el.chunk)) bytes += el.chunk.length;
Chapter 2 ■ NetworkiNg with Node.js
else
bytes += Buffer.byteLength(el.chunk, el.encoding); });
if (data) {
if (Buffer.isBuffer(data)) bytes += data.length; else
bytes += Buffer.byteLength(data, encoding); }
return bytes; });
The remoteAddress and remotePort parameters come from the socket handle itself. These represent an abstraction (Listing 2-39) on top of the Node.js handle’s getpeername object, which holds an address and a port parameter. This makes it simple for Node.js to define a getter for the remotePort and remoteAddress parameters. Listing 2-39. getpeername Method and the remoteAddress and remotePort Properties
Socket.prototype._getpeername = function() { if (!this._handle || !this._handle.getpeername) { return {}; } if (!this._peername) { this._peername = this._handle.getpeername(); // getpeername() returns null on error if (this._peername === null) { return {}; } } return this._peername; }; Socket.prototype.__defineGetter__('remoteAddress', function() { return this._getpeername().address; }); Socket.prototype.__defineGetter__('remotePort', function() { return this._getpeername().port; });
You have seen how well Node.js defines properties on the server and sockets that support networked applications, making them easy to retrieve and to work with. Gaining these details about connected services can provide much-needed information when you develop a Node.js application.