`
zzc1684
  • 浏览: 1224341 次
  • 性别: Icon_minigender_1
  • 来自: 广州
文章分类
社区版块
存档分类
最新评论

WebSocket不同版本的三种握手方式以及一个Netty实现JAVA类

阅读更多

一、WebSocket不同版本的三种握手方式

WebSocket是HTML5中的新特性,应用也是非常的广泛,特别是用户WEB端与后台服务器的消息通讯,如阿里的WEBWW就是使用的 WebSocket与后端服务器建立长连接进行的通讯。目前WebSocket还处于发展当中,就目前的发展过程而言,WebSocket现在不同的版 本,有三种不同的握手方式:

1、基于Flash的WebSocket通讯,使用场景是IE的多数版本,因为IE的多数版本不都不支持WebSocket协议,以及FF、CHROME等浏览器的低版本,还没有原生的支持WebSocket,可以使用FLASH的WebSocket实现进行通讯:

浏览器请求:

[plain] view plaincopy
  1. GET /ls HTTP/1.1  
  2. Upgrade: WebSocket  
  3. Connection: Upgrade  
  4. Host: www.xx.com  
  5. Origin: http://www.xx.com  

服务器回应:

[plain] view plaincopy
  1. HTTP/1.1 101 Web Socket Protocol Handshake  
  2. Upgrade: WebSocket  
  3. Connection: Upgrade  
  4. WebSocket-Origin: http://www.xx.com  
  5. WebSocket-Location: ws://www.xx.com/ls  

 

原理:

    如果客户端没有发送Origin请求头,则客户端不需要返回,如果客户端没有发送WebSocket-Protocol请求头,服务端也不需要返回;服务端唯一需要组装返回给客户端做为校验的就是WebSocket-Location请求头,拼装一个websocket请求的地址就可以了。

  这种方式,是最老的一种方式,连一个安全Key都没有,服务端也没有对客户的请求做加密性校验。

2、第二种握手方式是带两个安全key请求头的,结果以md5加密,并放在body中返回的方式,参看如下示例:

浏览器请求:

[plain] view plaincopy
  1. GET /demo HTTP/1.1  
  2. Host: example.com  
  3. Connection: Upgrade  
  4. Sec-WebSocket-Key2: 12998 5 Y3 1  .P00  
  5. Sec-WebSocket-Protocol: sample  
  6. Upgrade: WebSocket  
  7. Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5  
  8. Origin: http://example.com  
  9. ^n:ds[4U  

 

服务器回应:

[plain] view plaincopy
  1. HTTP/1.1 101 WebSocket Protocol Handshake  
  2. Upgrade: WebSocket  
  3. Connection: Upgrade  
  4. Sec-WebSocket-Origin: http://example.com  
  5. Sec-WebSocket-Location: ws://example.com/demo  
  6. Sec-WebSocket-Protocol: sample  
  7. 8jKS’y:G*Co,Wxa-  

 

原理:

    在请求中的“Sec-WebSocket-Key1”, “Sec-WebSocket-Key2”和最后的“^n:ds[4U”都是随机的,服务器端会用这些数据来构造出一个16字节的应答。

把第一个Key中的数字除以第一个Key的空白字符的数量,而第二个Key也是如此。然后把这两个结果与请求最后的8字节字符串连接起来成为一个字符串,服务器应答正文(“8jKS’y:G*Co,Wxa-”)即这个字符串的MD5 sum。

 

3、第三种是带一个安全key的请求,结果是先以“SHA-1”进行加密,再以base64的加密,结果放在Sec-WebSocket-Accept请求头中返回的方式:

浏览器请求:

[plain] view plaincopy
  1. GET /ls HTTP/1.1  
  2. Upgrade: websocket  
  3. Connection: Upgrade  
  4. Host: www.xx.com  
  5. Sec-WebSocket-Origin: http://www.xx.com  
  6. Sec-WebSocket-Key: 2SCVXUeP9cTjV+0mWB8J6A==  
  7. Sec-WebSocket-Version: 8  


服务器回应:

[plain] view plaincopy
  1. HTTP/1.1 101 Switching Protocols  
  2. Upgrade: websocket  
  3. Connection: Upgrade  
  4. Sec-WebSocket-Accept: mLDKNeBNWz6T9SxU+o0Fy/HgeSw=  

 

原理:

   握手的实现,首先要获取到请求头中的Sec-WebSocket-Key的值,再把这一段GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"加到获取到的Sec-WebSocket-Key的值的后面,然后拿这个 字符串做SHA-1 hash计算,然后再把得到的结果通过base64加密,就得到了返回给客户端的Sec-WebSocket-Accept的http响应头的值。

    还可以参看我前面专门针对这种协议写的一篇文章:http://blog.csdn.net/fenglibing/article/details/6852497

 

二、基于Netty实现JAVA类

为了支持以上提到的三种不同版本的websocket握手实现,服务端就需要针对这三种情况进行相应的处理,以下是一段基于netty实现的java代码,一个完整的WebSocketHelper实现:

  1. import java.io.UnsupportedEncodingException;  
  2. import java.security.MessageDigest;  
  3. import java.security.NoSuchAlgorithmException;  
  4.   
  5. import org.jboss.netty.buffer.ChannelBuffer;  
  6. import org.jboss.netty.buffer.ChannelBuffers;  
  7. import org.jboss.netty.handler.codec.http.DefaultHttpResponse;  
  8. import org.jboss.netty.handler.codec.http.HttpHeaders;  
  9. import org.jboss.netty.handler.codec.http.HttpHeaders.Names;  
  10. import org.jboss.netty.handler.codec.http.HttpRequest;  
  11. import org.jboss.netty.handler.codec.http.HttpResponse;  
  12. import org.jboss.netty.handler.codec.http.HttpResponseStatus;  
  13. import org.jboss.netty.handler.codec.http.HttpVersion;  
  14.   
  15. public class WebSocketHelper {  
  16.   
  17.     private final static String SEC_WEBSOCKET_KEY     = "Sec-WebSocket-Key";  
  18.     private final static String SEC_WEBSOCKET_ACCEPT  = "Sec-WebSocket-Accept";  
  19.     /* websocket版本号:草案8到草案12版本号都是8,草案13及以后的版本号都和草案号相同 */  
  20.     private final static String Sec_WebSocket_Version = "Sec-WebSocket-Version";  
  21.   
  22.     /** 
  23.      * 判断是否是WebSocket请求 
  24.      *  
  25.      * @param req 
  26.      * @return 
  27.      */  
  28.     public boolean supportWebSocket(HttpRequest req) {  
  29.         return (HttpHeaders.Values.UPGRADE.equalsIgnoreCase(req.getHeader(HttpHeaders.Names.CONNECTION)) && HttpHeaders.Values.WEBSOCKET.equalsIgnoreCase(req.getHeader(HttpHeaders.Names.UPGRADE)));  
  30.     }  
  31.   
  32.     /** 
  33.      * 根据WebSocket请求,判断不同的握手形式,并返回相应版本的握手结果 
  34.      *  
  35.      * @param req 
  36.      * @return 
  37.      */  
  38.     public HttpResponse buildWebSocketRes(HttpRequest req) {  
  39.         String reasonPhrase = "";  
  40.         boolean isThirdTypeHandshake = Boolean.FALSE;  
  41.         int websocketVersion = 0;  
  42.         if (req.getHeader(Sec_WebSocket_Version) != null) {  
  43.             websocketVersion = Integer.parseInt(req.getHeader(Sec_WebSocket_Version));  
  44.         }  
  45.         /** 
  46.          * 在草案13以及其以前,请求源使用http头是Origin,是草案4到草案10,请求源使用http头是Sec-WebSocket-Origin,而在草案11及以后使用的请求头又是Origin了, 
  47.          * 不知道这些制定WEBSOCKET标准的家伙在搞什么东东,一个请求头有必要变名字这样变来变去的吗。<br> 
  48.          * 注 意,这里还有一点需要注意的就是"websocketVersion >= 13"这个条件,并不一定适合以后所有的草案,不过这也只是一个预防, 有可能会适应后面的草案, 如果不适合还只有升级对应的websocket协议。<br> 
  49.          */  
  50.         if (websocketVersion >= 13  
  51.             || (req.containsHeader(Names.SEC_WEBSOCKET_ORIGIN) && req.containsHeader(SEC_WEBSOCKET_KEY))) {  
  52.             isThirdTypeHandshake = Boolean.TRUE;  
  53.         }  
  54.   
  55.         // websocket协议草案7后面的格式,可以参看wikipedia上面的说明,比较前后版本的不同:http://en.wikipedia.org/wiki/WebSocket  
  56.         if (isThirdTypeHandshake = Boolean.FALSE) {  
  57.             reasonPhrase = "Switching Protocols";  
  58.         } else {  
  59.             reasonPhrase = "Web Socket Protocol Handshake";  
  60.         }  
  61.         HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, new HttpResponseStatus(101, reasonPhrase));  
  62.         res.addHeader(HttpHeaders.Names.UPGRADE, HttpHeaders.Values.WEBSOCKET);  
  63.         res.addHeader(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.UPGRADE);  
  64.         // Fill in the headers and contents depending on handshake method.  
  65.         if (req.containsHeader(Names.SEC_WEBSOCKET_KEY1) && req.containsHeader(Names.SEC_WEBSOCKET_KEY2)) {  
  66.             // New handshake method with a challenge:  
  67.             res.addHeader(Names.SEC_WEBSOCKET_ORIGIN, req.getHeader(Names.ORIGIN));  
  68.             res.addHeader(Names.SEC_WEBSOCKET_LOCATION, getWebSocketLocation(req));  
  69.             String protocol = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);  
  70.             if (protocol != null) {  
  71.                 res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, protocol);  
  72.             }  
  73.             // Calculate the answer of the challenge.  
  74.             String key1 = req.getHeader(Names.SEC_WEBSOCKET_KEY1);  
  75.             String key2 = req.getHeader(Names.SEC_WEBSOCKET_KEY2);  
  76.             int a = (int) (Long.parseLong(getNumeric(key1)) / getSpace(key1).length());  
  77.             int b = (int) (Long.parseLong(getNumeric(key2)) / getSpace(key2).length());  
  78.             long c = req.getContent().readLong();  
  79.             ChannelBuffer input = ChannelBuffers.buffer(16);  
  80.             input.writeInt(a);  
  81.             input.writeInt(b);  
  82.             input.writeLong(c);  
  83.             ChannelBuffer output = null;  
  84.             try {  
  85.                 output = ChannelBuffers.wrappedBuffer(MessageDigest.getInstance("MD5").digest(input.array()));  
  86.             } catch (NoSuchAlgorithmException e) {  
  87.             }  
  88.   
  89.             res.setContent(output);  
  90.         } else if (isThirdTypeHandshake = Boolean.FALSE) {  
  91.             String protocol = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);  
  92.             if (protocol != null) {  
  93.                 res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, protocol);  
  94.             }  
  95.             res.addHeader(SEC_WEBSOCKET_ACCEPT, getSecWebSocketAccept(req));  
  96.         } else {  
  97.             // Old handshake method with no challenge:  
  98.             if (req.getHeader(Names.ORIGIN) != null) {  
  99.                 res.addHeader(Names.WEBSOCKET_ORIGIN, req.getHeader(Names.ORIGIN));  
  100.             }  
  101.             res.addHeader(Names.WEBSOCKET_LOCATION, getWebSocketLocation(req));  
  102.             String protocol = req.getHeader(Names.WEBSOCKET_PROTOCOL);  
  103.             if (protocol != null) {  
  104.                 res.addHeader(Names.WEBSOCKET_PROTOCOL, protocol);  
  105.             }  
  106.         }  
  107.   
  108.         return res;  
  109.     }  
  110.   
  111.     private String getWebSocketLocation(HttpRequest req) {  
  112.         return "ws://" + req.getHeader(HttpHeaders.Names.HOST) + req.getUri();  
  113.     }  
  114.   
  115.     private String getSecWebSocketAccept(HttpRequest req) {  
  116.         // CHROME WEBSOCKET VERSION 8中定义的GUID,详细文档地址:http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-10  
  117.         String guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";  
  118.         String key = "";  
  119.         key = req.getHeader(SEC_WEBSOCKET_KEY);  
  120.         key += guid;  
  121.         try {  
  122.             MessageDigest md = MessageDigest.getInstance("SHA-1");  
  123.             md.update(key.getBytes("iso-8859-1"), 0, key.length());  
  124.             byte[] sha1Hash = md.digest();  
  125.             key = base64Encode(sha1Hash);  
  126.         } catch (NoSuchAlgorithmException e) {  
  127.         } catch (UnsupportedEncodingException e) {  
  128.         }  
  129.         return key;  
  130.     }  
  131.   
  132.     String base64Encode(byte[] input) {  
  133.         sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();  
  134.         String base64 = encoder.encode(input);  
  135.         return base64;  
  136.     }  
  137.   
  138.     // 去掉传入字符串的所有非数字  
  139.     private String getNumeric(String str) {  
  140.         return str.replaceAll("\\D""");  
  141.     }  
  142.   
  143.     // 返回传入字符串的空格  
  144.     private String getSpace(String str) {  
  145.         return str.replaceAll("\\S""");  
  146.     }  
  147. }  

 

 

三、注意事项

不同版本的WebSocket标准,编码和解码的方式还有所不同,在第一种和第二种WebSocket协议标准中,使用Netty自带的Encoder和Decoder即可:

org.jboss.netty.handler.codec.http.websocket.WebSocketFrameEncoder
org.jboss.netty.handler.codec.http.websocket.WebSocketFrameDecoder

而如果要支持第三种实现标准,Netty目前官方还不支持,可以到github中找到实现的Encoder及Decoder:

https://github.com/joewalnes/webbit/tree/0356ba12f5c21f8a297a5afb433215bb2f738008/src/main/java/org/webbitserver/netty

不过,它的实现有一点问题,就是没有处理客户端主动发起的WebSocket请求断开,既客户端主动发起opcode为8的请求,不过它还是有预留的,找到这个类:

Hybi10WebSocketFrameDecoder

的包含这以下内容的行:

} else if (this.opcode == OPCODE_CLOSE) {

在其中插入:

return new DefaultWebSocketFrame(0x08, frame);

然后在你的实现子类中增加如下的代码判断即可:

  1. if (frame.getType() == 0x08) {  
  2.         //处理关闭事件的XXX方法  
  3.         return;  

分享到:
评论

相关推荐

    基于Netty的Java WebSocket集群框架。.zip

    在WebSocket服务器端,Netty提供了WebSocketServerProtocolHandler来处理WebSocket握手和消息传输。 2. **WebSocket协议**:WebSocket协议是HTTP/1.1协议的一个扩展,通过"Upgrade"头字段进行升级,建立起一个持久...

    netty实现websocket例子

    客户端通常会通过WebSocketClientHandshaker类来发起WebSocket握手,然后通过WebSocketFrameDecoder和WebSocketFrameEncoder来解码接收到的帧和编码要发送的帧。 在"描述"中提到的示例,客户端可以向服务器发送一条...

    基于netty的websocket开发小结

    Netty是一个高性能、异步事件驱动的网络应用程序框架,广泛用于构建高性能的服务器和客户端应用,包括WebSocket服务。这篇基于Netty的WebSocket开发小结将探讨如何使用Netty实现WebSocket服务端和客户端的交互。 ...

    Netty实现Java服务端和C#客户端联通

    标题中的“Netty实现Java服务端和C#客户端联通”是指使用Netty作为Java服务器框架,与C#客户端(使用DotNetty库)进行通信的一种技术实现。这涉及到跨平台的网络通信,以及两个不同编程语言间的交互。 Netty是Java...

    WebSocket利用netty连接入门项目

    这个入门项目是学习WebSocket与Netty结合的一个好起点,通过实际操作,你可以更深入地理解WebSocket协议的工作原理,以及如何使用Netty构建高效稳定的WebSocket服务器。同时,对于前端开发人员,这也是一个了解...

    netty+websocket实现心跳和断线重连

    接着,定义一个自定义的 `ChannelInboundHandlerAdapter`,用于处理 WebSocket 握手和消息传递。 2. **心跳检测** - **服务端实现**:在服务端,我们可以创建一个定时任务,周期性地检查 `Map` 集合中的 `...

    netty3实现的websocket服务

    在这个“netty3实现的websocket服务”项目中,开发者利用Netty 3版本构建了一个WebSocket服务器和一个简单的Web服务器,旨在帮助学习者深入理解Netty的工作原理以及如何基于Netty构建实际项目。 WebSocket协议是一...

    netty-websocket-example 基于netty的websocket实现示例

    总的来说,“netty-websocket-example”项目是一个很好的学习资源,它展示了如何利用 Netty 的强大功能来实现 WebSocket 通信,并通过 JMX 进行性能测试。对于希望深入理解 Netty 和 WebSocket 应用的开发者来说,这...

    netty5实现的websocket服务器

    在本项目中,“netty5实现的websocket服务器”利用了Netty 5版本的特性来构建WebSocket服务端,旨在为Android和iOS的APP提供长连接功能,从而增强应用的实时响应能力。 WebSocket 协议是一种在TCP基础上建立持久...

    基于netty的websocket

    4. **WebSocket握手**: WebSocket连接的建立需要经过一个HTTP升级请求的握手过程。在Netty中,这个过程由`WebSocketServerHandshaker`类处理。服务器收到客户端的Upgrade请求后,会返回一个101 Switching Protocols...

    springboot基础netty和websocket

    在IT行业中,SpringBoot、Netty和WebSocket是三个非常重要的技术组件,它们分别在不同的领域发挥着关键作用。本文将详细解析如何利用SpringBoot、Netty以及WebSocket来构建实时通信系统,并探讨它们各自的核心概念。...

    websocket聊天 netty聊天

    在WebSocket聊天应用中,Netty可以用来处理WebSocket握手过程、数据帧的解码与编码,以及网络I/O操作,确保数据的高效传输。 实现WebSocket聊天业务通常包括以下几个步骤: 1. **服务器端配置**:创建一个Netty...

    Netty通过WebSocket编程实现服务器和客户端长连接.zip

    在本示例"Netty通过WebSocket编程实现服务器和客户端长连接"中,我们将深入探讨如何利用Netty这一高性能的Java网络应用框架来构建WebSocket服务。 Netty是一个异步事件驱动的网络应用框架,适用于开发高并发、高...

    Netty WebSocket服务端

    在实际项目中,我们还可以使用Netty的WebSocketServerHandshakerFactory来定制WebSocket握手的参数,如子协议、允许的最大帧大小等。此外,可以结合Spring Boot或类似的框架,将WebSocket服务集成到Web应用中,方便...

    Netty整合webSocket

    在Java领域,Netty提供了一种高效的方式来整合WebSocket,使得开发者能够构建实时的、低延迟的Web应用。 一、Netty概述 Netty是基于NIO(非阻塞I/O)的框架,它简化了网络编程,提供了包括TCP、UDP以及HTTP等多种...

    简易版netty websocket通讯demo 聊天

    在本示例中,"简易版netty websocket通讯demo 聊天" 提供了一个基础的 WebSocket 协议通信的实现,用于构建聊天应用。WebSocket 是一种在客户端和服务器之间建立持久连接的协议,它允许双方进行全双工通信,即数据...

    netty5_兼容tcp、websocket小例子 实现信息广播

    在这个“netty5_兼容tcp、websocket小例子”中,我们将探讨如何利用Netty 5版本实现TCP和WebSocket的兼容,并实现信息的广播功能。TCP是一种面向连接的、可靠的传输层协议,而WebSocket则是一种在Web上提供全双工...

    基于Netty5.0的高级案例NettyWebsocket

    这个文件名可能代表的是一个实现了WebSocket服务器的Java类,可能包含了初始化Netty服务器、配置WebSocket处理逻辑以及启动服务器的相关代码。它可能是整个案例的核心部分。 **总结:** Netty与WebSocket的结合为...

    netty websocket源码

    WebSocket是Web交互的一种高效协议,它允许服务器与客户端进行全双工通信,即双方可以同时发送数据,极大地提升了...不过,这个基础已经足够让你理解Netty和WebSocket的结合,并能动手实现一个简单的WebSocket服务。

    netty学习文件,实现http,websocket,protobuf

    在本文中,我们将深入探讨Netty如何实现HTTP、WebSocket和Protobuf这三种通信方式,以及它们在服务器与客户端通信中的应用。 ### Netty与HTTP HTTP(超文本传输协议)是互联网上应用最广泛的一种网络协议,用于从...

Global site tag (gtag.js) - Google Analytics