从Node.js中的TCP套接字读取字符串时出现的问题

时间:2012-10-13 11:15:07

标签: json node.js sockets

我实现了一个使用TCP套接字进行通信的客户端/服务器。我正在写入套接字的数据是字符串化的JSON。最初一切都按预期工作,但是,随着我提高写入速率,我最终遇到JSON解析错误,其中客户端的开头在旧的结尾处接收到新写入的开始。

这是服务器代码:

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套接字分隔传入请求的最佳做法或更有效的方法?

谢谢!

5 个答案:

答案 0 :(得分:26)

感谢大家的解释,他们帮助我更好地理解通过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
    }      
});

欢迎评论......

答案 1 :(得分:6)

使用分隔符,你在正确的轨道上。但是,您不能只在分隔符之前提取内容,处理它,然后丢弃它后面的内容。你必须缓冲分隔符后得到的任何内容,然后连接它旁边的内容。这意味着在给定的data事件之后,您最终可能会得到任何数字(包括0)的JSON“块”。

基本上你保留一个缓冲区,你初始化为""。在每个data事件上,您将收到的任何内容连接到缓冲区的末尾,然后split 分隔符上的缓冲区。结果将是一个或多个条目,但最后一个可能不完整,因此您需要测试缓冲区以确保它以分隔符结束。如果没有,则弹出最后一个结果并将缓冲区设置为它。然后处理剩下的任何结果(可能不是任何结果)。

答案 2 :(得分:2)

请注意,TCP不会保证它所分割的数据块的位置。它保证所有发送的字节都将按顺序接收,除非连接完全失败。

我相信Node data事件会在套接字说它有数据的时候进来。从技术上讲,您可以为JSON数据中的每个字节获取单独的data事件,并且它仍然在操作系统允许的范围内。没有人这样做,但你的代码需要被编写,好像它可能在任何时候突然开始变得健壮。由您来组合数据事件,然后沿着对您有意义的边界重新分割数据流。

为此,您需要缓冲任何非“完整”的数据,包括附加到“完整”数据块末尾的数据。如果您正在使用分隔符,请不要在分隔符后丢弃任何数据 - 始终将其作为前缀保留,直到您看到更多数据并最终看到另一个分隔符或结束事件。

另一个常见的选择是使用长度字段为所有数据添加前缀。假设您使用固定的64位二进制值。然后你总是等待8个字节,加上那些字节中的值表示更多,以便到达。假设您有一大块十个字节的数据传入。您可能在一个事件中获得2个字节,然后是5个,然后是4个 - 此时您可以解析长度并知道您需要7个以上,因为第三个块的最后3个字节是有效负载。如果下一个事件实际上包含25个字节,那么你将从前面的前一个7和前面的3中解析它,然后查找另一个字节为8-16的长度字段。

这是一个人为的例子,但请注意,在低流量率下,网络层通常会以您提供的任何块发送数据,因此这种情况只会在您增加负载时才会真正开始出现。一旦操作系统立即开始从多个写入开始构建数据包,它将开始分割一个方便网络而不是你的粒度,你必须处理它。

答案 3 :(得分:1)

以下回复:

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
    }      
});

我遇到了定界符问题,因为;是我发送的数据的一部分。

可以使用此更新来实现自定义定界符:

var chunk = "";
const DELIMITER = (';;;');
client.on('data', function(data) {
    chunk += data.toString(); // Add string on the end of the variable 'chunk'
    d_index = chunk.indexOf(DELIMITER); // 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+DELIMITER.length); // Cuts off the processed chunk
        d_index = chunk.indexOf(DELIMITER); // Find the new delimiter
    }      
});

答案 4 :(得分:-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.
});
希望能帮到你。