以下是一个简单的使用 C++ 解析 WebSocket 帧的代码示例:
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 定义 WebSocket 帧类型
enum Opcode {
CONTINUATION_FRAME = 0x00,
TEXT_FRAME = 0x01,
BINARY_FRAME = 0x02,
CLOSE_FRAME = 0x08,
PING_FRAME = 0x09,
PONG_FRAME = 0x0A
};
// 定义 WebSocket 帧头部结构体
struct FrameHeader {
bool fin;
Opcode opcode;
bool mask;
uint64_t payloadLength;
};
// 解析 WebSocket 帧头部信息
FrameHeader ParseFrameHeader(const char* buffer, size_t length) {
FrameHeader header;
// 判断 FIN 标志位
header.fin = (buffer[0] & 0x80) != 0;
// 获取操作码
header.opcode = static_cast<Opcode>(buffer[0] & 0x0F);
// 判断是否使用掩码
header.mask = (buffer[1] & 0x80) != 0;
// 获取负载数据长度
uint8_t payloadLenByte = buffer[1] & 0x7F;
if (payloadLenByte == 126) {
if (length < sizeof(FrameHeader) + sizeof(uint16_t)) {
throw runtime_error("Invalid frame: insufficient data");
}
const uint16_t* pPayloadLength16Bit = reinterpret_cast<const uint16_t*>(buffer + sizeof(FrameHeader));
header.payloadLength = ntohs(*pPayloadLength16Bit);
} else if (payloadLenByte == 127) {
if (length < sizeof(FrameHeader) + sizeof(uint64_t)) {
throw runtime_error("Invalid frame: insufficient data");
}
const uint64_t* pPayloadLength64Bit = reinterpret_cast<const uint64_t*>(buffer + sizeof(FrameHeader));
header.payloadLength = be64toh(*pPayloadLength64Bit);
} else {
header.payloadLength = payloadLenByte;
}
return header;
}
// 解析 WebSocket 帧负载数据
vector<char> ParseFramePayload(const char* buffer, size_t length, const FrameHeader& header) {
vector<char> payload;
// 获取掩码
const char* maskKey = nullptr;
if (header.mask) {
maskKey = buffer + sizeof(FrameHeader);
}
// 计算负载数据起始位置和长度
size_t payloadOffset = sizeof(FrameHeader);
size_t payloadLength = static_cast<size_t>(header.payloadLength);
if (header.mask) {
payloadOffset += 4;
payload.resize(payloadLength);
for (size_t i = 0; i < payloadLength; ++i) {
size_t j = i % 4;
payload[i] = buffer[payloadOffset + i] ^ maskKey[j];
}
} else {
payload.assign(buffer + payloadOffset, buffer + length);
}
return payload;
}
int main() {
// 构造测试数据
string testDataStr =
"\x81\x85\x37\xfa\x21\x3d\x7f\x9f\xb6\xf1"
"Hello";
const char* testDataBuffer = testDataStr.c_str();
size_t testDataSize = testDataStr.size();
try {
// 解析帧头部信息
FrameHeader header = ParseFrameHeader(testDataBuffer, testDataSize);
// 解析负载数据
vector<char> payload = ParseFramePayload(testDataBuffer, testDataSize, header);
// 打印解析结果
cout << "FIN: " << header.fin << endl;
cout << "Opcode: " << header.opcode << endl;
cout << "Mask: " << header.mask << endl;
cout << "Payload Length: " << header.payloadLength << endl;
cout << "Payload Data: ";
for (auto c : payload) {
cout.put(c);
}
cout << endl;
} catch (const exception& e) {
cerr << e.what() << endl;
}
return 0;
}
上面的代码中,使用了 ParseFrameHeader 函数和 ParseFramePayload 函数来分别解析 WebSocket 帧头部信息和负载数据。
在 ParseFrameHeader 函数中,首先判断 FIN 标志位、操作码、是否使用掩码以及负载数据长度。然后根据不同情况计算出实际的负载数据长度,并返回一个包含这些信息的结构体。
在 ParseFramePayload 函数中,根据帧头部信息获取掩码以及负载数据起始位置和长度。如果使用了掩码,则对每个字节进行异或操作;否则直接拷贝到 vector 中。最后返回包含所有解析出来的负载数据的 vector。
上述示例代码只是基本示例,并未考虑所有特殊情况。在实际应用中,还需要根据实际情况进行更加严谨的解析处理。




