websocket 简介
这里介绍的是 websocket 的一些内容,主要就是最小可运行的 websocket 的基础部分,更深层次的内容不在这里。
先看一张图
代码分析
对于 handleshake 部分这里不分析了,与 http 的交互很相似,只是字段的变化
主要看看在内容交互阶段对消息格式的解析
解析部分
以下为伪代码
/*read fin and op code*/
int n = cm_conn_tcp_recv_simple(conn, tmpdata, &len);
if (n <= 0) {
return n == 0 ? 0 : -1;
}
// 1. 读取前两个字节后,解析是否是最后一条消息以及消息中的 payload 格式
head->cFin = (p[0] & 0x80) == 0x80;
head->cOpcode = p[0] & 0x0F;
n = cm_conn_tcp_recv_simple(conn, tmpdata, &len);
if (n <= 0) {
return n == 0 ? 0 : -2;
}
// 2. 继续读取2个字节,提取出是否对内容做的 掩码计算,掩码值在后面读取,如果没有掩码值 ,后面直接就是内容
// 同时取出此字节表示的数据长度
head->cMask = (p[0] & 0x80) == 0x80;
head->ullPayloadLength = p[0] & 0x7f;
// 3. 数据长度分两种,
// 3.1 126 表示后面有两个字节来表示真实的负载长度,读取两个字节并提取出长度
// 3.2 127 表示后面有8个字节来表示真实的负载长度,读取8个字节并提取长度
// 注意:这里面的长度是 网络字节序,如果系统使用的不是这样的,应当进行转化,即小端序与大端序的区别
if (head->ullPayloadLength == 126)
{
char extern_len[2];
len = 2;
n = cm_conn_tcp_recv_simple(conn, extern_len, &len);
if (n <= 0)
{
return n == 0 ? 0 : -3;
}
head->ullPayloadLength = (extern_len[0]&0xFF) << 8 | (extern_len[1]&0xFF);
}
else if (head->ullPayloadLength == 127)
{
char extern_len[8];
len = 8;
n = cm_conn_tcp_recv_simple(conn, extern_len, &len);
if (n <= 0)
{
return n == 0 ? 0 : -4;
}
cm_ws_invert_string(extern_len,8);
memcpy(&(head->ullPayloadLength),extern_len,8);
}
// 4. 如果有掩码,需要读取出来,4个字节长度
/*read masking-key*/
if (head->cMask) {
len = 4;
n = cm_conn_tcp_recv_simple(conn, tmpdata, &len);
if (n <= 0) {
return n == 0 ? 0 : -5;
}
}
// 5. 下面则是实际内容部分
组装部分
// 1. 定义相关的头部分
// Generate websocket header
CMWSFrameHeader header;
memset(&header, 0, sizeof(CMWSFrameHeader));
CMWSFrameHeader resheader;
memset(&resheader, 0, sizeof(CMWSFrameHeader));
char *head_str = NULL;
int head_str_len = 0;
// 2. 设置长度和掩码
// 2.1 随机生成掩码值,用于后面的 掩码计算
// Set header
header.ullPayloadLength = len;
header.cMask = 1;
cm_random_with_num_char(header.cMasking_key, 4);
// 3. 下面这个函数会跟据数据长度,去填充对应的字段,并设置掩码标志位等信息,这里就不列出了
cm_ws_generate_frame_head(&header, &head_str, &head_str_len);
cmdebug("websocket request generated header length is [%d] payload length is [%d]\n", head_str_len, len);
// Send with data
int sendlen = head_str_len + len;
char *senddata = (char*)malloc(sendlen);
memcpy(senddata, head_str, head_str_len);
// 4. 生成掩码并写入头结构中
cmdebug("websocket request send data \n[%s]\n", msg);
if (header.cMask) {
cm_ws_umask((char*)msg, len, header.cMasking_key);
}
掩码计算
// 根据 rfc 文档而来
for (i=0;i<len;++i)
*(data+i) ^= *(mask+(i%4));
附件
- 参见工程源码 jklibs,
common/cm_conn_ws.c, common/cm_http.c, common/cm_http_tcp.c