2012-10-13 41 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中的另一个长度字段。

这是一个人为的例子,但要知道,在低流量速率下,网络层通常会将数据以您提供的任何数据块的形式发送出去,所以这种情况只会在增加负载时才真正显现。一旦操作系统一次开始从多个写入开始构建数据包,它将开始分割,这对于网络而言并不适合您,并且您必须处理这个问题。