websocket是基于应用层的传输控制协议,而socket是基于传输层的传输控制协议。它们都是全双工的(可以同时接收和发送),传输层意味着数据都是以16进制传输,而传输层以二进制传输。
websocket的握手分为客户端请求和服务器端回应。
客户端的请求如下:
GET / HTTP/1.1 // 这个请求必须是GET请求,并且HTTP的协议必须是1.1
Connection:Upgrade // 表示客户端希望连接升级
Upgrade:websocket // 这个值必须是websocket,表示请求升级为websocket协议
Host:xxxx // 请求的主机名称
Origin:xxxx // 打开这个socket的页面
Sec-Websocket-Key:sN9cRrP/n9NdMgdcy2VJFQ== // 随机生成的字符串,客户端通过GUID来验证这个参数是不是一个有效请求
Sec-WebSocket-Version:13 // websocket使用的协议,RFC6455规定这个值必须万为13
Sec-WebSocket-Protocol:xxx // 客户端可以指定应用程序支持的协议,服务器选择其中的一个做为双方通信的协议
Sec-webSocket-Extension:xxxx // 客户端指定某些扩展协议,服务器选择其中一个
服务器端的回应如下:
HTTP/1.1 101 Switching Protocols // 服务器端同意客户端的握手请求
Connection: Upgrade // 同意这个连接升级
Sec-WebSocket-Accept: IIRiohCjop4iJrmvySrFcwcXpHo= // 通过GUID计算出来的值
Sec-WebSocket-Version: 13 // websocket的版本
Upgrade: websocket // 同意升级为websocket协议
如果HTTP的状态不是101客户端应该断开这个连接,如果Sec-WebSocket-Protocol规定了其中的子协议则服务器必须响应其中一个,否则客户端必须断开这个连接。
Sec-WebSocket-Accept的值以php计算如下:
$client_key = 'xxxxx';
$guid = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
$join = $client_key . $guid;
$sha1 = sha1($join,true);
$accept = base64_encode($sha1);
也就是客户端的Sec-WebSocket-Key的值加上GUID后,哈希为20个字符串后,再使用base64编码即可得到服务器的Sec-WebSocket-Accept的值
数据帧格式:(这个图示转载别人的,原帖在这里:https://www.cnblogs.com/songwenjie/p/8575579.html)
第一个字节包括:
FIN:一位,是否是最后一个消息分片。如果此值是0表示这个消息不是最后一个,反之就是最后一个
RSV1,RSV2,RS3:三位,用于扩展定义的值各一位,如果没有扩展这个值必须为0
opcode:四位,消息接收类型
第二个字节:
MASK:一位,数据是否经过掩码处理,客户端发往服务端的数据都必须经过掩码处理,值为1。
payloadData的长度:7位,7+16位,7+64位
如果其值在0-125,则是payload的真实长度。
如果值是126,则后面2个字节形成的16位无符号整型数的值是payload的真实长度。
如果值是127,则后面8个字节形成的64位无符号整型数的值是payload的真实长度。
其中《原来你是这样的websocket》对消息帧讲解的非常透彻,文中一些图片和文字均来自这篇文章。
这个里有几个不错的帖子
https://www.cnblogs.com/songwenjie/p/8575579.html(原来你是这样的websocket)
https://segmentfault.com/a/1190000013298527(websocket协议)
https://blog.csdn.net/stoneson/article/details/8063802(websocket规范 RFC6455翻译中文文档)
https://www.w3.org/TR/websockets/(w3c关于websocket介绍)