1)、最老的websocket草案标准中是没有安全key,草案7.5、7.6中有两个安全key,而现在的草案10中只有一个安全key,即将7.5、7.6中http头中的"
Sec-WebSocket-Key1"与"
Sec-WebSocket-Key2"合并为了一个"Sec-WebSocket-Key"2)、把http头中Upgrade的值由"
WebSocket
"修改为了"websocket";3)、把http头中的"-Origin"修改为了"Sec-WebSocket-Origin";
4)、增加了http头"Sec-WebSocket-Accept",用来返回原来草案7.5、7.6服务器返回给客户端的握手验证,原来是以内容的形式返回,现在是放到了http头中;另外服务器返回客户端的验证方式也变了,后面会有介绍。
以下是一个格式标准图:
FIN:1位,用来表明这是一个消息的最后的消息片断,当然第一个消息片断也可能是最后的一个消息片断;
RSV1, RSV2, RSV3: 分别都是1位,如果双方之间没有约定自定义协议,那么这几位的值都必须为0,否则必须断掉WebSocket连接;
Opcode:4位操作码,定义有效负载数据,如果收到了一个未知的操作码,连接也必须断掉,以下是定义的操作码:
* %x0 表示连续消息片断
* %x1 表示文本消息片断
* %x2 表未二进制消息片断
* %x3-7 为将来的非控制消息片断保留的操作码
* %x8 表示连接关闭
* %x9 表示心跳检查的ping
* %xA 表示心跳检查的pong
* %xB-F 为将来的控制消息片断的保留操作码 Mask:1位,定义传输的数据是否有加掩码,如果设置为1,掩码键必须放在masking-key区域,客户端发送给服务端的所有消息,此位的值都是1;
Payload length: 传输数据的长度,以字节的形式表示:7位、7+16位、或者7+64位。如果 这个值以字节表示是0-125这个范围,那这个值就表示传输数据的长度;如果这个值是126,则随后的两个字节表示的是一个16进制无符号数,用来表示传 输数据的长度;如果这个值是127,则随后的是8个字节表示的一个64位无符合数,这个数用来表示传输数据的长度。多字节长度的数量是以网络字节的顺序表 示。负载数据的长度为扩展数据及应用数据之和,扩展数据的长度可能为0,因而此时负载数据的长度就为应用数据的长度。
Masking-key:0或4个字节,客户端发送给服务端的数据,都是通过内嵌的一个32位值作为掩码的;掩码键只有在掩码位设置为1的时候存在。
Payload data:(x+y)位,负载数据为扩展数据及应用数据长度之和。
Extension data:x位,如果客户端与服务端之间没有特殊约定,那么扩展数据的长度始终为0,任何的扩展都必须指定扩展数据的长度,或者长度的计算方式,以及在握手时如何确定正确的握手方式。如果存在扩展数据,则扩展数据就会包括在负载数据的长度之内。
Application data:y位,任意的应用数据,放在扩展数据之后,应用数据的长度=负载数据的长度-扩展数据的长度。
- ws-frame
= frame-fin -
frame-rsv1 -
frame-rsv2 -
frame-rsv3 -
frame-opcode -
frame-masked -
frame-payload-length -
[ frame-masking-key ] -
frame-payload-data -
- frame-fin
= %x0 ; 表示这不是当前消息的最后一帧,后面还有消息 -
/ %x1 ; 表示这是当前消息的最后一帧 -
- frame-rsv1
= %x0 -
; 1 bit, 如果没有扩展约定,该值必须为0 -
- frame-rsv2
= %x0 -
; 1 bit, 如果没有扩展约定,该值必须为0 -
- frame-rsv3
= %x0 -
; 1 bit, 如果没有扩展约定,该值必须为0 -
- frame-opcode
= %x0 ; 表示这是一个连续帧消息 -
/ %x1 ; 表示文本消息 -
/ %x2 ; 表示二进制消息 -
/ %x3-7 ; 保留 -
/ %x8 ; 表示客户端发起的关闭 -
/ %x9 ; ping(用于心跳) -
/ %xA ; pong(用于心跳) -
/ %xB-F ; 保留 -
- frame-masked
= %x0 ; 数据帧没有加掩码,后面没有掩码key -
/ %x1 ; 数据帧加了掩码,后面有掩码key -
- frame-payload-length
= %x00-7D -
/ %x7E frame-payload-length-16 -
/ %x7F frame-payload-length-63 -
; 表示数据帧的长度 -
- frame-payload-length-16
= %x0000-FFFF -
; 表示数据帧的长度 -
- frame-payload-length-63
= %x0000000000000000-7FFFFFFFFFFFFFFF -
; 表示数据帧的长度 -
- frame-masking-key
= 4( %0x00-FF ) ; 掩码key,只有当掩码位为1时出现 -
- frame-payload-data
= (frame-masked-extension-data -
frame-masked-application-data) ; 当掩码位为1时,这里的数据为带掩码的数据,扩展数据及应用数据都带掩码 -
/ (frame-unmasked-extension-data -
frame-unmasked-application-data) ; 当掩码位为0时,这里的数据为不带掩码的数据,扩展数据及应用数据都不带掩码 -
- frame-masked-extension-data
= *( %x00-FF ) ; 目前保留,以后定义 -
- frame-masked-application-data
= *( %x00-FF ) -
- frame-unmasked-extension-data
= *( %x00-FF ) ; 目前保留,以后定义 -
- frame-unmasked-application-data
= *( %x00-FF )
-
private HttpResponse buildWebSocketRes(HttpRequest req, boolean isWebSocketProtocolAfter Draft7) { -
String reasonPhrase = ""; -
// websocket协议草案7后面的格式,可以参看wikipedia上面的说明,比较前后版本的不同:http://en.wikipedia.org/wiki/WebSocket -
reasonPhrase = "Switching Protocols"; -
HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, new HttpResponseStatus(101, reasonPhrase)); -
res.addHeader(HttpHeaders.Names.UPGRADE, "websocket"); -
res.addHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.UPGRADE); -
String protocol = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); -
if (protocol != null) { -
res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, protocol); -
} -
res.addHeader(SEC_WEBSOCKET_ACCEPT, getSecWebSocketAccept(req)); -
return res; -
} - private
String getSecWebSocketAccept(HttpRequest req) { -
// CHROME WEBSOCKET VERSION 8中定义的GUID,详细文档地址:http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10 -
String guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; -
String key = ""; -
key = req.getHeader(SEC_WEBSOCKET_KEY); -
key += guid; -
try { -
MessageDigest md = MessageDigest.getInstance("SHA-1"); -
md.update(key.getBytes("iso-8859-1"), 0, key.length()); -
byte[] sha1Hash = md.digest(); -
key = base64Encode(sha1Hash); -
} catch (NoSuchAlgorithmException e) { -
if (logger.isErrorEnabled()) { -
logger.error("NoSuchAlgorithmException :" + e.getMessage(), e); -
} -
} catch (UnsupportedEncodingExcep tion e) { -
if (logger.isErrorEnabled()) { -
logger.error("UnsupportedEncodingExcep tion:" + e.getMessage(), e); -
} -
} -
return key; -
} -
-
public static String base64Encode(byte[] input) { -
BASE64Encoder encoder = new BASE64Encoder(); -
String base64 = encoder.encode(input); -
return base64; -
}