2012-10-13 64 views
6

我已經實現了使用TCP套接字進行通信的客戶機/服務器。我寫入套接字的數據是字符串化的JSON。然而,最初一切都按預期工作,隨着我提高寫入速度,我最終遇到了JSON解析錯誤,其中客戶端的開始部分在舊部分的末尾處接收到新寫入的開始。從Node.js中的TCP套接字讀取字符串時出現問題

這裏是服務器代碼:

var data = {}; 
data.type = 'req'; 
data.id = 1; 
data.size = 2; 
var string = JSON.stringify(data); 
client.write(string, callback()); 

這裏是我如何在客戶端服務器上接收到該代碼:

client.on('data', function(req) { 
    var data = req.toString(); 
    try { 
     json = JSON.parse(data); 
    } catch (err) { 
     console.log("JSON parse error:" + err); 
    } 
}); 

是我收到的加息的錯誤是:

SyntaxError: Unexpected token { 

這似乎是下一個請求被標記到當前結尾的開始之一。

我試過使用;爲每個JSON請求結束分隔符,然後使用:

var data = req.toString().substring(0,req.toString().indexOf(';')); 

然而這種方法,而不是造成JSON解析錯誤似乎導致完全丟失客戶端上的一些要求,因爲我提高速度寫超過300每秒。

是否有任何的最佳做法或更有效的方式來界定通過TCP套接字傳入的請求?

謝謝!

+0

有兩個相關的問題[1](http://stackoverflow.com/questions/ 9962197/node-js-readline-not-wait-for-a-full-line-on-socket-connections),[2](http://stackoverflow.com/questions/7034537/nodejs-what-is-the -proper路到處理-TCP-插座流-哪個定界符)。在他們中,解決方案都是使用分隔符,並存儲了以前消息的剩餘部分。不幸的是,更好的解決方案似乎還不存在。 – mayconbordin

回答

23

感謝大家的解釋,他們幫助我更好地理解通過TCP套接字發送和接收數據的方式。下面是我最終使用的代碼的簡要概述:

var chunk = ""; 
client.on('data', function(data) { 
    chunk += data.toString(); // Add string on the end of the variable 'chunk' 
    d_index = chunk.indexOf(';'); // Find the delimiter 

    // While loop to keep going until no delimiter can be found 
    while (d_index > -1) {   
     try { 
      string = chunk.substring(0,d_index); // Create string up until the delimiter 
      json = JSON.parse(string); // Parse the current string 
      process(json); // Function that does something with the current chunk of valid json.   
     } 
     chunk = chunk.substring(d_index+1); // Cuts off the processed chunk 
     d_index = chunk.indexOf(';'); // Find the new delimiter 
    }  
}); 

歡迎評論...

+0

+1太棒了!這解決了我的長期懸而未決的問題。謝謝。 – ajay

+0

很好的答案,謝謝! –

+1

您應該在嘗試後添加一條catch語句... –

-3

嘗試用end事件,並沒有數據

var data = ''; 

client.on('data', function (chunk) { 
    data += chunk.toString(); 
}); 

client.on('end', function() { 
    data = JSON.parse(data); // use try catch, because if a man send you other for fun, you're server can crash. 
}); 

希望幫助你。

+0

這對插座通信中的nodejs不起作用 –

5

您在使用分隔符的正確軌道上。但是,您不能在分隔符之前提取這些內容,對其進行處理,然後丟棄它之後的內容。你必須緩衝你在分隔符之後得到的任何東西,然後連接它旁邊的內容。這意味着在給定的data事件之後,最終可能會包含任何數字(包括0個)JSON「塊」。

基本上你保持一個緩衝區,你初始化""。在每個data事件您連接您收到的緩衝區的末尾任何然後split在限定的緩衝區。結果將是一個或多個條目,但最後一個條目可能不完整,因此您需要測試緩衝區以確保它以分隔符結束。如果沒有,你彈出最後的結果並設置你的緩衝區。然後處理任何結果(可能不是)。

2

要知道,TCP不會使有關地方將數據分割的您收到的塊任何保證。它所保證的是,所有發送的字節將按順序接收,除非連接完全失敗。

我相信節點data事件進來的時候說插座有數據爲您服務。從技術上講,您可以爲JSON數據中的每個字節分別獲得data事件,並且它仍然在OS允許的範圍內。沒有人會這樣做,但是您的代碼需要被編寫成好像它可能會在任何時候突然開始發生以保持健壯。您需要合併數據事件,然後將數據流重新分割爲對您有意義的邊界。要做到這一點,您需要緩衝任何不完整的數據,包括附加在「完整」數據塊尾部的數據。如果您使用分隔符,請勿在分隔符之後丟棄任何數據 - 始終將其作爲前綴保留,直到看到更多數據並最終顯示另一個分隔符或結束事件。

另一個常見的選擇是用長度字段爲所有數據加前綴。假設您使用固定的64位二進制值。然後,您總是等待8個字節,再加上這些字節中的值,表示到達。假設你有一大塊十字節的數據傳入。在一個事件中,你可能會得到2個字節,然後是5,然後是4 - 在這一點上,你可以解析長度,並知道你需要7個字節,因爲第三個塊的最後3個字節是有效載荷。如果下一個事件實際上包含25個字節,那麼您會從前面的3箇中取出前7個,並解析它,然後查找字節8-16中的另一個長度字段。

這是一個人爲的例子,但要知道,在低流量速率下,網絡層通常會將數據以您提供的任何數據塊的形式發送出去,所以這種情況只會在增加負載時才真正顯現。一旦操作系統一次開始從多個寫入開始構建數據包,它將開始分割,這對於網絡而言並不適合您,並且您必須處理這個問題。