Building applications

Creating a TCP Server

Here is a simple example of creating a TCP server socket in NodeJS, with relevant explanations in the code comments.

var net = require('net');

var HOST = '127.0.0.1';
var PORT = 6969;

// Create a TCP server instance, and call the listen function to start listening on the specified port
// The callback function passed to net.createServer() will be the event handler for the "connection" event
// In each "connection" event, the socket object received by this callback function is unique
var server = net.createServer();
server.listen(PORT, HOST);
console.log('Server listening on ' +
    server.address().address + ':' + server.address().port);

server.on('connection', function(sock) {
    console.log('CONNECTED: ' +
         sock.remoteAddress +':'+ sock.remotePort);
});

First, let's take a look at net.createServer, which returns an instance of Server, as follows.

1075 function Server(options, connectionListener) {
1076   if (!(this instanceof Server))
1077     return new Server(options, connectionListener);
1078 
1079   EventEmitter.call(this);
1080 
1081   var self = this;
1082 
1083   if (typeof options === 'function') {
1084     connectionListener = options;
1085     options = {};
1086     self.on('connection', connectionListener);
1087   } else {
1088     options = options || {};
1089 
1090     if (typeof connectionListener === 'function') {
1091       self.on('connection', connectionListener);
1092     }
1093   }
1094 
1095   this._connections = 0;
1096   // ...
1111 
1112   this._handle = null;
1113   this._usingSlaves = false;
1114   this._slaves = [];
1115   this._unref = false;
1116 
1117   this.allowHalfOpen = options.allowHalfOpen || false;
1118   this.pauseOnConnect = !!options.pauseOnConnect;
1119 }
1120 util.inherits(Server, EventEmitter);

Server inherits from EventEmitter. If a callback function is passed, L1086 and L1091 bind the passed function as a listener to the connection event, and then listen. Let's take a look at the callback processing when a connection arrives as a server.

1400 function onconnection(err, clientHandle) {
1401   var handle = this;
1402   var self = handle.owner;
1403 
1404   debug('onconnection');
1405 
1406   if (err) {
1407     self.emit('error', errnoException(err, 'accept'));
1408     return;
1409   }
1410 
1411   if (self.maxConnections && self._connections >= self.maxConnections) {
1412     clientHandle.close();
1413     return;
1414   }
1415 
1416   var socket = new Socket({
1417     handle: clientHandle,
1418     allowHalfOpen: self.allowHalfOpen,
1419     pauseOnCreate: self.pauseOnConnect
1420   });
1421   socket.readable = socket.writable = true;
1422 
1423 
1424   self._connections++;
1425   socket.server = self;
1426   socket._server = self;
1427   // ...
1431   self.emit('connection', socket);
1432 }

This function is called back by TCPWrap::OnConnection, tcp_wrap->MakeCallback(env->onconnection_string(), ARRAY_SIZE(argv), argv);, where the first parameter indicates the status and the second parameter is the connection handle.

L1416-L1421, create a JS-level Socket based on the passed handle, and send a connection event to the observer at L1431.

In the example of the TCP server above, the server listens for the connection event and customizes the user processing logic.

Creating a TCP Client

Now let's create a TCP client that connects to the server just created, sends a message to the server, and closes the connection after receiving feedback from the server. The following code describes this process.

var net = require('net');

var HOST = '127.0.0.1';
var PORT = 6969;

var client = new net.Socket();
client.connect(PORT, HOST, function() {

    console.log('CONNECTED TO: ' + HOST + ':' + PORT);
    // Send data to the server immediately after establishing a connection, and the server will receive this data
    client.write('I am Chuck Norris!');

});

// Add a "data" event processing function to the client
// Data is the data sent back by the server
client.on('data', function(data) {

    console.log('DATA: ' + data);
    // Completely close the connection
    client.destroy();

});

// Add a "close" event processing function to the client
client.on('close', function() {
    console.log('Connection closed');
});

After creating the Socket object, the client initiates a connection to the server. Before the actual connection, a DNS query is required (not required if an IP is provided), and lookupAndConnect is called, followed by function connect(self, address, port, addressType, localAddress, localPort) to initiate the connection.

We notice the five-tuple: <remoteAddress, remotePort, addressType, localAddress, localPort>, which uniquely identifies a network connection.

After establishing a full-duplex Socket, the user program can listen for the data event to obtain data.

Last updated