1、握手
握手的过程被称为 challenge-response。首先客户端发起一个名为Upgrade的HTTP GET请求,服务器验证此请求,给出101响应以表示接受此次协议升级,握手即完成了。
chrome inspector美化过的握手信息:
Request
Request Method:GET
Status Code:101 WebSocket Protocol Handshake
Request Headers
Connection:Upgrade
Host:192.168.144.131:4400
Origin::800
Sec-WebSocket-Key1:p2 G 947T 80 661 jAf2
Sec-WebSocket-Key2:z Z Q ^326 5 9= 7s1 1 7H4
Sec-WebSocket-Protocol::my-custom-chat-protocol
Upgrade:WebSocket
(Key3):7C:44:56:CA:1F:19:D2:0A
Response Headers
Connection:Upgrade
Sec-WebSocket-Location:ws://192.168.144.131:4400/pub/chat?q=me
Sec-WebSocket-Origin::800
Sec-WebSocket-Protocol:my-custom-chat-protocol
Upgrade:WebSocket
(Challenge Response):52:DF:2C:F4:50:C2:8E:98:14:B7:7D:09:CF:C8:33:40
请求头部分
Host: websocket服务器主机
Connection: 连接类型
Upgrade: 协议升级类型
Origin: 访问来源
Sec-WebSocket-Protocol: 可选,子协议名称,由应用自己定义,多个协议用空格分割。(*另外一个仅剩的可选项是cookie)
Sec-WebSocket-Key1: 安全认证key,xhr请求不能伪造'sec-'开头的请求头。
Sec-WebSocket-Key2: 同上
Key3: 响应体内容,8字节随机。
响应头部分
Sec-WebSocket-Protocol: 必须包含请求的子协议名
Sec-WebSocket-Origin: 必须等于请求的来源
Sec-WebSocket-Location: 必须等于请求的地址
Challenge Response: 响应体内容,根据请求中三个key计算得来,16字节。
应答字符串计算过程伪代码:
part_1 = key1中所有数字 / key1中空格数量 part_2 同上 sum = big_endian(part_1)+big_endian(part_2)+key3 challenge_response = md5_digest(sum);
32位整数的big_endian计算策略:
# 很类似于rgba颜色计算,从下面的函数可以看出计算过程 var big_endian = function(n) { return [3,2,1,0].map(function(i) { return n >> 8*i & 0xff }); } big_endian(0xcc77aaff); // -> [204, 119, 170, 255]
2、发送数据
WebSocket API的被设计成用事件处理数据,客户端只要得到事件通知就可以获取到完整的数据,而不需要手动处理缓冲器。
这种情况下,每一笔数据被称为一帧。在规范的定义中,它的头部必须以0x00开始,尾部属性以0xff结束,这样每一次数据发送至少有两个字节。
服务器实现中,收到数据时要截掉头尾;而发送数据是要包装头尾。格式如下:
# '你好'的原始二进制表示,请求头和这里都是utf8编码
<Buffer e4 bd a0 e5 a5 bd>
# 包装后的二进制表示。
<Buffer 00 e4 bd a0 e5 a5 bd ff>