概述
WebSocket 是在单个 TCP 连接上进行全双工通讯的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,客户端和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
WebSocket 是真正意义上的全双工模式,也就是我们俗称的「长连接」。
WebSocket 是基于TCP/IP协议,独立于 HTTP 协议的通信协议。是应用层的通信协议。
协议标识符变成了 “ws” 或者 “wss”,分别表示明文和加密的 WebSocket 协议。
OkHttp 实现了对 WebSocket 的支持,本文简单介绍一下 OkHttp 实现 WebSocket 的基本原理。
基础知识
WebSocket 和 http 协议的 keep-alive
keep-alive 是 http 协议规定的一种连接模式,在非KeepAlive模式时,每个请求/应答客户和服务器都要新建一个连接,完成之后立即断开连接(HTTP协议为无连接的协议);当使用Keep-Alive模式(又称持久连接、连接重用)时,Keep-Alive功能使客户端到服务器端的连接持续有效,当出现对服务器的后继请求时,Keep-Alive功能避免了建立或者重新建立连接。
http 1.0中默认是关闭的,需要在http头加入”Connection:Keep-Alive”,才能启用Keep-Alive;http 1.1中默认启用Keep-Alive,如果加入”Connection:close”,才关闭。目前大部分浏览器都是用http1.1协议,也就是说默认都会发起Keep-Alive的连接请求了,所以是否能完成一个完整的Keep- Alive连接就看服务器设置情况。
http keep-alive只是一种为了达到复用tcp连接的“协商”行为,双方并没有建立正真的连接会话,服务端也可以不认可,也可以随时(在任何一次请求完成后)关闭掉。它是指在一次 TCP 连接中完成多个 http请求,但是对每个请求仍然要单独发header,所以除了真正的数据部分外,服务器和客户端还要大量交换httpheader,信息交换效率很低,这样建立的“长连接”都是伪长连接。
websocket 不同,它本身就规定了是正真的、双工的长连接,两边都必须要维持住连接的状态。
断线重连机制
造成WebSocket断线原因:
- 网络状态不好(网络断开、网络信号差)。
- 数据受各种阻塞(路由器、防火墙、代理服务器)。
- Web服务端故障。
解决 websocket 断线方法:心跳重连。
- 通过服务端实现 Pings / Pongs 方式实现心跳。
即通过服务端向浏览器(客户端)发送ping 0x9 消息,浏览器会自动返回pong 0xA消息。(基于session技术)
在经过握手之后的任意时刻里,无论客户端还是服务端都可以选择发送一个ping给另一方。 当ping消息收到的时候,接受的一方必须尽快回复一个pong消息。可以使用这种方式来确保客户端的连接状态。
一个ping或者pong都只是一个常规的帧,只是这个帧是一个控制帧。ping消息的opcode字段值为0x9,pong消息的opcode值为0xA。当你得到一个ping消息时,回复一个跟ping消息有相同载荷数据的pong消息。 - 通过浏览器(客户端)自带的心跳机制。
即监听网络,浏览器(客户端)定时发送消息到服务端。
PS:这里消息指的websockect协议的数据帧,不需要通过代码实现。不同浏览器发送的消息时间间隔有所差异。 - 通过代码在浏览器(客户端)实现心跳机制。
注意:心跳重连只能由服务端实现,不建议由客户端实现。
使用方法
借助 https://www.websocket.org/,该网站提供了一个测试接口wss://echo.websocket.org。
1 | private void websocket() { |
建立成功连接后会回调 onOpen
,后面我们用 mWebsocket.send
向服务器发送一条消息,www.websocket.org 服务器马上会通过 onMessage
返回一条消息给我们。
而且每隔 30 秒,www.websocket.org 服务器会向客户端发送一条 ping 消息来确保连接的状态。
另外,我们在创建 OkHttpClient 对象时,可以调用 pingInterval(long interval, TimeUnit unit)
方法来配置客户端向服务端发送心跳包,可以定时向服务器发送 ping 消息。
实现原理
我们就从 OkHttpClient.newWebSocket
方法开始分析。
1 | @Override public WebSocket newWebSocket(Request request, WebSocketListener listener) { |
RealWebSocket
是 WebSocket
的实现类:
1 | public RealWebSocket(Request request, WebSocketListener listener, Random random, |
通过调用 connect 方法,最终建立与服务器的连接:
1 | public void connect(OkHttpClient client) { |
1 | public void loopReader() throws IOException { |
1 | private void readHeader() throws IOException { |
处理服务器的消息:
1 | private void readControlFrame() throws IOException { |