11

我已经实现了一个使用 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 套接字的传入请求?

谢谢!

4

5 回答 5

28

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

欢迎评论...

于 2012-10-16T16:11:40.703 回答
6

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

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

于 2012-10-14T10:00:33.573 回答
3

请注意,TCP 不保证它在哪里划分您收到的数据块。它所保证的是,您发送的所有字节都将按顺序接收,除非连接完全失败。

data我相信只要套接字说它有数据给你,节点事件就会出现。从技术上讲,您可以为 JSON 数据中的每个字节获取单独data的事件,并且它仍然在操作系统允许执行的操作范围内。没有人这样做,但是您的代码需要编写得好像它可以随时突然发生以保持健壮。您可以组合数据事件,然后沿着对您有意义的边界重新拆分数据流。

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

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

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

于 2012-10-14T10:19:27.153 回答
2

在此响应之后:

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
    }      
});
于 2019-09-17T09:10:18.990 回答
-4

尝试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.
});

希望能帮到你。

于 2012-10-13T11:55:52.450 回答