juandopazo
juandopazo

Reputation: 6329

Writing data to a socket in Node

I'm getting a weird result when writing to a socket. I wrote a simple experiment with a client and a server:

server.js

var net = require('net');
net.createServer(function (connection) {
  connection.on('data', function (data) {
    console.log('data: ' + data);
  });
}).listen(1337);

client.js

var net = require('net');
var client = net.connect({port: 1337}, function () {
  var i = 0;
  function send() {
   client.write('a');
    if (++i < 100) {
      process.nextTick(send);
    } else {
      client.end();
    }
  }
  send();
});

I expected the server to show 100 lines of data: a, but I ended up getting a smaller number of data: aaaaaaa lines. There's socket.setNoDelay() that seems to be what I want, but it doesn't seem to have any effect.

What am I missing?

Thanks a lot,

Upvotes: 2

Views: 8889

Answers (1)

The TCP protocol only sends exactly the bytes you write in the socket. They will not be separated into messages, that's up to you. If you would like to get 100 lines of a then you would have to define 100 separate messages, and choose a delimiter for them. Usually people delimit messages sent to a TCP socket by \r\n.

So you would need to change your server to

var net = require('net');
net.createServer(function (connection) {
  connection.on('data', function (buffer) {
    var data = buffer.toString();
    if (data.indexOf('\r\n') > -1) { // If there's more than one line in the buffer
        var lines = data.split('\r\n'); // Split the lines
        var i = lines.length;
        while (i--) { // This will read your lines in reverse, be careful
            console.log(lines[i]);   // Print each line
        }
    } else {
        console.log(data); // If only one line came through, print it
    }
  });
}).listen(1337);

And your client to

var net = require('net');
var client = net.connect({port: 1337}, function () {
  var i = 0;
  function send() {
   client.write('a\r\n'); // Notice the \r\n part. This is what will help you separate messages on the server
    if (++i < 100) {
      process.nextTick(send);
    } else {
      client.end();
    }
  }
  send();
});

And then I believe you would get 100 lines of a.

This module also provides a very interesting way to do it, and of course ZeroMQ would also shine in this because it already has a nice protocol that puts things in envelopes and sends them.

Also interestingly but out of the scope of your question, the messages you send write to the socket on one side will not arrive in the same order on the server. If you change your send function to

  function send() {
    if (++i < 100) {
      client.write('a'+i+'\r\n');
      process.nextTick(send);
    } else {
      client.end();
    }
  }

you can see them arriving not in the order you sent them.

By "The TCP protocol only sends exactly the bytes you write in the socket" I mean that if you do socket.write("1"); socket.write("2"), you will receive "12" on the server, because that's what you wrote on the socket. You have to explicitly separate your messages by something so that the server can know when a message starts and when a message ends.

About receiving things in order or not, you'll notice that if you remove the process.nexTick and have your client like:

var net = require('net');
var client = net.connect({port: 1337}, function () {
  var i = 100;
  while (i--) {
    client.write('a'+i+'\r\n');
  }
});

you'll get two messages on the server (at least I got): first numbers 83 - 99 and then 0 - 82, despite having wrote them in order.

Its because TCP splits it in packets in some magic way. The first package was actually larger than the second one, so it got there last. You can read more about how TCP works in the wikipedia page of course, and this video is probably going to say more than what you need to hear but its good to understand everything you're working with.

Upvotes: 6

Related Questions