如何处理异常组合的Websocket消息?

时间:2018-08-06 09:10:23

标签: javascript json node.js websocket stream

我正在使用node ws library(在Ubuntu 16.04上为节点10.8.0)连接到外部websocket api。我有一个侦听器,它仅解析json并将其传递给回调:

this.ws.on('message', (rawdata) => {
    let data = null;
    try {
        data = JSON.parse(rawdata);
    } catch (e) {
        console.log('Failed parsing the following string as json: ' + rawdata);
        return;
    }
    mycallback(data);
});

我现在收到rawData如下的错误(我格式化并删除了不相关的内容):

�~A
{
    "id": 1,
    etc..
}�~�
{
    "id": 2,
    etc..

然后我想知道;这些字符是什么?看到结构,我最初认为第一个怪异符号必须是数组([)的开括号,而第二个是逗号(,),以便创建对象数组。

然后我通过在文件遇到JSON解析错误时将rawdata写入文件来进一步研究该问题。在一个小时左右的时间内,它已保存了大约1500个这些错误文件,这意味着很多。我在终端中cat $string = "It is test string for performance"; // VERSION 1 $time_start = microtime(true); $explode = explode(' ', $string); foreach ($explode as $s) { echo $s . "<br />"; } $time_end = microtime(true); $execution_time = ($time_end - $time_start)/60; echo '<b>Total Execution Time:</b> '.$execution_time.' Mins <br />'; // VERSION 2 $time_start2 = microtime(true); foreach (explode(' ', $string) as $s) { echo $s . "<br />"; } $time_end2 = microtime(true); $execution_time2 = ($time_end2 - $time_start2)/60; echo '<b>Total Execution Time 2:</b> '.$execution_time2.' Mins <br />'; 存放了其中的两个文件,并在下面上传了一个示例:

enter image description here

一些有趣的地方:

  1. 文件始终以这些怪异符号之一开头。
  2. 这些文件似乎存在于多个应单独接收的消息中。奇怪的标志将那些单独的消息分开。
  3. 文件始终以未完成的json对象结尾。
  4. 文件长度不同。它们的大小并不总是相同的,因此没有切成特定的长度。

我对websocket的经验不是很丰富,但是可能是我的websocket以某种方式接收了将它们连接在一起的消息流,这些消息流带有这些奇怪的符号作为分隔符,然后随机切断了最后一条消息吗?也许是因为我不断收到非常快速的消息?

还是由于服务器端错误(或功能)而将那些单独的消息组合在一起?

有人知道这里发生了什么吗?欢迎所有提示!

[编辑]

@bendataclear建议将其解释为utf8。我照做了,然后粘贴了以下结果的屏幕截图。第一个打印照原样,第二个打印解释为utf8。对我来说,这看起来并不像什么。我当然可以转换为utf8,然后按这些字符分开。尽管最后一条消息总是被截断,但这至少会使某些消息可读。但是仍然有其他想法。

enter image description here

4 个答案:

答案 0 :(得分:4)

我的假设是您仅使用英语/ ASCII字符,并且某些内容可能会使流混乱。 注意:我假设),没有特殊字符,如果是这样,那么我建议您将整个json字符串传递给此函数:

function cleanString(input) {
    var output = "";
    for (var i=0; i<input.length; i++) {
        if (input.charCodeAt(i) <= 127) {
            output += input.charAt(i);
        }
    }
    console.log(output);
}

//example
cleanString("�~�")

您可以引用How to remove invalid UTF-8 characters from a JavaScript string?

编辑

摘自Internet Engineering Task Force (IETF)的一篇文章

  

发送文本数据时出现常见的安全问题      使用错误的编码。该协议指定带有      文本数据类型(与Binary或其他类型相反)包含UTF-8-      编码数据。虽然长度仍然指示和      实现此协议的应用程序应使用长度来      确定帧实际结束的位置,并以不正确的方式发送数据

     
     

“有效载荷数据”是编码为UTF-8的文本数据。请注意,特定的文本框架可能包括部分UTF-8序列。但是,整个消息必须包含有效的UTF-8。按照 UTF-8编码数据中的处理错误中的说明处理重组邮件中无效的UTF-8,该状态指出当端点将字节流解释为UTF-8时,却发现字节流实际上不是有效的UTF-8流,则端点必须_使WebSocket Connection_失败。此规则在打开握手期间和后续数据交换期间均适用。

我真的相信您的错误(或功能)是来自服务器端的,它结合了您的各个消息,因此我建议采用一种逻辑,以确保您的所有字符必须都可以转换首先将字符编码为UTF-8,从Unicode转换为ASCII。并且您可能还想安装npm install --save-optional utf-8-validate来有效地检查邮件是否包含规范要求的有效UTF-8。

您可能还希望通过if条件来帮助您进行一些检查;

this.ws.on('message', (rawdata) => {
    if (message.type === 'utf8') { // accept only text
    }

我希望这会有所帮助。

答案 1 :(得分:0)

这些字符被称为“ REPLACEMENT CHARACTER”-用于替换未知,无法识别或无法表示的字符。

发件人:https://en.wikipedia.org/wiki/Specials_(Unicode_block)

  

替换字符(通常是带有白色问号的黑色菱形或一个空的方框),是Unicode标准中Specials表中代码点U + FFFD处的符号。当系统无法将数据流呈现为正确的符号时,它用于指示问题。通常会在数据无效且不匹配任何字符时看到

检查section 8 of the WebSocket protocol Error Handling

8.1。从服务器处理UTF-8中的错误

当客户端将字节流解释为UTF-8但发现该字节流实际上不是有效的UTF-8流时,则无效的UTF-8序列的任何字节或字节序列必须为解释为U + FFFD替换字符。

8.2。从客户端处理UTF-8中的错误

当服务器将字节流解释为UTF-8但发现该字节流实际上不是有效的UTF-8流时,则行为未定义。服务器可以关闭连接,将无效的字节序列转换为U + FFFD替换字符,逐字存储数据,或执行特定于应用程序的处理。 WebSocket协议上的子协议可能会定义服务器的特定行为。


取决于使用的实现或库如何处理此问题,例如,来自本帖子Implementing Web Socket servers with Node.js

socket.ondata = function(d, start, end) {
    //var data = d.toString('utf8', start, end);
    var original_data = d.toString('utf8', start, end);
    var data = original_data.split('\ufffd')[0].slice(1);
    if (data == "kill") {
        socket.end();
    } else {
        sys.puts(data);
        socket.write("\u0000", "binary");
        socket.write(data, "utf8");
        socket.write("\uffff", "binary");
    }
};

在这种情况下,如果找到,它将这样做:

var data = original_data.split('\ufffd')[0].slice(1);
if (data == "kill") {
    socket.end();
} 

您可以做的另一件事是,将OpenSSL and Breaking UTF-8 Change (fixed in Node v0.8.27 and v0.10.29)上的内容更新为最新的节点:

  

从这些发行版开始,如果您尝试传递带有不匹配代理对的字符串,则Node将用未知的Unicode字符(U + FFFD)替换该字符。为了保留旧的行为,请将环境变量NODE_INVALID_UTF8设置为任何值(甚至什么也没有)。如果根本没有环境变量,它将恢复为旧的行为。

答案 2 :(得分:0)

您的输出似乎有一些空格,如果有空格或发现任何特殊字符,请使用Unicode来完全填充它们。

Here is the list of Unicode characters

这可能对我有帮助。

答案 3 :(得分:0)

您遇到的问题是,一方发送的JSON与另一方发送的编码不同。

尝试使用以下代码解决此问题:

const { StringDecoder } = require('string_decoder');

this.ws.on('message', (rawdata) => {
    const decoder = new StringDecoder('utf8');
    const buffer = new Buffer(rawdata);
    console.log(decoder.write(buffer));
});

或使用utf16

const { StringDecoder } = require('string_decoder');

this.ws.on('message', (rawdata) => {
    const decoder = new StringDecoder('utf16');
    const buffer = new Buffer(rawdata);
    console.log(decoder.write(buffer));
});

请阅读:String Decoder Documentation