HTTP协议是短连接的,通信模式如图:
如何实现服务器主动向客户端发送数据呢?!websocket协议就是解决这个问题的。
通信过程大致如下:
本文中要有两部分组成 一、建立连接请求过程
二、数据帧格式
一、建立连接请求过程
在建立连接请求过程中使用的协议是HTTP协议。
1、客户端发送建立连接请求
使用的是HTTP协议头,服务器端按照HTTP协议正常解析,但这块不同的是服务器程序必须判断是否是websocket请求,其实服务器只需要查看该http协议头中是否包含了websockt等字符串,请求头如下
websockt握手请求协议包含Upgrade: websocket字段,服务器通过该字段就可以知道客户端想要建立websockt连接请求。
2、服务器响应连接请求
服务器从请求协议包中获取Sec-WebSocket-Key字段值,如下图:
然后将该key值与字符串“258EAFA5-E914-47DA-95CA-C5AB0DC85B11”拼接形成新的字符串,如"d359Fdo6omyqfxyYF7Yacw==258EAFA5-E914-47DA-95CA-C5AB0DC85B11";然后服务器对该字符串进行SHA-1加密,之后再进行一次base64加密,将加密之后的结果作为最终的响应key发送给客户端,服务器相依协议头如下:
其中Sec-WebSocket-Accept:字段填充由请求中的Sec-WebSocket-Key字段值与字符串”258EAFA5-E914-47DA-95CA-C5AB0DC85B11“拼接而成的字符串经过加密之后的字符串组成。
按照以上两步即可建立websocket连接。接下来客户端与服务器就可以互相异步的发送数据了。
二、数据帧格式
连接建立之后,客户端与服务器就可以相互发送数据了,数据包格式如下:
注意:
如果条件不满足,则扩展字节不出现在数据帧中。
列 明
位数
含义
取值
FIN
1 bit
是否是最后一帧数据
1表示后面没数据了
0:表示后面还有数据
RSV
3 bit
保留
OPCODE
4 bit
消息类型
0x0表示附加数据帧
0x1表示文本数据帧
0x2表示二进制数据帧
0x3-7暂时无定义,为以后的非控制帧保留
0x8表示连接关闭
0x9表示ping
0xA表示pong
0xB-F暂时无定义,为以后的控制帧保留
MASK
1 bit
是否经过掩码处理;
客户端必须对其发送到服务器的所有帧进行掩码处理。
服务器一旦收到无掩码帧,将关闭连接。服务器可能发送一个状态码是1002(表示协议错误)的Close帧。
而服务器发送客户端的数据帧不做掩码处理,一旦客户端发现经过掩码处理的帧,将关闭连接。客户端可能使用状态码1002
1:表示处理过0:表示没处理过
用于标识PayloadData是否经过掩码处理。如果是1,Masking-key域的数据即是掩码密钥,用于解码PayloadData。客户端发出的数据帧需要进行掩码处理,所以此位是1。
Payload length
7 bit
7+16bit
7+64 bit
PayloadData
数据长度
Payload长度是ExtensionData长度与ApplicationData长度之和
0-125:则是payload数据的真实长度
126:之后的2个字节的16位无符号正数值表示真是长度
127:之后的8个字节形成的64位无符号整型数的值是payload的真实长度
(注意网络字节序)
Masking-key
32 bit
掩码
data
....
数据
.....................
1、客户端到服务器的数据必须通过掩码处理
2、服务器收到数据包之后需要判断是否有掩码,即MASK是否有效,无效则发送关闭连接数据包,如果掩码位有效,则解掩码来获取真正的数据
3、服务器发送给客户端的数据不需要掩码处理,相对简单
协议示例:
客户端发送给服务器的文本消息”hello”
0x81 0x850x37 0xfa 0x21 0x3d0x7f 0x9f 0x4d 0x51 0x58(包含"Hello")
掩码与解掩码过程:
1)、计算j
j= i MOD 4
2)、计算掩码后的数据,解掩码方法相同 transformed-octet-i =original-octet-i XOR masking-key-octet-j
original-octet-i表示原始数据中下标为i的数据,如[0x7f 0x9f 0x4d 0x51 0x58]
masking-key-octet-j 表示四个字节组成的掩码数组中下标为j的掩码值,
如[0x37 0xfa 0x21 0x3d ]
transformed-octet-i:表示掩码或解掩码之后的数据
示例解码过程如下:
Unsigned char original-octet[] = {0x7f,0x9f,0x4d,0x51,0x58};
Unsigned char masking-key-octet[] = {0x37,0xfa,0x21,0x3d};
//LENGTH = sizeof(original-octet);
Unsigned char Transformed-octer[LENGTH];
0x7f: i = 0; => j = i % 4 = 0;
Transformed-octer[i] = original-octet[i] XOR masking-key-octet[j];
= 0x7F XOR 0x37 = 0x48
0x48 = ‘h’
其他数字掩码或解掩码过程同上
下一篇文章将给出服务器和客户端主要代码,服务器是Linux下用C语言编写的,客户端使用js脚本实现。