2

I'm trying to set up a node.js application that could receive connections and still be listening to port 9001 once a socket is ended. How can I do that? Here is my current code (it doesn't close after the socket.end(), but it won't accept any other connections) :

var net = require('net');
var mySocket;

var server = net.createServer(function(socket) {
    mySocket = socket;
    mySocket.on("connect", onConnect);
    mySocket.on("data", onData);
});

function onConnect() {
    console.log("Connected");
}

function onData(command) {
    if (command == "exit") {
        console.log("Exiting");
        mySocket.end();
    }
}

console.log("Waiting for incoming connections");
server.listen(9001);

I tried to add another server.listen(9001); after the socket.end();, but I get a : Error: listen EADDRINUSE message. Also, will that code be able to receive several connections coming from different addresses at the same time, and handle them separately?

This is the full code. When executed, node.js receives 4 commands from the Flash application, and works properly (except that the onConnect() function seems never to be called), and the "exit;" command closes the socket properly, yet if I reload the Flash application, it doesn't connect to the server

var net = require('net');
const PACKET_SEPARATOR = 59 // ;
var connection_ack = false;
var counter = 0;

var server = net.createServer(function(socket) {
    function onConnect() {
        console.log("Connected to Flash");
    }

    function dataHandler(command) {
        if (command[command.length - 1] != String.fromCharCode(PACKET_SEPARATOR) && connection_ack) {
       console.log("SEP : " + PACKET_SEPARATOR + " - last : " + command[command.length - 1] + " - ack " + connection_ack);
            console.log("CAUGHT EXCEPTION : WRONG PACKET FORMAT --- " + command + " --- " + command.length);
        }
        if (command == "exit;") {
            console.log("Received exit request from " + socket.address().address + ":" + socket.address().port + " (" + socket.address().family + "). Ending connection...");
            socket.end();
        }
        else if (command == "<policy-file-request/>\0") {
            socket.write('<cross-domain-policy>\n<allow-access-from domain="*" to-ports="*" />\n</cross-domain-policy>\0', 'utf8');
            console.log("Policy file sent to " + socket.address().address + ":" + socket.address().port);
            player1.pxacceleration = 0;
            player1.pyacceleration = 0;
            connection_ack = true;
        }
        else {
            console.log("Got data from " + socket.address().address + ":" + socket.address().port + " (" + socket.address().family + ")");
            console.log("--> " + command);
            counter++;
            socket.write("Received " + counter + " commands;", 'utf8');
            console.log("Sending : Received " + counter + " commands;");
        }
    }

    function onData(d) {
        var command = "";
        for (i=0; i <= d.length - 1; i++) {
            command += String.fromCharCode(d[i]);
            if (d[i] == PACKET_SEPARATOR || i == d.length - 1 && !connection_ack) {
                dataHandler(command);
                command = "";
            }
        }
    }

    socket.on("connect", onConnect);
    socket.on("data", onData);
});

