【发布时间】:2015-02-26 21:29:53
【问题描述】:
我正在尝试解释通过 TCP 连接获得的 WebSocket 帧。我想在纯 C 中做到这一点(所以没有 reinterpret_cast)。格式在IEEE RFC 6455 中指定。我想填写以下结构:
typedef struct {
uint8_t flags;
uint8_t opcode;
uint8_t isMasked;
uint64_t payloadLength;
uint32_t maskingKey;
char* payloadData;
} WSFrame;
具有以下功能:
static void parseWsFrame(char *data, WSFrame *frame) {
frame->flags = (*data) & FLAGS_MASK;
frame->opcode = (*data) & OPCODE_MASK;
//next byte
data += 1;
frame->isMasked = (*data) & IS_MASKED;
frame->payloadLength = (*data) & PAYLOAD_MASK;
//next byte
data += 1;
if (frame->payloadLength == 126) {
frame->payloadLength = *((uint16_t *)data);
data += 2;
} else if (frame->payloadLength == 127) {
frame->payloadLength = *((uint64_t *)data);
data += 8;
}
if (frame->isMasked) {
frame->maskingKey = *((uint32_t *)data);
data += 4;
}else{
//still need to initialize it to shut up the compiler
frame->maskingKey = 0;
}
frame->payloadData = data;
}
代码适用于 ESP8266,因此只能使用 printfs 到串行控制台进行调试。使用这种方法,我发现代码在frame->maskingKey = *((uint32_t *)data); 之后立即崩溃,并且前两个 if 被跳过,所以这是我第一次将指针转换为另一个指针。
数据没有\0 终止,但我在接收到的数据回调中获得了大小。在我的测试中,我试图通过已经建立的WebSocket发送消息'test',并且接收到的数据长度是10,所以:
- 1 字节标志和操作码
- 1 字节掩码和有效负载长度
- 4 字节掩码键
- 4 字节有效载荷长度
在代码崩溃时,我希望数据从初始位置偏移 2 个字节,因此它有足够的数据来读取以下 4 个字节。
我很长时间没有编写任何 C 代码,所以我预计我的代码中只会出现一个小错误。
PS.:我见过很多代码,它们逐字节解释值并移动值,但我看不出为什么这种方法也不起作用。
【问题讨论】:
-
根据您提供的信息很难确定,但对于 32 位取消引用,数据指针似乎没有正确对齐。尝试一次构造一个字节的 maskingKey。在您的所有代码中,取消引用数据作为字节指针。例如。 ((uint8_t*)data)[0]
-
IEEE RFC 6455 参考中的图片显示掩码键未在 32 位边界上对齐,因此未对齐的访问可能导致崩溃。
-
您的
payloadLength、maskingKey和payloadData在该结构中未正确对齐。您可以在上面使用#pragma pack,但是在使用它们之前,您必须将这些变量memcpy复制到本地副本中。更好的是,只需重新定义您的协议,并在这些字段之前添加uint8_t reserved。 -
&开头的行有点可疑,“操作码”和“长度”等值更常见的是基于 LSB 而不是将它们保持在它们的任何位位置进去了。
标签: c casting pointer-arithmetic