简单介绍
WebSocket 协议在2008年诞生,2011年成为国际标准。现在所有浏览器都已经支持了。WebSocket 的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,双工模式。
WebSocket是HTML5出(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算)HTTP 有 1.1 和 1.0 之说,也就是所谓的 keep-alive ,把多个 HTTP 请求合并为一个,但是 Websocket 其实是一个新协议,跟 HTTP 协议基本没有关系,只是为了兼容现有浏览器,所以在握手阶段使用了 HTTP 。
Websocket是一个持久化的协议,相对于HTTP这种非持久的协议来说。
WebSocket 建立在 TCP 协议之上,服务器端的实现比较容易。与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。
数据格式比较轻量,性能开销小,通信高效。可以发送文本,也可以发送二进制数据。没有同源限制,客户端可以与任意服务器通信。协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
原理
首先 WebSocket 是基于 HTTP 协议的,或者说借用了 HTTP 协议来完成一部分握手。
1 | GET /chat HTTP/1.1 |
这段类似 HTTP 协议的握手请求中,多了这么几个东西。
1 | Upgrade: websocket |
这就是 WebSocket 的核心了,告诉 Apache 、 Nginx 等服务器,发起的请求要用 WebSocket 协议,找到对应处理而不是 HTTP。
1 | Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw== |
首先, Sec-WebSocket-Key 是一个 Base64 encode 的值,这个是浏览器随机生成的,告诉服务器来验证是否是WebSocket服务。
然后, Sec_WebSocket-Protocol 是一个用户定义的字符串,用来区分同 URL 下,不同的服务所需要的协议。
最后, Sec-WebSocket-Version 是告诉服务器所使用的 WebSocket Draft (协议版本),在最初的时候,WebSocket 协议还在 Draft 阶段,各种不同的协议都有,Firefox 和 Chrome 用的不是一个版本之类的,当初 WebSocket 协议太多可是一个大难题。
然后服务器会返回下列东西,表示已经接受到请求, 成功建立 WebSocket:
1 | HTTP/1.1 101 Switching Protocols |
这里开始就是 HTTP 最后负责的部分。告诉了客户端,成功转换协议WebSocket协议。
1 | Upgrade: websocket |
告诉客户端即将升级的是 WebSocket 协议。 Sec-WebSocket-Accept 这个则是经过服务器确认,并且加密过后的 Sec-WebSocket-Key , Sec-WebSocket-Protocol 则是表示最终使用的协议。
实现
在没有WebSocket之前,我们是怎么处理长连接的情况,是使用ajax轮询 和 long poll。大多数 Web 应用程序将通过频繁的异步 JavaScript 和 XML(AJAX)请求实现长轮询。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。
- ajax轮询
ajax轮询的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。 - poll
long poll 其实原理跟 ajax轮询 差不多,都是采用轮询的方式,不过采取的是阻塞模型,客户端发起请求后,如果没消息,就一直不返回 Response 给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。
这两种方式,都是在不断地建立HTTP连接,然后等待服务端处理,可以体现HTTP协议的另外一个特点,被动性:服务端不能主动联系客户端,只能有客户端发起。
ajax轮询 需要服务器有很快的处理速度和资源。long poll 需要有很高的并发。
- WebSocket 如何工作?
Web 浏览器和服务器都必须实现 WebSockets 协议来建立和维护连接。由于 WebSockets 连接长期存在,与典型的 HTTP 连接不同,对服务器有重要的影响。
基于多线程或多进程的服务器无法适用于 WebSockets,因为它旨在打开连接,尽可能快地处理请求,然后关闭连接。任何实际的 WebSockets 服务器端实现都需要一个异步服务器。
WebSocket 客户端
在客户端,没有必要为 WebSockets 使用 JavaScript 库。实现 WebSockets 的 Web 浏览器将通过 WebSockets 对象实现客户端功能(Html5已经实现了WebSocket)。
API
以下 API 用于创建 WebSocket 对象。
1 | //第一个参数 url, 指定连接的 URL。第二个参数 protocol 是可选的,指定了可接受的子协议。 |
WebSocket 属性
属性 | 描述
——- | ——-
Socket.readyState | 只读属性 readyState 表示连接状态,CONNECTING:值为0,表示正在连接。OPEN:值为1,表示连接成功,可以通信了。CLOSING:值为2,表示连接正在关闭。CLOSED:值为3,表示连接已经关闭,或者打开连接失败。
Socket.bufferedAmount | 只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。
WebSocket 事件
事件 | 事件处理程序 | 描述
——- | ——- | ——-
open | Socket.onopen | 连接建立时触发
message | Socket.onmessage | 客户端接收服务端数据时触发
error | Socket.onerror | 通信发生错误时触发
close | Socket.onclose | 连接关闭时触发
WebSocket 方法
方法 | 描述
—|—
Socket.send() | 使用连接发送数据
Socket.close() | 关闭连接
WebSocket 服务端
WebSocket 在服务端的实现非常丰富。Node.js、Java、C++、Python 等多种语言都有自己的解决方案。
语言框架 | 实现
——- | ——-
Node.js | 3种实现:µWebSockets、Socket.IO、WebSocket-Node
Java | javaweb通常依托servlet 容器:Tomcat7、Jetty7 及以上版本均开始支持 WebSocket。Spring 框架对 WebSocket 也提供了支持。
它们都遵循RFC6455 的通信标准,并且 Java API 统一遵循 JSR 356 - JavaTM API for WebSocket 规范。所以,在实际编码中,API 差异不大。
示例
新建一个maven项目引入包:
1 | <!-- javaweb实现的websocket --> |
页面文件js:
1 | <script type="text/javascript"> |
Java web服务端:
1 | /** |
idea中部测试:
创建webapp目录和web.xml
创建artifacts
配置服务器:
选择Deployment
运行:
1 | 有新连接加入! |
把它改成一个聊天室的模式
用户可以看到加入的其他用户发的消息,后台代码如下:
1 | "/demo1", encoders = {ServerEncoder.class}) (value = |
由于在运行时报错:javax.websocket.EncodeException: No encoder specified for object of class
因为,使用client.session.getBasicRemote().sendObject(obj);发送对象数据。解决办法:
新增一个类,并且在主类注解中加入代码:encoders = {ServerEncoder.class}
1 | public class ServerEncoder implements Encoder.Text<WebSocketServiceDemo.Message> { |
页面:
1 | <html> |
测试成功:
多用户多聊天室
1 | "/demo/{roomId}/{userId}", encoders = {ServerEncoder.class}) (value = |
index:
1 | <%@ page language="java" pageEncoding="UTF-8" %> |
测试完成。
简单的websocket就完成。后面介绍websocket spring或springboot实现。