WEBSOCKET协议判断、握手及反馈
2011-08-19 10:48
363 查看
消息中心的布点,是通过WEBSOCKET与后端服务器建立长连接的方式实现的,这种方式的优点一是节约网络带宽,二是用户可以实时的收到由后台发过来的消息,后端的实现采用的是NETTY,经过压力测试,每台服务器可以承受50万的长连接,也就是同时50万个用户(只为每个网站用户建立一个长连接),性能上还是比较好的。
要建立长连接,首先需要由客户端发起与服务端的握手动作,以下是从wikipedia找的一个示例:
浏览器请求:
在请求中的“Sec-WebSocket-Key1”, “Sec-WebSocket-Key2”和最后的“^n:ds[4U”都是随机的,服务器端会用这些数据来构造出一个16字节的应答。其中:^n:ds[4U为请求的内容,其它的都是http请求头。
注:Sec-WebSocket-Key1和Sec-WebSocket-Key2在旧的WEBSOCKET协议中是没有的,因为判断当前请求是否WEBSOCKET,主要还是通过请求头中的Connection是不是等于Upgrade以及Upgrade是否等于WebSocket,也就是说判断一个请求是否WEBSOCKET请求,只需要判断请求头中的Connection及Upgrade,判断新旧版本可以通过是否包含“Sec-WebSocket-Key1”和“Sec-WebSocket-Key2”。以下是一小段判断是否WEBSOCKET请求的代码:
服务端回应:
后记:
最近发现chrome14及FF6.5中使用最新的websocket草案10协议,也就是说上面的例出的代码还不能够支持草案10的握手协议,草案手的协议变量比较大,如传输是通过帧来进行的,并且对帧的位有权限检查等,详细可以查看我的另外一篇文章:/article/2658268.html
本文出自:冯立彬的博客
要建立长连接,首先需要由客户端发起与服务端的握手动作,以下是从wikipedia找的一个示例:
浏览器请求:
GET /demo HTTP/1.1 Host: example.com Connection: Upgrade Sec-WebSocket-Key2: 12998 5 Y3 1 .P00 Sec-WebSocket-Protocol: sample Upgrade: WebSocket Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5 Origin: http://example.com ^n:ds[4U
在请求中的“Sec-WebSocket-Key1”, “Sec-WebSocket-Key2”和最后的“^n:ds[4U”都是随机的,服务器端会用这些数据来构造出一个16字节的应答。其中:^n:ds[4U为请求的内容,其它的都是http请求头。
注:Sec-WebSocket-Key1和Sec-WebSocket-Key2在旧的WEBSOCKET协议中是没有的,因为判断当前请求是否WEBSOCKET,主要还是通过请求头中的Connection是不是等于Upgrade以及Upgrade是否等于WebSocket,也就是说判断一个请求是否WEBSOCKET请求,只需要判断请求头中的Connection及Upgrade,判断新旧版本可以通过是否包含“Sec-WebSocket-Key1”和“Sec-WebSocket-Key2”。以下是一小段判断是否WEBSOCKET请求的代码:
//注:代码是基于Netty的 private boolean isWebSocketReq(HttpRequest req) { return (HttpHeaders.Values.UPGRADE.equalsIgnoreCase(req.getHeader(HttpHeaders.Names.CONNECTION)) && HttpHeaders.Values.WEBSOCKET.equalsIgnoreCase(req.getHeader(HttpHeaders.Names.UPGRADE))); }
服务端回应:
HTTP/1.1 101 WebSocket Protocol Handshake Upgrade: WebSocket Connection: Upgrade Sec-WebSocket-Origin: http://example.com Sec-WebSocket-Location: ws://example.com/demo Sec-WebSocket-Protocol: sample 8jKS’y:G*Co,Wxa-把请求的第一个Key中的数字除以第一个Key的空白字符的数量,而第二个Key也是如此。然后把这两个结果与请求最后的8字节字符串连接起来成为一个字符串,服务器应答正文(“8jKS’y:G*Co,Wxa-”)即这个字符串的MD5 sum。以下是一段基于Netty的响应J***A代码:
//基于Netty的WEBSOCKET响应代码 private HttpResponse buildWebSocketRes(HttpRequest req) { HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, new HttpResponseStatus(101, "Web Socket Protocol Handshake")); res.addHeader(HttpHeaders.Names.UPGRADE, HttpHeaders.Values.WEBSOCKET); res.addHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.UPGRADE); // Fill in the headers and contents depending on handshake method. if (req.containsHeader(Names.SEC_WEBSOCKET_KEY1) && req.containsHeader(Names.SEC_WEBSOCKET_KEY2)) {//草案7.5、7.6和协议标准 // New handshake method with a challenge: res.addHeader(Names.SEC_WEBSOCKET_ORIGIN, req.getHeader(Names.ORIGIN)); res.addHeader(Names.SEC_WEBSOCKET_LOCATION, getWebSocketLocation(req)); String protocol = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); if (protocol != null) { res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, protocol); } // Calculate the answer of the challenge. String key1 = req.getHeader(Names.SEC_WEBSOCKET_KEY1); String key2 = req.getHeader(Names.SEC_WEBSOCKET_KEY2); int a = (int) (Long.parseLong(getNumeric(key1)) / getSpace(key1).length()); int b = (int) (Long.parseLong(getNumeric(key2)) / getSpace(key2).length()); long c = req.getContent().readLong(); ChannelBuffer input = ChannelBuffers.buffer(16); input.writeInt(a); input.writeInt(b); input.writeLong(c); ChannelBuffer output = null; try { output = ChannelBuffers.wrappedBuffer(MessageDigest.getInstance("MD5").digest(input.array())); } catch (NoSuchAlgorithmException e) { logger.error("no such Algorithm : MD5. ", e); } res.setContent(output); } else {//最老的websocket协议 // Old handshake method with no challenge: if (req.getHeader(Names.ORIGIN) != null) { res.addHeader(Names.WEBSOCKET_ORIGIN, req.getHeader(Names.ORIGIN)); } res.addHeader(Names.WEBSOCKET_LOCATION, getWebSocketLocation(req)); String protocol = req.getHeader(Names.WEBSOCKET_PROTOCOL); if (protocol != null) { res.addHeader(Names.WEBSOCKET_PROTOCOL, protocol); } } return res; } // 去掉传入字符串的所有非数字 private String getNumeric(String str) { return str.replaceAll("\\D", ""); } // 返回传入字符串的空格 private String getSpace(String str) { return str.replaceAll("\\S", ""); }
后记:
最近发现chrome14及FF6.5中使用最新的websocket草案10协议,也就是说上面的例出的代码还不能够支持草案10的握手协议,草案手的协议变量比较大,如传输是通过帧来进行的,并且对帧的位有权限检查等,详细可以查看我的另外一篇文章:/article/2658268.html
本文出自:冯立彬的博客
相关文章推荐
- [转载]websocket最新协议的握手实现
- websocket最新协议的握手实现
- websocket协议握手详解
- 基于Websocket草案10协议的升级及基于Netty的握手实现
- Websocket协议之握手连接
- WebSocket握手协议
- Websocket协议之握手连接
- 打开阶段握手——WebSocket协议翻译
- WebSocket握手协议
- WebSocket握手协议
- 基于Websocket草案10协议的升级及基于Netty的握手实现
- websocket服务器握手协议
- MQTT协议笔记之mqtt.io项目Websocket协议支持
- TCP协议的三次握手和四次挥手
- TCP三次握手和四次挥手协议
- Websocket协议的学习、调研和实现
- mysql握手连接协议
- TCP协议中的三次握手和四次挥手(图解)
- 网络与通信:网络协议及握手
- 协议连接建立时3次握手的过程4次结束连接