console.log("Ready. Waiting for incoming connections");
server.listen(9001);
server.listen(80); //TODO : Remove?     
pie3636
  • 795
  • 17
  • 31
  • 1
    Unless you close `server` what you want should already be happening. – user207421 Nov 16 '14 at 23:47
  • 1
    The `server` should already out-live the individual `socket`s. For multiple clients, the `server` will create multiple `socket`s that can be open and active at the same time. The global `mySocket`, on the other hand, will not help the `server` properly support multiple clients as it can only store 1 of them. – Jonathan Lonowski Nov 16 '14 at 23:48
  • 1
    Very bad to use `mySocket` as a global. As soon as you have more than one socket connecting at once, your `mySocket` variable is overwritten. Make onData be a local function and you can directly reference `socket` without using a global. – jfriend00 Nov 17 '14 at 00:51
  • I'm not sure what you mean. Could you provide me a small example? – pie3636 Nov 17 '14 at 01:04
  • 2
    [TCP is a stream protocol, not a message protocol. A `data` event does **not** necessarily equate to a single write.](http://stackoverflow.com/a/14540576/201952) – josh3736 Nov 17 '14 at 21:39
  • I know, that's why I added the instructions in `onData` to split the packets into several messages. This is only temporary since my program will mostly use streams to communicate. – pie3636 Nov 17 '14 at 21:42
  • You're not buffering correctly though. `command` is a local in the data handler, meaning it is empty every time the `data` event fires. Consider what happens when a message (ie `ABCDEF`) is split: packet 1 will have `ABC`, packet 2 will have `DEF`. You've lost the ABC part of the message. – josh3736 Nov 17 '14 at 21:48
  • Also, don't manually convert buffers to strings. [Set the socket's encoding](http://nodejs.org/api/stream.html#stream_readable_setencoding_encoding) to `utf8` (or whatever encoding you're actually expecting). – josh3736 Nov 17 '14 at 21:53

1 Answers1

1

As jfriend00 said, using mySocket as a global is not recommended. Try the below instead.

var server = net.createServer(function(socket) {
    function onData(command) {
        if (command == "exit") {
            console.log("Exiting");
            socket.end();
        }
    }
    socket.on("connect", onConnect);
    socket.on("data", onData);
});
...

This eliminates the need for the global in the first place. This should also allow multiple sockets and prevent the original error. I think. I'm new here, so I guess we will see.

EDIT Alright. I've been interacting with your code via telnet. I've also read up on some of the documentation. First, the socket.on("connect", onConnect); listener should be moved(along with the onConnect function) into the global scope and changed to server.on("connection", onConnect);. The reason for this is that the socket event listener connect is a client side listener. We are working server side. The server side listener for new connections is connection and the server should be listening for it in the same way it is listening for connections on a particular port.

This part of your code should look like this now:

//more code up above here
....
    function onData(d) {
        var command = "";
        for (i=0; i <= d.length - 1; i++) {
            command += String.fromCharCode(d[i]);
            if (d[i] == PACKET_SEPARATOR || i == d.length - 1 && !connection_ack) {    
                dataHandler(command);
                command = "";
            }
        }
    }
    socket.on("data", onData);
});

function onConnect() {
    console.log("Connected to Flash");
}

server.on("connection", onConnect);
....
//more code below here

However, the code would not recognize exit as a command via telnet. I was unable to figure this bit out. Since your question did not cover this problem it might just be me or you have it figured out.

EDIT 2 The code below keeps it local.

var server = net.createServer(function(socket) {

    function onConnect() {
        console.log("Connected to Flash");
        socket.write("we're connected");
    }
....
    function onData(d) {
        var command = "";
        for (i=0; i <= d.length - 1; i++) {
            command += String.fromCharCode(d[i]);
            if (d[i] == PACKET_SEPARATOR || i == d.length - 1 && !connection_ack) {    
                dataHandler(command);
                command = "";
            }
        }
    }
    onConnect();
    socket.on("data", onData);
});
enolam
  • 221
  • 1
  • 6
  • Thanks. I implemented your solution, but it's still not working. I updated my question with the full code – pie3636 Nov 17 '14 at 16:46
  • Thanks! Though, I can't define `onConnect()` in the global scope since the socket is only defined inside the `net.createServer(function(socket){...})`. How could I solve that? Also, the "exit;" command is only a string that I send using Flash. It could have been "quit;" or anything else, it's just the syntax that my Actionscript program uses for now – pie3636 Nov 17 '14 at 21:30
  • 1
    You could just leave the `onConnect` function in the local scope and then just use `onConnect();` without the listener since it only would get called every time there is a new socket connection to the server anyway. I will edit accordingly so it's clear. – enolam Nov 17 '14 at 21:39
  • It's almost working now, and I get the "Connected to Flash" message each time. The only issue left is that the `onData()` listener is not recreated after the first socket is ended (the server does not react to any data sent from Flash except while the first socket is active). – pie3636 Nov 17 '14 at 21:50
  • 1
    I've been able to close and reopen multiple sockets via telnet. I cannot replicate your error. Try using `socket.destroy();` instead of `socket.end();` and see where that gets you. This completely destroys the socket and is usually only used for troubleshooting. – enolam Nov 17 '14 at 22:05
  • It doesn't work, but this time the problem probably comes from the client side. I'll try to fix that on my own, thanks for the help. – pie3636 Nov 17 '14 at 22:09