Node.js - 持久连接适配器

时间:2016-05-13 05:44:51

标签: node.js tcp-ip persistent-connection

我的要求有点不同,即使它是可以实现的也不知道。

我使用Node.js开发后端应用服务器。这个服务器基本上做两个工作:

(1)为客户提供服务:我的客户都是将发送HTTP(S)请求的手机,收到回复后会关闭会话。

(2)调用其他异步工作服务:另一方面,服务器将连接到仅通过TCP / IP连接而非HTTP工作的其他服务器。这里异步意味着,服务器将发送请求,不应等待响应。响应将通过相同的TCP / IP连接接收。

所以我想要实现的流程是:

  1. 手机将HTTP请求发送至服务器
  2. 服务器收到HTTP请求后,调用TCP / IP上的服务
  3. 服务器通过TCP / IP连接从TCP / IP服务接收响应
  4. 服务器通过响应响应电话。
  5. 为了表示上述流程,我附上了以下图片。

    enter image description here

    在上图中,TCP / IP服务器由其他提供商管理。

    我在node.js中编写了以下代码,它根据我们的要求有时完美地工作,但有时它会向HTTP请求发送错误的响应。我没有编写任何代码来处理这个问题。

        var net = require('net');
    
        var client = new net.Socket();
        client.connect(2202, 'example_ip', function () {
        console.log('Connected');
        // client.write('Hello, server! Love, Client.');
        });
    
        //Lets require/import the HTTP module
        var http = require('http');
    
        //Lets define a port we want to listen to
        const PORT = 8080;
    
        //We need a function which handles requests and send response
        function handleRequest(request, response) {
        var body = '';
    
        request.on('data', function (chunk) {
            body += chunk;
        });
    
        request.on('end', function () {
            console.log('Received request from JMeter------------>>>');
            // console.log(body);
            client.write(body);
    
        var count = 0;
        client.on('data', function (data) {
                console.log('<<<------------Received from SSM: ' + data);
                response.end(data);
                // client.destroy(); // kill client after server's response
            });
    
    
        });
    
    
    
        client.on('close', function () {
            console.log('Connection closed');
        });
    
    }
    
    //Create a server
    var server = http.createServer(handleRequest);
    
    //Lets start our server
    server.listen(PORT, function () {
        //Callback triggered when server is successfully listening. Hurray!
        console.log("Server listening on: http://localhost:%s", PORT);
    });
    

    请有人指导我解决这个问题。

2 个答案:

答案 0 :(得分:0)

您可以为每个传入请求实例化一个新客户端吗?这样,每个请求的TCP连接都是唯一的。

答案 1 :(得分:0)

TCP流不像WebSocket流那样工作(正如您所期望的那样)。您需要使用自己的协议与TCP服务器通信。请记住,HTTP客户端很多,并且您只有一个TCP连接来处理它们,所以使用如下所示的requestIds,代码解释了自己。

未经测试,但您可以理解。

shared.js

exports.tcp = {
    host: 'example_ip',
    port: 2202
};

exports.http = {
    host: 'localhost',
    port: 8080
};

/**
 *  TCP "guarantees" that a receiver will receive the reconstituted 
 *   stream of --> BYTES <-- as it was originally sent by the sender.
 *
 *  eg. if written message = 'How are you today?'
 *   the messages can come to us as follows:
 *     
 *     'How ar'
 *     'e you '
 *     'today?'
 *  
 *  so we need to use a simple protocol to handle messages
 */
exports.protocol = protocol;

function protocol(options) {

    if (!options) options = {};

    this.END_OF_MESSAGE = options.endOfMessage || '\0';
    this.END_OF_PART = options.endOfPart || '\1';

    this.dataBuffer = '';
}

protocol.prototype.packMessage = function(id, body) {
    return [id, body].join( this.END_OF_PART ) + this.END_OF_MESSAGE;
};

protocol.prototype.unpackMessage = function(message) {

    var parts = message.toString('utf8').split( this.END_OF_PART );
    return {id: parts.shift(), body: parts.shift()};
};

protocol.prototype.extractMessages = function(data, callback) {

    this.dataBuffer += data.toString('utf8');

    if (this.dataBuffer.indexOf(this.END_OF_MESSAGE) !== -1)
    {
        var messages = this.dataBuffer.split(this.END_OF_MESSAGE);
        var incomplete = this.dataBuffer.slice(-1) === this.END_OF_MESSAGE
            ? '' : messages.pop();

        messages.forEach(function(message)
        {
            if (message !== '') {
                callback( this.unpackMessage(message) );
            }
        });

        this.dataBuffer = incomplete;
        // rest of 'data'
    }

    /**
    if (Buffer.byteLength(this.dataBuffer, 'utf8') > 10240) { // 10KB
        console.log('[!] socket flooded');
        this.dataBuffer = '';
    }
    */
};

protocol.prototype.reset = function() {
    this.dataBuffer = '';
};

httpServer.js

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

var shared = require('./shared.js');
var protocol = new shared.protocol();

var server = http.createServer(handleRequest);
server.listen(shared.http.port, shared.http.host, function() {
    console.log('HTTP server listening: %s:%s', shared.http.host, shared.http.port);
});

function handleRequest(request, response) {

    var body = '';

    var requestId = nextId++;
    var eventName = 'message' + requestId;

    request.on('data', function(chunk) {
        body += chunk.toString('utf8');
    });

    request.on('end', function()
    {
        // ref#2
        client.write( protocol.packMessage(requestId, body) );

        // ref#3
        client.once(eventName, function(data) {

            clearTimeout(timeoutId);
            response.end(data);
        });
    });

    var timeoutId = setTimeout(function() {

        client.removeListener(eventName);
        response.end('timeout');

    }, 10000); // 10 sec.

    /** 
     * [!] Don't do this; you are adding just another 'data' event to
     *  the TCP client for EVERY http request !?
     *  
     *  request: UNIQUE obj. for every http request
     *  client: a PERSISTENT (TCP) stream obj.
     *
    client.on('data', function() { });
    **/
}

var client = new net.Socket();

// ref#1
client.connect(shared.tcp.port, shared.tcp.host, function() {
    console.log('TCP conn. established to: ', shared.tcp.host, shared.tcp.port);
});

var nextId = 0;
// unique per http req.

/**
 * [!] Do this ( once ) ( not for every request )
 */
client.on('data', function(data)
{
    protocol.extractMessages(data, function(message) {

        client.emit('message' + message.id, message.body);
        // ref#3
    });
});

client.on('close', function()
{
    console.log('TCP conn. closed');
    client.removeAllListeners();
})

client.on('error', function()
{
    console.log('TCP conn. error', arguments);
    // client.destroy(); // and reconnect here
});

tcpServer.js

var net = require('net');

var shared = require('./shared.js');
var protocol = new shared.protocol();

var server = net.createServer(handleConnection);
server.listen(shared.tcp, function() {
    console.log('TCP server listening %s:%s', shared.tcp.host, shared.tcp.port);
});

// [!] CONNECTION handler ( ref#1 )
function handleConnection(client)
{
    var this.dataBuffer = '';

    // [!] DATA handler ( ref#2 )
    client.on('data', function(data) {

        protocol.extractMessages(data, function(message)
        {
            var requestId = message.id;
            var body = message.body;

            // Do whatever you want with 'body' here

            /**
             * And return back to 'client' with 'requestId' using same protocol again
             *  so the 'client' ( from httpServer.js ) can handle your response
             */
            client.write( protocol.packMessage(requestId, body) );
        });
    });
}