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

Posted

技术标签:

【中文标题】如何处理奇怪组合的 websocket 消息?【英文标题】:How to handle weirdly combined websocket messages? 【发布时间】:2019-01-13 04:58:32 【问题描述】:

我正在使用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 个这样的错误文件,这意味着这种情况经常发生。我在终端中cated 了几个这样的文件,我在下面上传了一个示例:

这里有几件事很有趣:

    文件总是以这些奇怪的符号之一开头。 文件似乎存在于应分别接收的多条消息中。奇怪的标志将这些单独的信​​息分开。 文件总是以未完成的 json 对象结尾。 文件的长度不同。它们的大小并不总是相同,因此不会在特定长度上被切断。

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

或者可能是因为服务器端的错误(或功能)将这些单独的消息组合在一起?

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

[编辑]

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

【问题讨论】:

由于 JSON 应该使用 UTF(8、16 或 32)之一进行编码,因此正确解码输入可能是个好主意。但是,这些位置的预期字符都属于 UTF-8 的 ASCII 子集,所以我怀疑解码会帮助您解决这个特殊问题。 �~ 字符是“替换字符”,因此如果您看到此问题,再修复它已经太晚了。您可以尝试使用 utf8 模块(npm install utf8)转换为 utf8,然后转换字符串(utf8.encode(string))吗? @bendataclear - 我尝试将结果添加到上述问题中。这会给你任何提示吗? @kramer65 - 看起来这是通过其他一些编码(二进制?),您使用的是标准节点 websocket 客户端(require('websocket').client)吗? @bendataclear - 不,我正在使用 ws 库:github.com/websockets/ws。我也在考虑二进制,但为什么呢?以及如何处理它?我在基于那个奇怪的字符串的 utf8 转换后尝试了拆分,但令我惊讶的是,这似乎不起作用。还有其他想法吗? 【参考方案1】:

我的假设是您只使用英文/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)的一篇文章,

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


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

我真的相信你的错误(或功能)来自服务器端,它结合了你的个人消息,所以我建议你想出一个逻辑来确保你所有的字符必须被转换通过首先将字符编码为 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
    

我希望这会有所帮助。

【讨论】:

删除一些随机字节不会使字符串成为有效的 json 当然,关于更改JSON的答案是不正确的,因为在回调函数中删除或添加一些字符为时已晚。你应该在服务器端修复它。【参考方案2】:

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

Here is the list of Unicode characters

这可能对我有帮助。

【讨论】:

【参考方案3】:

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

发件人: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 设置为任何值(甚至没有)。如果环境变量完全存在,它将恢复到旧的行为。

【讨论】:

【参考方案4】:

您遇到的问题是,一方以不同的编码发送 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

【讨论】:

以上是关于如何处理奇怪组合的 websocket 消息?的主要内容,如果未能解决你的问题,请参考以下文章

WebSocket 服务器如何处理多个传入的连接请求?

如何处理 Azure App Service WebSockets 超时?

whatsapp 是如何处理特殊字符、unicode 和 emoji 的

Swoole 如何处理高并发以及异步 I/O 的实现

当您尝试去混淆奇怪的代码时如何处理名称冲突?

如何处理 git 别名的组合