Node.js net library: getting complete data from 'data' event

coding_hero picture coding_hero · Jun 26, 2013 · Viewed 11.1k times · Source

I've searched around, and either can't find the exact question I'm trying to answer, or I need someone to explain it to me like I'm 5.

Basically, I have a Node.js script using the Net library. I'm connecting to multiple hosts, and sending commands, and listening for return data.

var net = require('net');

var nodes = [
    'HOST1,192.168.179.8',
    'HOST2,192.168.179.9',
    'HOST3,192.168.179.10',
    'HOST4,192.168.179.11'
];

function connectToServer(tid, ip) {
    var conn = net.createConnection(23, ip);
    conn.on('connect', function() {
        conn.write (login_string);  // login string hidden in pretend variable
    });
    conn.on('data', function(data) {
        var read = data.toString();
        if (read.match(/Login Successful/)) {
            console.log ("Connected to " + ip);
            conn.write(command_string);
        }

        else if (read.match(/Command OK/)) { // command_string returned successful,
                                                    // read until /\r\nEND\r\n/

                    // First part of data comes in here
            console.log("Got a response from  " + ip + ':' + read);
        }
        else {
                 //rest of data comes in here
             console.log("Atonomous message from " + ip + ':' + read);
        }
    });
    conn.on('end', function() {
        console.log("Lost conncection to " + ip + "!!");
    });
    conn.on('error', function(err) {
        console.log("Connection error: " + err + " for ip " + ip);
    });
}

nodes.forEach(function(node) {
    var nodeinfo = node.split(",");
    connectToServer(nodeinfo[0], nodeinfo[1]);
});

The data ends up being split into two chunks. Even if I store the data in a hash and append the first part to the remainder when I read the /\r\nEND\r\n/ delimiter, there's a chunk missing out of the middle. How do I properly buffer the data in order to make sure I get the complete message from the stream?

EDIT: Ok, this seems to be working better:

function connectToServer(tid, ip) {
        var conn = net.createConnection(23, ip);

        var completeData = '';

        conn.on('connect', function() {
                conn.write (login_string);
        });
        conn.on('data', function(data) {
                var read = data.toString();

                if (read.match(/Login Successful/)) {
                        console.log ("Connected to " + ip);

                        conn.write(command_string);
                }
                else {
                        completeData += read;
                }

                if (completeData.match(/Command OK/)) {
                        if (completeData.match(/\r\nEND\r\n/)) {
                                console.log("Response: " + completeData);
                        }
                }
        });
        conn.on('end', function() {
                console.log("Connection closed to " + ip );
        });
        conn.on('error', function(err) {
                console.log("Connection error: " + err + " for ip " + ip);
        });
}

My biggest problem was apparently a logic error. I was either waiting for the chunk that began the reply, or the chunk that ended it. I wasn't saving everything in-between.

I guess if I wanted to get all Node-ish about it, I should fire an event whenever a complete message came in (beginning with a blank line, ending with 'END' on a line by itself), and do the processing there.

Answer

ChrisCM picture ChrisCM · Jun 26, 2013

You shouldn't do anything with the data you recieve, until you receive the end event. The end callback means that all data chunks have been sent through the stream to your callbacks. If data comes in more than one chunk, you need to create a variable within your function closure to store this data to. Most programs can work just fine ignoring this fact, because data usually comes across in one chunk. But sometimes it doesn't. It doesn't even necessarily depend on the amount of data. If you're in a situation where this is happening, I created an example that demos how to handle it. I basically used your code, but removed all the fluff... this is just demoing the logic you need to collect all the data and do work on it.

function connectToServer(tid, ip) {
    var conn = net.createConnection(23, ip);
    var completeData = '';

    conn.on('connect', function() {
        conn.write (login_string);  // login string hidden in pretend variable
    });
    conn.on('data', function(data) {
        completeData += data;
        var dataArray = completeData.split('your delimiter');
        if(dataArray.size > 1) { //If our data was split into several pieces, we have a complete chunk saved in the 0th position in the array
            doWorkOnTheFirstHalfOfData(dataArray[0]);
            completeData = dataArray[1];// The second portion of data may yet be incomplete, thise may need to be more complete logic if you can get more than one delimeter at a time...
        }
    });
    conn.on('end', function() {
        //do stuff with the "completeData" variable in here.
    });
}