首页 文章

如何处理奇怪的组合 websocket 消息?

提问于
浏览
23

我正在使用节点 ws 库(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 个错误文件,这意味着这种情况会发生很多。我在终端上编辑了几个这样的文件,其中我上传了一个例子:

在此输入图像描述

这里有一些有趣的事情:

  • 文件始终以这些奇怪的标志之一开始。

  • 这些文件似乎存在于应该单独接收的多条消息中。奇怪的标志将这些个别信息分开。

  • 文件总是以未完成的 json 对象结束。

  • 文件的长度各不相同。它们的尺寸并不总是相同,因此不会在特定长度上切断。

我对 websockets 不太熟悉,但可能是因为我的 websocket 以某种方式接收到它连接在一起的消息流,这些奇怪的符号作为分隔符,然后随机切断最后一条消息?也许是因为我得到了一个非常快速的消息流?

或者可能是因为服务器端的错误(或功能),它组合了这些单独的消息?

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

[编辑]

_1_suggested 将其解释为 utf8。所以我做了,我粘贴了下面结果的截图。第一个打印是原样,第二个打印为 utf8。对我来说这看起来不像什么。我当然可以转换为 utf8,然后按这些字符拆分。虽然最后一条消息总是被切断,但这至少会使一些消息变得可读。其他想法仍然欢迎。

在此输入图像描述

4 回答

  • 4

    我的假设是你只使用了 English/ASCII 个字符而且可能搞乱了流。 (NOTE:I am am),没有特殊字符,如果是这样,那么我建议你将整个 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("�~�")
    

    你可以参考如何从 JavaScript 字符串中删除无效的 UTF-8 字符?

    编辑

    来自互联网工程任务组(IETF)的文章,

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


    “有效载荷数据”是编码为 UTF-8 的文本数据。请注意,特定文本框架可能包含部分 UTF-8 序列;但是,整个消息必须包含有效的 UTF-8。重组消息中的无效 UTF-8 按照_处理 UTF-8-Encoded 数据**中的错误中的描述进行处理,其中指出当端点将字节流解释为 UTF-8 但发现字节流实际上不是有效的 UTF-8 流时,该端点必须_Fail WebSocket Connection_。此规则适用于打开握手期间和后续数据交换期间。

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

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

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

    我希望这会有所帮助。

  • 0

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

    这是 Unicode 字符列表

    这可能对我有所帮助。

  • 0

    这些字符称为“替换字符” - 用于替换未知,未识别或不可表示的字符。

    来自:https://en.wikipedia.org/wiki/Specials_(Unicode_block)

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

    检查WebSocket 协议错误处理的第 8 部分

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

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

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

    当服务器将字节流解释为 UTF-8 但发现字节流实际上不是有效的 UTF-8 流时,行为是未定义的。服务器可以关闭连接,将无效字节序列转换为 U FFFD REPLACEMENT CHARACTER,逐字存储数据或执行 application-specific 处理。在 WebSocket 协议上分层的子协议可以定义服务器的特定行为。


    取决于正在使用的实现或库如何处理这个,例如从这篇文章使用 Node.js 实现 Web 套接字服务器

    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 和 Breaking UTF-8 Change(在 Node v0.8.27 和 v0.10.29 中修复)

    从这些版本开始,如果您尝试传递具有不匹配代理项对的字符串,Node 将使用未知的 unicode 字符(U FFFD)替换该字符。要保留旧行为,请将环境变量 NODE_INVALID_UTF8 设置为任何内容(即使没有)。如果环境变量完全存在,它将恢复为旧行为。

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

    请阅读:字符串解码器文档

相关问题