这个话题应该是面试中出现频率比较高的吧....无论咋样仍是有必要深刻了解下二者之间的关联。废话很少说,直接入题吧:java
网站上的即时通信是很常见的,好比网页的QQ,聊天系统等。按照以往的技术能力一般是采用轮询、Comet技术解决。web
HTTP协议是非持久化的,单向的网络协议,在创建链接后只容许浏览器向服务器发出请求后,服务器才能返回相应的数据。当须要即时通信时,经过轮询在特定的时间间隔(如1秒),由浏览器向服务器发送Request请求,而后将最新的数据返回给浏览器。这样的方法最明显的缺点就是须要不断的发送请求,并且一般HTTP request的Header是很是长的,为了传输一个很小的数据 须要付出巨大的代价,是很不合算的,占用了不少的宽带。面试
缺点:会致使过多没必要要的请求,浪费流量和服务器资源,每一次请求、应答,都浪费了必定流量在相同的头部信息上设计模式
然而WebSocket的出现能够弥补这一缺点。在WebSocket中,只须要服务器和浏览器经过HTTP协议进行一个握手的动做,而后单独创建一条TCP的通讯通道进行数据的传送。浏览器
WebSocket同HTTP同样也是应用层的协议,可是它是一种双向通讯协议,是创建在TCP之上的。安全
WebSocket在创建握手时,数据是经过HTTP传输的。可是创建以后,在真正传输时候是不须要HTTP协议的。服务器
Socket其实并非一个协议,而是为了方便使用TCP或UDP而抽象出来的一层,是位于应用层和传输控制层之间的一组接口。websocket
Socket是应用层与TCP/IP协议族通讯的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来讲,一组简单的接口就是所有,让Socket去组织数据,以符合指定的协议。网络
当两台主机通讯时,必须经过Socket链接,Socket则利用TCP/IP协议创建TCP链接。TCP链接则更依靠于底层的IP协议,IP协议的链接则依赖于链路层等更低层次。session
WebSocket则是一个典型的应用层协议。
Socket是传输控制层协议,WebSocket是应用层协议。
WebSocket API 是 HTML5 标准的一部分, 但这并不表明 WebSocket 必定要用在 HTML 中,或者只能在基于浏览器的应用程序中使用。
实际上,许多语言、框架和服务器都提供了 WebSocket 支持,例如:
如下简要介绍一下 WebSocket 的原理及运行机制。
WebSocket 是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通讯,能更好的节省服务器资源和带宽并达到实时通信,它创建在 TCP 之上,同 HTTP 同样经过 TCP 来传输数据,可是它和 HTTP 最大不一样是:
非 WebSocket 模式传统 HTTP 客户端与服务器的交互以下图所示:
使用 WebSocket 模式客户端与服务器的交互以下图:
上图对比能够看出,相对于传统 HTTP 每次请求-应答都须要客户端与服务端创建链接的模式,WebSocket 是相似 Socket 的 TCP 长链接的通信模式,一旦 WebSocket 链接创建后,后续数据都以帧序列的形式传输。在客户端断开 WebSocket 链接或 Server 端断掉链接前,不须要客户端和服务端从新发起链接请求。在海量并发及客户端与服务器交互负载流量大的状况下,极大的节省了网络带宽资源的消耗,有明显的性能优点,且客户端发送和接受消息是在同一个持久链接上发起,实时性优点明显。
咱们再经过客户端和服务端交互的报文看一下 WebSocket 通信与传统 HTTP 的不一样:
在客户端,new WebSocket 实例化一个新的 WebSocket 客户端对象,链接相似 ws://yourdomain:port/path 的服务端 WebSocket URL,WebSocket 客户端对象会自动解析并识别为 WebSocket 请求,从而链接服务端端口,执行双方握手过程,客户端发送数据格式相似:
GET /webfin/websocket/ HTTP/1.1 Host: localhost Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: xqBt3ImNzJbYqRINxEFlkg== Origin: http://localhost:8080 Sec-WebSocket-Version: 13
能够看到,客户端发起的 WebSocket 链接报文相似传统 HTTP 报文,”Upgrade:websocket”参数值代表这是 WebSocket 类型请求,“Sec-WebSocket-Key”是 WebSocket 客户端发送的一个 base64 编码的密文,要求服务端必须返回一个对应加密的“Sec-WebSocket-Accept”应答,不然客户端会抛出“Error during WebSocket handshake”错误,并关闭链接。
服务端收到报文后返回的数据格式相似:
HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: K7DJLdLooIwIG/MOpvWFB3y3FE8=
“Sec-WebSocket-Accept”的值是服务端采用与客户端一致的密钥计算出来后返回客户端的,“HTTP/1.1 101 Switching Protocols”表示服务端接受 WebSocket 协议的客户端链接,通过这样的请求-响应处理后,客户端服务端的 WebSocket 链接握手成功, 后续就能够进行 TCP 通信了。
在开发方面,WebSocket API 也十分简单,咱们只须要实例化 WebSocket,建立链接,而后服务端和客户端就能够相互发送和响应消息,在下文 WebSocket 实现及案例分析部分,能够看到详细的 WebSocket API 及代码实现。
如上文所述,WebSocket 的实现分为客户端和服务端两部分,客户端(一般为浏览器)发出 WebSocket 链接请求,服务端响应,实现相似 TCP 握手的动做,从而在浏览器客户端和 WebSocket 服务端之间造成一条 HTTP 长链接快速通道。二者之间后续进行直接的数据互相传送,再也不须要发起链接和相应。
如下简要描述 WebSocket 服务端 API 及客户端 API。
WebSocket 服务端在各个主流应用服务器厂商中已基本得到符合 JEE JSR356 标准规范 API 的支持,如下列举了部分常见的商用及开源应用服务器对 WebSocket Server 端的支持状况:
厂商 | 应用服务器 | 备注 |
---|---|---|
IBM | WebSphere | WebSphere 8.0 以上版本支持,7.X 以前版本结合 MQTT 支持相似的 HTTP 长链接 |
甲骨文 | WebLogic | WebLogic 12c 支持,11g 及 10g 版本经过 HTTP Publish 支持相似的 HTTP 长链接 |
微软 | IIS | IIS 7.0+支持 |
Apache | Tomcat | Tomcat 7.0.5+支持,7.0.2X 及 7.0.3X 经过自定义 API 支持 |
Jetty | Jetty 7.0+支持 |
如下咱们使用 Tomcat7.0.5 版本的服务端示例代码说明 WebSocket 服务端的实现:
JSR356 的 WebSocket 规范使用 javax.websocket.*的 API,能够将一个普通 Java 对象(POJO)使用 @ServerEndpoint 注释做为 WebSocket 服务器的端点,代码示例以下:
@ServerEndpoint("/echo") public class EchoEndpoint { @OnOpen public void onOpen(Session session) throws IOException { //如下代码省略... } @OnMessage public String onMessage(String message) { //如下代码省略... } @Message(maxMessageSize=6) public void receiveMessage(String s) { //如下代码省略... } @OnError public void onError(Throwable t) { //如下代码省略... } @OnClose public void onClose(Session session, CloseReason reason) { //如下代码省略... } }
代码解释:
上文的简洁代码即创建了一个 WebSocket 的服务端,@ServerEndpoint("/echo") 的 annotation 注释端点表示将 WebSocket 服务端运行在 ws://[Server 端 IP 或域名]:[Server 端口]/websockets/echo 的访问端点,客户端浏览器已经能够对 WebSocket 客户端 API 发起 HTTP 长链接了。
使用 ServerEndpoint 注释的类必须有一个公共的无参数构造函数,@onMessage 注解的 Java 方法用于接收传入的 WebSocket 信息,这个信息能够是文本格式,也能够是二进制格式。
OnOpen 在这个端点一个新的链接创建时被调用。参数提供了链接的另外一端的更多细节。Session 代表两个 WebSocket 端点对话链接的另外一端,能够理解为相似 HTTPSession 的概念。
OnClose 在链接被终止时调用。参数 closeReason 可封装更多细节,如为何一个 WebSocket 链接关闭。
更高级的定制如 @Message 注释,MaxMessageSize 属性能够被用来定义消息字节最大限制,在示例程序中,若是超过 6 个字节的信息被接收,就报告错误和链接关闭。
注意:早期不一样应用服务器支持的 WebSocket 方式不尽相同,即便同一厂商,不一样版本也有细微差异,如 Tomcat 服务器 7.0.5 以上的版本都是标准 JSR356 规范实现,而 7.0.2x/7.0.3X 的版本使用自定义 API (WebSocketServlet 和 StreamInbound, 前者是一个容器,用来初始化 WebSocket 环境;后者是用来具体处理 WebSocket 请求和响应,详见案例分析部分),且 Tomcat7.0.3x 与 7.0.2x 的 createWebSocketInbound 方法的定义不一样,增长了一个 HttpServletRequest 参数,使得能够从 request 参数中获取更多 WebSocket 客户端的信息,以下代码所示:
public class EchoServlet extends WebSocketServlet { @Override protected StreamInbound createWebSocketInbound(String subProtocol, HttpServletRequest request) { //如下代码省略.... return new MessageInbound() { //如下代码省略.... } protected void onBinaryMessage(ByteBuffer buffer) throws IOException { //如下代码省略... } protected void onTextMessage(CharBuffer buffer) throws IOException { getWsOutbound().writeTextMessage(buffer); //如下代码省略... } }; } }
所以选择 WebSocket 的 Server 端重点须要选择其版本,一般状况下,更新的版本对 WebSocket 的支持是标准 JSR 规范 API,但也要考虑开发易用性及老版本程序移植性等方面的问题,以下文所述的客户案例,就是由于客户要求统一应用服务器版本因此使用的 Tomcat 7.0.3X 版本的 WebSocketServlet 实现,而不是 JSR356 的 @ServerEndpoint 注释端点。
对于 WebSocket 客户端,主流的浏览器(包括 PC 和移动终端)现已都支持标准的 HTML5 的 WebSocket API,这意味着客户端的 WebSocket JavaScirpt 脚本具有良好的一致性和跨平台特性,如下列举了常见的浏览器厂商对 WebSocket 的支持状况:
浏览器 | 支持状况 |
---|---|
Chrome | Chrome version 4+支持 |
Firefox | Firefox version 5+支持 |
IE | IE version 10+支持 |
Safari | IOS 5+支持 |
Android Brower | Android 4.5+支持 |
客户端 WebSocket API 基本上已经在各个主流浏览器厂商中实现了统一,所以使用标准 HTML5 定义的 WebSocket 客户端的 JavaScript API 便可,固然也可使用业界知足 WebSocket 标准规范的开源框架,如 Socket.io。
如下以一段代码示例说明 WebSocket 的客户端实现:
var ws = new WebSocket(“ws://echo.websocket.org”); ws.onopen = function(){ws.send(“Test!”); }; ws.onmessage = function(evt){console.log(evt.data);ws.close();}; ws.onclose = function(evt){console.log(“WebSocketClosed!”);}; ws.onerror = function(evt){console.log(“WebSocketError!”);};
第一行代码是在申请一个 WebSocket 对象,参数是须要链接的服务器端的地址,同 HTTP 协议开头同样,WebSocket 协议的 URL 使用 ws://开头,另外安全的 WebSocket 协议使用 wss://开头。
第二行到第五行为 WebSocket 对象注册消息的处理函数,WebSocket 对象一共支持四个消息 onopen, onmessage, onclose 和 onerror,有了这 4 个事件,咱们就能够很容易很轻松的驾驭 WebSocket。
当 Browser 和 WebSocketServer 链接成功后,会触发 onopen 消息;若是链接失败,发送、接收数据失败或者处理数据出现错误,browser 会触发 onerror 消息;当 Browser 接收到 WebSocketServer 发送过来的数据时,就会触发 onmessage 消息,参数 evt 中包含 Server 传输过来的数据;当 Browser 接收到 WebSocketServer 端发送的关闭链接请求时,就会触发 onclose 消息。咱们能够看出全部的操做都是采用异步回调的方式触发,这样不会阻塞 UI,能够得到更快的响应时间,更好的用户体验。