Arduino Websocket客户端接收数据乱码问题求助(附代码)
Arduino Websocket客户端接收文本乱码问题排查
我在为Arduino编写Websocket客户端库,目前连接与文本发送功能正常,服务器能正常接收数据,但接收服务器发送的文本时串口输出乱码,怀疑是掩码处理或其他逻辑存在问题。以下是相关代码及串口输出的乱码数据,请求协助排查问题。
processData()和processCompleteFrame()函数
void HsHSocketClient::processData() { static uint8_t fragmentedOpcode; static String fragmentedPayload; while (client.available()) { uint8_t opcode = client.read(); bool fin = (opcode & 0x80) != 0; opcode &= 0x0F; uint8_t payloadLen = client.read() & 0x7F; if (payloadLen == 126) { payloadLen = (client.read() << 8) | client.read(); payloadLen -= 2; // Subtract 2 to account for the two additional bytes read for length } else if (payloadLen == 127) { payloadLen = (client.read() << 24) | (client.read() << 16) | (client.read() << 8) | client.read(); payloadLen -= 8; // Subtract 8 to account for the eight additional bytes read for length } uint8_t maskingKey[4]; client.readBytes(maskingKey, 4); Serial.print("[Ws] - Masking Key: "); for (size_t i = 0; i < 4; ++i) { Serial.print(maskingKey[i], HEX); Serial.print(" "); } Serial.println(); uint8_t payload[payloadLen]; client.readBytes(payload, payloadLen); for (size_t i = 0; i < payloadLen; ++i) { payload[i] ^= maskingKey[i % 4]; } Serial.print("[Ws] - Unmasked Payload: "); for (size_t i = 0; i < payloadLen; ++i) { Serial.print(payload[i], HEX); Serial.print(" "); } Serial.println(); if (fin) { if (fragmentedPayload.length() > 0) { fragmentedPayload += String(payload, payloadLen); processCompleteFrame(fragmentedOpcode, fragmentedPayload.c_str(),payloadLen); fragmentedPayload = ""; } else { processCompleteFrame(opcode, reinterpret_cast<const char*>(payload),payloadLen); } } else { fragmentedOpcode = opcode; fragmentedPayload += String(payload, payloadLen); } } } void HsHSocketClient::processCompleteFrame(uint8_t opcode, const char* payload, size_t payloadLen) { Serial.print("[Ws] - Received frame, Opcode: 0x"); Serial.println(opcode, HEX); Serial.print("[Ws] - Payload Hex: "); for (size_t i = 0; i < payloadLen; ++i) { Serial.print(payload[i], HEX); Serial.print(" "); } Serial.println(); if (opcode == 0x1) { // Text frame Serial.print("[Ws] - Received text frame: "); Serial.println(payload); }else if (opcode == 0x2) { // Binary frame Serial.print("[Ws] - Received binary frame: "); Serial.println(payload); } else if (opcode == 0x8) { // Close frame Serial.print("[Ws] - Received close frame: "); Serial.println(payload); sendCloseFrame(); client.stop(); } else if (opcode == 0x9) { // Ping frame Serial.print("[Ws] - Received ping frame: "); Serial.println(payload); } else if (opcode == 0xA) { // Pong frame Serial.print("[Ws] - Received pong frame: "); Serial.println(payload); } else { Serial.print("[Ws] - Received unknown frame: "); Serial.println(payload); } }
Node.js服务器代码
wss.on('connection', function(ws) { console.log("Client connected!"); ws.on('error', console.error); ws.on('message', function(data) { let msg; try { msg = JSON.parse(data); } catch (error) { msg = data; } console.log('received: %s', msg); }); ws.on("close", function() { console.log("Client disconnected!"); }); setInterval(() => { ws.send('Test message'); }, 5000); });
串口输出数据
[Ws] - Connecting to server: 192.168.10.104:80 [Ws] - Connected to server [Ws] - Masking Key: 54 50 2F 31 [Ws] - Unmasked Payload: 7A 61 F 0 64 61 F 62 23 39 5B 52 3C 39 41 56 74 0 5D 5E 20 3F 4C 5E 38 23 22 3B 1 20 48 43 35 34 4A B 74 27 4A 53 27 3F 4C 5A 31 24 22 3B 17 3F 41 5F 31 33 5B 58 3B 3E 15 11 1 20 48 43 35 34 4A 3C 5E 3 4A 52 79 7 4A 53 7 3F 4C 5A 31 24 2 70 [Ws] - Masking Key: 65 70 74 3A [Ws] - Unmasked Payload: 45 34 4D E 33 3B 3D 59 3C 14 23 77 57 40 43 5D 1 22 3B 6D 1 35 46 6B 6 22 3F 63 58 7D 7E 37 6F 5B 66 BA B5 39 8F 5 C9 1 88 5 A1 B8 37 5 75 70 74 3A 64 70 74 3A 6E 71 74 3A ED CC F4 5 FF 5D 66 BA 95 39 8F 5 51 2C 88 5 6 70 74 3A 75 70 74 3A F1 2C 88 5 64 70 74 3A 45 73 8F 5 1B C1 60 [Ws] - Masking Key: 54 65 73 74 [Ws] - Unmasked Payload: 74 8 16 7 27 4 14 11 60 39 8F 4B [Ws] - Received frame, Opcode: 0x3 [Ws] - Payload Hex: 7A 61 F 0 64 61 F 62 23 39 5B 52 [Ws] - Received unknown frame: za [Ws] - Masking Key: 54 65 73 74 [Ws] - Unmasked Payload: 74 8 16 7 27 4 14 11 60 39 8F 4B [Ws] - Received frame, Opcode: 0x1 [Ws] - Payload Hex: 74 8 16 7 27 4 14 11 60 39 8F 4B [Ws] - Received text frame: '`9�K [Ws] - Masking Key: 54 65 73 74 [Ws] - Unmasked Payload: 74 8 16 7 27 4 14 11 60 39 8F 4B [Ws] - Received frame, Opcode: 0x1 [Ws] - Payload Hex: 74 8 16 7 27 4 14 11 60 39 8F 4B [Ws] - Received text frame: '`9�K
问题排查与修复
1. 错误处理MASK位(核心问题)
Websocket协议规定:服务器发送给客户端的帧不带掩码,只有客户端向服务器发送的帧需要掩码。你当前的代码无条件读取4字节掩码,导致把负载的前4个字节当成掩码读取,后续所有数据错位,这是乱码的根本原因。
修改代码:
// 替换原有的payloadLen读取逻辑 uint8_t secondByte = client.read(); bool masked = (secondByte & 0x80) != 0; // 判断是否带掩码 uint8_t payloadLen = secondByte & 0x7F; // ... 处理扩展长度 ... uint8_t maskingKey[4]; if (masked) { // 只有带掩码时才读取掩码 client.readBytes(maskingKey, 4); } // ... 读取payload后,仅在masked为true时解码 ... if (masked) { for (size_t i = 0; i < payloadLen; ++i) { payload[i] ^= maskingKey[i % 4]; } }
2. 负载长度计算错误
当payloadLen为126或127时,你错误地减去了扩展长度的字节数,导致实际读取的负载长度比真实长度少,后续数据错位。正确的做法是直接使用读取到的扩展长度作为payloadLen:
if (payloadLen == 126) { // 读取16位扩展长度 uint16_t extLen = (static_cast<uint16_t>(client.read()) << 8) | client.read(); payloadLen = extLen; } else if (payloadLen == 127) { // 读取64位扩展长度(Arduino为32位系统,这里只处理低32位,足够应对常规场景) uint32_t extLen = (static_cast<uint32_t>(client.read()) << 24) | (static_cast<uint32_t>(client.read()) << 16) | (static_cast<uint32_t>(client.read()) << 8) | client.read(); // 跳过高4字节(如果你的应用不需要处理超过4GB的负载) client.readBytes(new uint8_t[4], 4); payloadLen = extLen; }
3. 片段帧处理的长度错误
处理拼接后的片段帧时,你传入了当前帧的长度而非整个拼接后字符串的长度,导致打印时截断。修改如下:
if (fin) { if (fragmentedPayload.length() > 0) { fragmentedPayload += String(reinterpret_cast<char*>(payload), payloadLen); // 传入拼接后的总长度 processCompleteFrame(fragmentedOpcode, fragmentedPayload.c_str(), fragmentedPayload.length()); fragmentedPayload = ""; } else { processCompleteFrame(opcode, reinterpret_cast<const char*>(payload), payloadLen); } }
4. String构造的截断问题
原代码中String(payload, payloadLen)会把0x00字节当成字符串结束符,导致拼接片段帧时截断。改用String(reinterpret_cast<char*>(payload), payloadLen)可以避免这个问题。
内容的提问来源于stack exchange,提问作者Dr.Random
相关产品推荐
相关产品推荐

