一、帧结构图及含义
0Bit:
FIN 结束标识位,如果FIN为1,代表该帧为结束帧(如果一条消息过长可以将其拆分为多个帧,这时候FIN可以置为0,表示后面还有数据帧,服务器需要将该帧内容缓存起来,待所有帧都接收后再拼接到一起。控制帧不可拆分为多帧)。
1~3Bit:
RSV1~RSV3 保留标识位,以后做协议扩展时才会用到,目前该3位都为0
4~7Bit:
opcode 操作码,用于标识该帧负载的类型,如果收到了未知的操作码,则根据协议,需要断开WebSocket连接。操作码含义如下:
0x00 连续帧,浏览器的WebSocket API一般不会收到该类型的操作码
0x01 文本帧,最常用到的数据帧类别之一,表示该帧的负载是一段文本(UTF-8字符流)
0x02 二进制帧,较常用到的数据帧类别之一,表示该帧的负载是二进制数据
0x03-0x07 保留帧,留作未来非控制帧扩展使用
0x08 关闭连接控制帧,表示要断开WebSocket连接,浏览器端调用close方法会发送0x08控制帧
0x09 ping帧,用于检测端点是否可用,暂未发现浏览器可以通过何种方法发送该帧
0x0A pong帧,用于回复ping帧,暂未发现浏览器可以发送此种类型的控制帧
0x0B-0x0F 保留帧,留作未来控制帧扩展使用
8Bit:
MASK 掩码标识位,用来表明负载是否经过掩码处理,浏览器发送的数据都是经过掩码处理(浏览器自动处理,无需开发者编码),服务器发送的帧必须不经过掩码处理。所以此处浏览器发送的帧必为1,服务器发送的帧必为0,否则应断开WebSocket连接
9~15Bit:
payload length 负载长度,单位字节如果负载长度0~125字节,则此处就是负载长度的字节数,如果负载长度在126~65535之间,则此处的值为126,16~32Bit表示负载的真实长度。如果负载长度在65536~2的64次方-1时,16~80Bit表示负载的真实长度。其中负载长度包括应用数据长度和扩展数据的长度
payload length 后面4个字节可能是掩码的key(如果掩码位是1则有这4个字节的key,否则没有),掩码计算方法将在后面给出。
接下来就是负载的数据了,他们可能需要根据掩码的key进行编码(仅浏览器需要掩码),如果存在扩展数据,需要放在应用数据之前
#include
#include
#include
#include
#include
typedef struct
{
/* byte 0 */
unsigned char opcode:4;
unsigned char R:3;
unsigned char F:1;
/* byte 1 */
unsigned char payload_len:7;
unsigned char M:1;
unsigned short payload_len1; // if (payload_len == 126) 有效
unsigned long long payload_len2; // if (payload_len == 127) 有效
unsigned int Masking_key; //if (m ==1 ) 有效
}websocket_header;
int main()
{
//unsigned char buf[] = {0x81, 0x84, 0xd4, 0xde, 0xf5, 0x2c};//test payload_len < 126
/*
//test payload_len == 126
unsigned char buf[] = {0x81, 0xfe, 0x00, 0x89
, 0xbc, 0xb7, 0xdb, 0x3e
};
*/
//test payload_len == 127
unsigned char buf[] = {0x81, 0xff, 0x00, 0x89
, 0xbc, 0xb7, 0xdb, 0x3e
, 0xbc, 0xb7, 0xf5, 0x2e
, 0xd4, 0xde, 0xf5, 0x2c
};
int idx = 0; //解析buf的索引
websocket_header header;
memset(&header, 0, sizeof(header));
memcpy(&header, buf, 2);
idx += 2;
printf("*******input buf len: %d\n", sizeof(buf));
printf("F: %d\n", header.F);
printf("R: %d\n", header.R);
printf("opcode: %d\n", header.opcode);
printf("M: %d\n", header.M);
printf("payload_len: %d\n", header.payload_len);
if (header.payload_len == 126)
{
unsigned short temp = 0;
memcpy(&temp, &buf[idx], sizeof(temp));
idx += sizeof(temp);
header.payload_len1 = ntohs(temp);
}
else if (header.payload_len == 127)
{
//大端转小端 转换网络字节序的8字节到主机字节序payload_len2
unsigned long long temp = 0;
header.payload_len2 = 0;
temp = 0;
temp |= buf[idx++];
header.payload_len2 |= (temp << 56);
temp = 0;
temp |= buf[idx++];
header.payload_len2 |= (temp << 48);
temp = 0;
temp |= buf[idx++];
header.payload_len2 |= (temp << 40);
temp = 0;
temp |= buf[idx++];
header.payload_len2 |= (temp << 32);
temp = 0;
temp |= buf[idx++];
header.payload_len2 |= (temp << 24);
temp = 0;
temp |= buf[idx++];
header.payload_len2 |= (temp << 16);
temp = 0;
temp |= buf[idx++];
header.payload_len2 |= (temp << 8);
temp = 0;
temp |= buf[idx++];
header.payload_len2 |= (temp << 0);
}
if (header.M == 1)
{
unsigned int temp = 0;
memcpy(&temp, &buf[idx], sizeof(temp));
idx += sizeof(temp);
header.Masking_key = ntohl(temp);
}
printf("payload_len1: %d\n", header.payload_len1);
printf("payload_len2: %llu, 0x%016llx\n", header.payload_len2, header.payload_len2);
printf("Masking_key: %#x\n", header.Masking_key);
return 0;
}