JSON 编码的缓冲区数据未正确编码

Posted

技术标签:

【中文标题】JSON 编码的缓冲区数据未正确编码【英文标题】:JSON encoded buffer data not encoding properly 【发布时间】:2017-09-19 23:04:38 【问题描述】:

我目前正在尝试json_encode 一些缓冲区数据并在node.js websocket 服务器上对其进行解码,但未成功。

我正在使用一个 docker 容器,它提供我格式化为 html 的 ANSI 数据。

我使用Symfony Process component 从 docker 容器获取实时进程输出,并使用socket_write 将其输出到我的Node.js websocket 服务器。

我使用socket_write 将数据从docker 写入Node.js 服务器的代码如下所示:

$process->run(function ($type, $buffer) use ($socket, $converter) 
    $html = $converter->convert($buffer);

    // Pass the room we want to send the HTML socket messages to.
    $data = [
        'html' => $html,
        'room' => 'http://arbiter.dev/stephan-v/arbiter-test'
    ];

    $json = json_encode($data);

    socket_write($socket, $json, strlen($json));
);

在我的服务器上,我尝试像这样解码json_encoded 数据:

socket.on('data', (msg) => 
    const data = JSON.parse(msg);
    io.sockets.in(data.room).emit('log', data.html.toString().trim());
);

这会因为语法错误而失败:

SyntaxError: Unexpected token in JSON at position 66

当我在Node.js 服务器上console.log 传入msg 时,我得到这个输出:

Websocket server online, listening on port :5600
<Buffer 7b 22 68 74 6d 6c 22 3a 22 3c 73 70 61 6e 20 73 74 79 6c 65 3d 5c 22 62 61 63 6b 67 72 6f 75 6e 64 2d 63 6f 6c 6f 72 3a 20 23 30 37 33 36 34 32 3b 20 ... >
<Buffer 7b 22 68 74 6d 6c 22 3a 22 3c 73 70 61 6e 20 73 74 79 6c 65 3d 5c 22 62 61 63 6b 67 72 6f 75 6e 64 2d 63 6f 6c 6f 72 3a 20 23 30 37 33 36 34 32 3b 20 ... >
<Buffer 7b 22 68 74 6d 6c 22 3a 22 22 2c 22 72 6f 6f 6d 22 3a 22 68 74 74 70 3a 5c 2f 5c 2f 61 72 62 69 74 65 72 2e 64 65 76 5c 2f 73 74 65 70 68 61 6e 2d 76 ... >
<Buffer 7b 22 68 74 6d 6c 22 3a 22 22 2c 22 72 6f 6f 6d 22 3a 22 68 74 74 70 3a 5c 2f 5c 2f 61 72 62 69 74 65 72 2e 64 65 76 5c 2f 73 74 65 70 68 61 6e 2d 76 ... >
undefined:1
"html":"","room":"http:\/\/arbiter.dev\/stephan-v\/arbiter-test""html":"","room":"http:\/\/arbiter.dev\/stephan-v\/arbiter-test"
                                                                  ^

SyntaxError: Unexpected token  in JSON at position 66
    at JSON.parse (<anonymous>)

所以看起来msg 是一个缓冲流,但JSON.parse 似乎仍然可以处理前几条消息。

然后对于我发送到我的套接字的第三条消息它失败了。这里发生了什么?

我的缓冲区是否根本不知道我的消息应该被分块成适当的片段,因为我看到 2 个 JSON 对象在我的最后一个 console.log 语句中粘在一起?

【问题讨论】:

通过网络,您永远不能依赖数据是如何分块的。这就是为什么大多数协议都有一个长度指示器,所以它知道完成这条消息需要多少字节,然后准备好下一个。 这是一个websocket 的问题吗?您使用的是原始 TCP/IP 还是 Websocket 协议?两种不同的东西。 我的socket_write 是一个字符串长度作为第三个参数,如下所示:strlen($json) 这是哪里出错了?它不能计算出正确的长度还是我超出了限制或其他什么? 【参考方案1】:

这似乎完全解决了我的问题:

Wait for socket_write to complete before writing again

问题是我不明白为什么。作为socket_write 的第三个参数,我传递我的 JSON 对象的长度,如下所示:

socket_write($socket, $json, strlen($json));

我知道我需要设置一个长度,因为它只是一个流,可以说它接受缓冲区中的“块”数据。

但是strlen($json) 不应该已经解决了我所有的问题吗?

为什么我的socket_write 消息有时仍将多个 JSON 对象附加在一起?

编辑

从这些套接字与 TCP 一起工作以来我一直在阅读的内容来看,数据分块似乎是因为 Nagle's Algorithm 而发生的,并且根据消息大小和最后一条消息被确认的时间进行分块。

php 在 PHP 7.1 中引入了TCP_NODELAY,但该算法的存在是有原因的,因此您需要对此保持警惕。

我想解决此问题的最佳方法是通过拆分您在 PHP 中添加的分隔符,自己在服务器端拆分消息。

如果我在这方面有任何错误,请纠正我,我很想听听对此的想法。

【讨论】:

以上是关于JSON 编码的缓冲区数据未正确编码的主要内容,如果未能解决你的问题,请参考以下文章

如何动态分配正确的消息来解码协议缓冲区消息?

Google Protocol Buffers - 对编码解码 base64 char * c 字符串协议缓冲区数据感到困惑

将协议缓冲区编码的消息从 Python 服务器发送到 Java 客户端

nodejs-09-Buffer

nodejs-09-Buffer

mql4:在线编码空缓冲区