我是靠谱客的博主 含蓄睫毛,这篇文章主要介绍基于OKHttp的websocket封装使用1.背景2.源码解析3.相关知识点,现在分享给大家,希望可以做个参考。

文章目录

  • 1.背景
  • 2.源码解析
    • 2.1基础封装
    • 2.2使用
  • 3.相关知识点
    • 3.1 websocket协议
      • 3.1.1、客户端:申请协议升级
      • 3.1.2、服务端:响应协议升级
      • 3.1.3、数据帧
    • 3.2 返回数据

Demo源码请点击(网络库中websocket部分)

1.背景

一般使用到websocket协议的应用场景都是持续保持长连接,直到业务处理完毕,不再需要保持连接时,则close掉连接。那么官方给出的指导使用文档足咦。我近期工作上接到的任务是,通过websocket协议,流式接收数据。一次任务结束后要求关掉连接,下次任务再重新建立连接。要求减少服务器并发连接数,忽略建立连接的资源消耗。所以本篇文章是介绍再实例化连接的client之后,不关闭分发执行service的前提下,每次都新建websocket连接。整个源码放在了我封装的网络请求框架里。

2.源码解析

2.1基础封装

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
public class WebSocketClient { private Request request; private OkHttpClient client; private WebSocket webSocket; public WebSocketClient() { client = new OkHttpClient(); request = new Request.Builder() .url("ws://echo.websocket.org") .build(); } public OkHttpClient getClient() { return client; } public void start(WebSocketListener listener) { client.dispatcher().cancelAll(); HLogger.d("request id = " + request.toString()); HLogger.d("listener id = " + listener.toString()); webSocket = client.newWebSocket(request, listener); HLogger.d("webSocket id = " + webSocket.toString()); } public void close() { if (webSocket != null) { webSocket.close(1000, null); } client.dispatcher().executorService().shutdown(); } }

以上是对Request、OkHttpClient、WebSocket的封装。基本保证只实例化一份Request、OkHttpClient,每次新任务newWebSocket。回调listener可以复用。

2.2使用

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
HsjWebSocketListener listener = new HsjWebSocketListener(); StringBuilder stringBuilder = new StringBuilder(); public void initSocket(View view) { webSocketClient = new WebSocketClient(); } public void webSocket(View view) { if (webSocketClient == null) { Toast.makeText(this, "请初始化Socket", Toast.LENGTH_SHORT).show(); return; } stringBuilder.setLength(0); stringBuilder.append(System.currentTimeMillis() + "-onClickn"); output(); webSocketClient.start(listener); } public void closeSocket(View view) { webSocketClient.close(); } private class HsjWebSocketListener extends WebSocketListener { @Override public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) { stringBuilder.append(System.currentTimeMillis() + "n"); webSocket.send("hello world"); webSocket.send("welcome"); webSocket.send(ByteString.decodeHex("adef")); webSocket.close(1000, "再见"); } @Override public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) { stringBuilder.append(System.currentTimeMillis() + "-onMessage: " + text + "n"); output(); } @Override public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString bytes) { stringBuilder.append(System.currentTimeMillis() + "-onMessage byteString: " + bytes + "n"); output(); } @Override public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) { HLogger.d("onFailure: " + t.getMessage()); stringBuilder.append(System.currentTimeMillis() + "-onFailure: " + t.getMessage() + "n"); output(); } @Override public void onClosing(@NotNull WebSocket webSocket, int code, @NotNull String reason) { stringBuilder.append(System.currentTimeMillis() + "-onClosing: " + code + "/" + reason + "n"); output(); } @Override public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) { stringBuilder.append(System.currentTimeMillis() + "-onClosed: " + code + "/" + reason + "n"); output(); } } private void output() { runOnUiThread(new Runnable() { @Override public void run() { textView.setText(stringBuilder.toString()); } }); }
  1. 先实现一个WebSocketListener,并声明实例化它listener。这是准备工作。
  2. 实例化WebSocketClient。
  3. 任务开始调用webSocketClient.start(listener);
  4. 若再一个新任务,再次调用webSocketClient.start(listener);
  5. 若所有任务结束,页面关闭等情况。调用webSocketClient.close();关闭client。
  6. 单次任务,一般会在onMessage方法中返回是否是最后一条数据信息的标记,若是本次连接的最后一条信息返回。可以主动webSocket.close(1000, “再见”);这个方法只是关闭掉本次连接的websocket。

3.相关知识点

3.1 websocket协议

websocket协议是基于http协议的升级。也就是说客户端先与服务器端通过http协议几次握手建立连接。连接建立后okhttp中封装的websocket框架中,WebSocketListener的onOpen会被回调,即此时已经建立好长连接,可以进行具体业务通信了。

3.1.1、客户端:申请协议升级

建立连接时的通信协议采用的是标准的HTTP报文格式,且只支持GET方法。

复制代码
1
2
3
4
5
6
7
8
GET / HTTP/1.1 Host: localhost:8080 Origin: http://127.0.0.1:3000 Connection: Upgrade Upgrade: websocket Sec-WebSocket-Version: 13 Sec-WebSocket-Key: w4v7O6xFTi36lq3RNcgctw==

重点请求首部意义如下:

  • Connection: Upgrade:表示要升级协议
  • Upgrade: websocket:表示要升级到websocket协议。
  • Sec-WebSocket-Version: 13:表示websocket的版本。如果服务端不支持该版本,需要返回一个Sec-WebSocket-Versionheader,里面包含服务端支持的版本号。
  • Sec-WebSocket-Key:与后面服务端响应首部的Sec-WebSocket-Accept是配套的,提供基本的防护,比如恶意的连接,或者无意的连接。

注意,上面请求省略了部分非重点请求首部。由于是标准的HTTP请求,类似Host、Origin、Cookie等请求首部会照常发送。在握手阶段,可以通过相关请求首部进行 安全限制、权限校验等。

3.1.2、服务端:响应协议升级

服务端返回内容如下,状态代码101表示协议切换。到此完成协议升级,协议升级完成后,后续的数据交换则遵照WebSocket的协议。

复制代码
1
2
3
4
5
HTTP/1.1 101 Switching Protocols Connection:Upgrade Upgrade: websocket Sec-WebSocket-Accept: Oy4NRAQ13jhfONC7bP8dTKb4PTU=

备注:每个header都以rn结尾,并且最后一行加上一个额外的空行rn。此外,服务端回应的HTTP状态码只能在握手阶段使用。过了握手阶段后,就只能采用特定的错误码。

3.1.3、数据帧

webscoket协议中客户端、服务器端通信是以数据帧的格式,非http协议中文本信息格式。
WebSocket客户端、服务端通信的最小单位是帧(frame),由1个或多个帧组成一条完整的消息(message)。

发送端:将消息切割成多个帧,并发送给服务端;
接收端:接收消息帧,并将关联的帧重新组装成完整的消息;

数据帧格式概览

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued, if payload len == 127 | + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key, if MASK set to 1 | +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +---------------------------------------------------------------+

具体对数据帧的解读,各位可以另行搜索相关文章。

3.2 返回数据

websocket回调的WebSocketListener中,各方法返回的数据都在非主线程中。所以若有界面展示需要更新UI的需求,需要通过handler或者runOnUiThread抛到主线程中更新。

Demo源码请点击(网络库中websocket部分)

最后

以上就是含蓄睫毛最近收集整理的关于基于OKHttp的websocket封装使用1.背景2.源码解析3.相关知识点的全部内容,更多相关基于OKHttp内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(83)

评论列表共有 0 条评论

立即
投稿
返回
顶部