根据 WebSocket 的协议,服务器端用如下响应,来表示接受建立 WebSocket 连接的请求:
- 响应码是 101;
- “Connection” header 的值为 “Upgrade”,以表明服务器并没有在处理完请求之后把连接断开;
- “Upgrade” header 的值为 “websocket”,以表明服务器接受后面使用 WebSocket 来通信;
- “Sec-WebSocket-Accept” header 的值为,key + WebSocketProtocol.ACCEPT_MAGIC 做 SHA1 hash,然后做 base64 编码,来做服务器接受连接的验证。关于这部分的设计的详细信息,可参考 《WebSocket 协议规范》。
为数据收发做准备的第二步是,初始化用于输入输出的 Source 和 Sink。Source 和 Sink 创建于之前发送 HTTP 请求的时候。这里会阻止在这个连接上再创建新的流。
Streams 是一个 BufferedSource 和 BufferedSink 的 holder:
第三步是调用回调 onOpen()
。
第四步是初始化 Reader 和 Writer:
OkHttp 使用 WebSocketReader
和 WebSocketWriter
来处理数据的收发。在发送数据时将数据组织成帧,在接收数据时则进行反向擦除,同时处理 WebSocket 的控制消息。
WebSocket 的所有数据发送动作,都会在单线程线程池的线程中,通过 WebSocketWriter 执行。在这里会创建 ScheduledThreadPoolExecutor 用于跑数据的发送操作。
WebSocket 协议中主要会传输两种类型的帧:
- 控制帧,主要是用于连接保活的 Ping 帧等;
- 数据载荷帧。在这里会根据用户的配置,调度 Ping 帧周期性地发送。我们在调用 WebSocket 的接口发送数据时,数据并不是同步发送的,而是被放在了一个消息队列中。发送消息的 Runnable 从消息队列中读取数据发送。其中会检查消息队列中是否有数据,如果有的话,会调度发送消息的 Runnable 执行。
第五步是配置 socket 的超时时间为 0,也就是阻塞 IO。
第六步执行 loopReader()
。这实际上是进入了消息读取循环了,也就是数据接收的逻辑了。
2.2 数据发送
我们可以通过 WebSocket 接口的 send(String text)
和 send(ByteString bytes)
分别发送文本的和二进制格式的消息。
可以看到我们调用发送数据的接口时,做的事情主要是将数