簡體   English   中英

Node.js TCP服務器傳入緩沖區

[英]Node.js TCP server incoming buffer

我有兩個相互通信的節點進程。 我將它們稱為[Node Server][Node Sender] [Node Sender]持續處理信息並通過TCP連接將消息寫入[Node Server] [Node Server]然后寫回狀態消息。

[Node Sender]的示例:

var message = "Test Message";
[Node Sender].connection.write(message);

[Node Server]示例:

[Node Server].socket.on("data", function(p_data) {
    this.write("OK");

    // Do some work with p_data
}

這沒有問題, p_data在5毫秒以上的任何時間發送時總是包含“測試消息” 但是,如果我加速[節點發送者]每毫秒寫一次, p_data偶爾會以“Test MessageTest MessageTes”之類的結尾。

據我所知, [Node Sender]中的緩沖區填充速度可能比write命令發送緩沖區要快。 有沒有辦法在發送消息時強制一對一的比例,同時仍保持異步?

我當然可以在我的消息中添加一個終結符,並在[Node Server]中填充一個緩沖區,但我想確保沒有明顯的東西我丟失了。

不,你沒有遺漏任何東西,是的,你需要在你的消息中添加某種形式的終止。

這里有兩個基本問題:

  1. TCP協議是面向流的,而不是面向消息的; 它沒有內在的知識可能構成“信息”。

  2. node.js網絡庫觸發的數據事件表明某些數據已到達但不知道消息可能包含的內容,它無法指示它已收到任何特定數據。

因此,通過比Node更快地發送消息,socket recv緩沖區會填充多個“消息”。

此問題的典型解決方案是添加行終止,如https://github.com/baudehlo/Haraka/blob/master/connection.js第32-34行所示:

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

和110-134行:

Connection.prototype.process_data = function (data) {
  if (this.disconnected) {
    logger.logwarn("data after disconnect from " + this.remote_ip);
    return;
  }

  this.current_data += data;
  this._process_data();
};

Connection.prototype._process_data = function() {
  var results;
  while (results = line_regexp.exec(this.current_data)) {
    var this_line = results[1];
    if (this.state === 'pause') {
        this.early_talker = 1;
        var self = this;
        // If you talk early, we're going to give you a delay
        setTimeout(function() { self._process_data() }, this.early_talker_delay);
        break;
    }
    this.current_data = this.current_data.slice(this_line.length);
    this.process_line(this_line);
  }
};

您需要累積傳入的緩沖區數據以獲取完整的消息。 請參考下面的例子。 此服務器需要具有4字節標頭和郵件正文的數據。 header是unsigned int,表示body的總長度,body是帶有分隔符'|'的字符串數據。 請注意,有可能一次沒有收到此“標題和消息”。 所以我們需要累積傳入的數據,直到我們得到一個完整的數據。 並且有可能一次接收多個“標題和消息”。 關鍵是我們需要數據積累。

var SERVER_PORT = 8124;
var TCP_DELIMITER = '|';
var packetHeaderLen = 4; // 32 bit integer --> 4

var server = net.createServer( function(c) {
    var accumulatingBuffer = new Buffer(0); 
    var totalPacketLen   = -1; 
    var accumulatingLen  =  0;
    var recvedThisTimeLen=  0;
    var remoteAddress = c.remoteAddress;
    var address= c.address();
    var remotePort= c.remotePort;
    var remoteIpPort = remoteAddress +":"+ remotePort;

    console.log('-------------------------------'+remoteAddress);
    console.log('remoteIpPort='+ remoteIpPort); 

    c.on('data', function(data) {
        console.log('received data length :' + data.length ); 
        console.log('data='+ data); 

        recvedThisTimeLen = data.length;
        console.log('recvedThisTimeLen='+ recvedThisTimeLen);

        //accumulate incoming data
        var tmpBuffer = new Buffer( accumulatingLen + recvedThisTimeLen );
        accumulatingBuffer.copy(tmpBuffer);
        data.copy ( tmpBuffer, accumulatingLen  ); // offset for accumulating
        accumulatingBuffer = tmpBuffer; 
        tmpBuffer = null;
        accumulatingLen += recvedThisTimeLen ;
        console.log('accumulatingBuffer = ' + accumulatingBuffer  ); 
        console.log('accumulatingLen    =' + accumulatingLen );

        if( recvedThisTimeLen < packetHeaderLen ) {
            console.log('need to get more data(less than header-length received) -> wait..');
            return;
        } else if( recvedThisTimeLen == packetHeaderLen ) {
            console.log('need to get more data(only header-info is available) -> wait..');
            return;
        } else {
            console.log('before-totalPacketLen=' + totalPacketLen ); 
            //a packet info is available..
            if( totalPacketLen < 0 ) {
                totalPacketLen = accumulatingBuffer.readUInt32BE(0) ; 
                console.log('totalPacketLen=' + totalPacketLen );
            }
        }    

        //while=> 
        //in case of the accumulatingBuffer has multiple 'header and message'.
        while( accumulatingLen >= totalPacketLen + packetHeaderLen ) {
            console.log( 'accumulatingBuffer= ' + accumulatingBuffer );

            var aPacketBufExceptHeader = new Buffer( totalPacketLen  ); // a whole packet is available...
            console.log( 'aPacketBufExceptHeader len= ' + aPacketBufExceptHeader.length );
            accumulatingBuffer.copy( aPacketBufExceptHeader, 0, packetHeaderLen, accumulatingBuffer.length); // 

            ////////////////////////////////////////////////////////////////////
            //process one packet data
            var stringData = aPacketBufExceptHeader.toString();
            var usage = stringData.substring(0,stringData.indexOf(TCP_DELIMITER));
            console.log("usage: " + usage);
            //call handler
            (serverFunctions [usage])(c, remoteIpPort, stringData.substring(1+stringData.indexOf(TCP_DELIMITER)));
            ////////////////////////////////////////////////////////////////////

            //rebuild buffer
            var newBufRebuild = new Buffer( accumulatingBuffer.length );
            newBufRebuild.fill();
            accumulatingBuffer.copy( newBufRebuild, 0, totalPacketLen + packetHeaderLen, accumulatingBuffer.length  );

            //init      
            accumulatingLen -= (totalPacketLen +4) ;
            accumulatingBuffer = newBufRebuild;
            newBufRebuild = null;
            totalPacketLen = -1;
            console.log( 'Init: accumulatingBuffer= ' + accumulatingBuffer );   
            console.log( '      accumulatingLen   = ' + accumulatingLen );  

            if( accumulatingLen <= packetHeaderLen ) {
                return;
            } else {
                totalPacketLen = accumulatingBuffer.readUInt32BE(0) ; 
                console.log('totalPacketLen=' + totalPacketLen );
            }    
        }  
    }); 

    ...
});

請參考下面的整個例子。

https://github.com/jeremyko/nodeChatServer

希望這有幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM