简体   繁体   中英

NodeJS TCP Client communication

First of all - I am aware of this answer to a kind of similar problem.

Problem

I have a third party protocol, that uses TCP\\IP. This protocol defines that the server replies to every message received. On the client side (which I try to implement) I have to wait for the answer from the server.

The problem occurs, when I try to send messages. I need to wait for the answer from the first message before I send the second one (like ping-pong).

I tried to do multiple writes on my NodeJS tcp-client like this, which understandably fails due to async:

client.connect(connectOptions, function () {
  client.write(message1);
  client.write(message2);
}); 

Like I said before, I have a third party component, which responses to both messages with a numeric value. So when

client.on('data',function (data) {});

fires an event, I can't distinguish which message, was responsible for the answer. Unlike the linked answer I don't have the ability, to tag the answer on the server side.

I am new to node.js, so I try to figure out the best way to solve this kind of problem, as it´s of the nature: do synchronous things in the async environment.

One way would be to use a common list of handlers to keep track of requests and responses:

var handlers = [];
client.connect(connectOptions, function () {
    client.write(message1);
    handlers.push(function msg1_handler(data) {});

    client.writewrite(message2);
    handlers.push(function msg2_handler(data) {});
});
client.on('data',function(data) {
    var handler = handlers.shift();
    handler(data);
});

All of this should obviously be wrapped in a separate class containing both handlers an client objects. It's just an example of how to do it. The drawback is that if the server fails to respond to some request then you have a complete mess, hard to make it right.


Another idea is to buffer requests:

function BufferedClient(cli) {
    this.cli = cli;
    this.buffer = [];
    this.waiting_for_response = false;
    var that = this;
    cli.on('data', function(data) {
        that.waiting_for_response = false;
        var pair = that.buffer.shift();
        var handler = pair[0];
        process.nextTick(function() {
            // we use .nextTick to avoid potential
            // exception in handler which would break
            // BufferedClient
            handler(data);
        });
        that.flush();
    });
};
BufferedClient.prototype = {
    request: function(msg, handler) {
        this.buffer.push([handler, msg]);
        this.flush();
    },
    flush: function() {
        var pair = this.buffer[0];
        if (pair && !this.waiting_for_response) {
            this.cli.write(pair[1]);
            this.waiting_for_response = true;
        }
    }
};

This time you send requests sequentially (so like synchronous) due to how .request() and .on('data') handler work together with .flush() function. Usage:

client.connect(connectOptions, function () {
    var buff_cli = new BufferedClient(client);
    buff_cli.request(message1, function(data) { });
    buff_cli.request(message2, function(data) { });
});

Now even if the server fails to respond you don't have a mess. However if you issue buff_cli.request parallely and one of them fails then you will have a memory leak (since this.buffer is getting bigger while nothing is draining it because the BufferedClient is waiting for a response). This can be fixed by adding some timeouts on the socket.

Note that both solutions assume that the server never pushes anything to the client without a request.

If I were you I would go with second solution. Note that I haven't tested the code so it might be buggy but the general idea should be ok.

Side note: When you implement a server (and I know that you don't in this case) you should always have a protocol that matches each request with a response in a unique way. One way would be to send a unique ID with each request so that the server would be respond with the same ID. In such scenario matching request with response is very easy and you avoid all that mess.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM