今天给您送上旧报道。 介绍网络套接字。 大家都知道。
前言
最近,上司又来了新的需求,有必要制作物品的网络相关APP。 其中,需要客户端发送和接收服务器不定期发出的消息。 内心OS :这个怎么样? 要用接口进行轮询吗? 是否定时访问接口,如果有数据,是否要更新? 不行。 浪费资源,消耗电力,很多请求变成无效的网络操作。 那是长连接吗? WebSocket协议似乎不错。 通过握手建立较长的连接后,可以随时发送和接收服务器的消息。 就是那个! 怎么整合? 正好在这期间,复习OkHttp源代码时,发现它支持Websocket协议,所以试着使用一下吧。打开!
WebSocket介绍
首先简单说明一下WebSocket。 我们知道Http是位于APP应用层的通信协议,但它只支持单向主动通信,服务器不能主动向客户端发送消息。 另外,Http是无状态的。 也就是说,每次通信都不相关,与服务器的关系不紧密。为了解决与服务器长时间通信的弱点,HTML5规范采用了Web套接字协议(你知道这个名字是怎么来的吗? 的HTML5规格引出的是爸爸的名字) ),然后是基于TCP协议的全双工通信协议。 他与Http属于同一APP应用层协议,下级还是需要通过TCP建立连接。
但是,web套接字在建立TCP连接后,将通过Http进行握手。 也就是说,通过Http向服务器发送GET请求消息,告诉服务器建立WebSocket连接。 请做好准备。 具体来说,在标头信息中添加相关参数。 然后,服务器响应我所知道的,将连接协议更改为web套接字,开始建立长连接。
我在这里贴了请求标头和响应标头的信息,从网上找了图:
3851594110877_.pic.jpg
简单地说,参数:
URL一般以ws或wss开始,ws对应于web套接字协议,wss对应于TLS之上的web套接字。 类似于Http和Https的关系。 请求方法为GET方法。 连接:升级意味着客户端在不使用Http协议的情况下进行连接和升级。 upgrade : web套接字表示客户端将升级并建立web套接字连接。 网络套接字密钥:密钥。 此密钥是随机生成的,服务使用此参数验证请求是否有效。 sec-web套接字版本:13、web套接字中使用的协议一般为13个。 sec-web套接字扩展3360每秒消息延迟是客户端指定的扩展协议。 例如,在这里,permessage-deflate是web套接字的压缩协议。 响应代码101表示对协议的升级做出响应,之后的数据交换遵循Upgradet指定的web套接字协议。
OkHttp实现
添加OkHttp依赖
实施(com.square up.ok http : ok http 33604.7.2 ) )
实现代码
首先初始化okhttp客户端和web套接字的实例。//*
*web套接字初始化
*/
公共语音启动器(
mwbsocketurl=' ws ://echo.web socket.org ';
m客户端=newokhttpclient.builder (
.平移间隔(10,TimeUnit.SECONDS ) )。
. build (;
请求请求=新建请求. builder (
. URL(mwbsocketurl ) )
. build (;
网络套接字=m
Client.newWebSocket(request, new WsListener()); }这里主要是配置了OkHttp的一些参数,以及WebSocket的连接地址。其中newWebSocket方法就是进行WebSocket的初始化和连接。
这里要注意的点是pingInterval方法的配置,这个方法主要是用来设置WebSocket连接的保活。相信做过长连接的同学都知道,一个长连接一般要隔几秒发送一条消息告诉服务器我在线,而服务器也会回复一个消息表示收到了,这样就确认了连接正常,客户端和服务器端都在线。
如果服务器没有按时收到这个消息那么服务器可能就会主动关闭这个连接,节约资源。客户端没有正常收到这个返回的消息,也会做一些类似重连的操作,所以这个保活消息非常重要。
我们称这个消息叫作心跳包,一般用PING,PONG表示,像乒乓球一样,一来一回。所以这里的pingInterval就是设置心跳包发送的间隔时间,设置了这个方法之后,OkHttp就会自动帮我们发送心跳包事件,也就是ping包。当间隔时间到了,没有收到pong包的话,监听事件中的onFailure方法就会被调用,此时我们就可以进行重连。
但是由于实际业务需求不一样,以及okhttp中心跳包事件给予我们权限较少,所以我们也可以自己完成心跳包事件,即在WebSocket连接成功之后,开始定时发送ping包,在下一次发送ping包之前检查上一个pong包是否收到,如果没收到,就视为异常,开始重连。感兴趣的同学可以看看文末的相关源码。
建立连接后,我们就可以正常发送和读取消息了,也就是在上文WsListener监听事件中表现:
//监听事件,用于收消息,监听连接的状态 class WsListener extends WebSocketListener { @Override public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) { super.onClosed(webSocket, code, reason); } @Override public void onClosing(@NotNull WebSocket webSocket, int code, @NotNull String reason) { super.onClosing(webSocket, code, reason); } @Override public void onFailure(@NotNull WebSocket webSocket, @NotNull Throwable t, @Nullable Response response) { super.onFailure(webSocket, t, response); } @Override public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) { super.onMessage(webSocket, text); Log.e(TAG, "客户端收到消息:" + text); onWSDataChanged(DATE_NORMAL, text); //测试发消息 webSocket.send("我是客户端,你好啊"); } @Override public void onMessage(@NotNull WebSocket webSocket, @NotNull ByteString bytes) { super.onMessage(webSocket, bytes); } @Override public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) { super.onOpen(webSocket, response); Log.e(TAG,"连接成功!"); } } //发送String消息 public void send(final String message) { if (mWebSocket != null) { mWebSocket.send(message); } } /** * 发送byte消息 * @param message */ public void send(final ByteString message) { if (mWebSocket != null) { mWebSocket.send(message); } } //主动断开连接 public void disconnect(int code, String reason) { if (mWebSocket != null) mWebSocket.close(code, reason); }这里要注意,回调的方法都是在子线程回调的,如果需要更新UI,需要切换到主线程。
基本操作就这么多,还是很简单的吧,初始化Websocket——连接——连接成功——收发消息。
其中WebSocket类是一个操作接口,主要提供了以下几个方法
send(text: String)发送一个String类型的消息send(bytes: ByteString) 发送一个二进制类型的消息close(code: Int, reason: String?)关闭WebSocket连接如果有同学想测试下WebSocket的功能但是又没有实际的服务器,怎么办呢?其实OkHttp官方有一个MockWebSocket服务,可以用来模拟服务端,下面我们一起试一下:
模拟服务器
首先集成MockWebSocket服务库:
implementation 'com.squareup.okhttp3:mockwebserver:4.7.2'然后就可以新建MockWebServer,并加入MockResponse作为接收消息的响应。
MockWebServer mMockWebServer = new MockWebServer(); MockResponse response = new MockResponse() .withWebSocketUpgrade(new WebSocketListener() { @Override public void onOpen(@NotNull WebSocket webSocket, @NotNull Response response) { super.onOpen(webSocket, response); //有客户端连接时回调 Log.e(TAG, "服务器收到客户端连接成功回调:"); mWebSocket = webSocket; mWebSocket.send("我是服务器,你好呀"); } @Override public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) { super.onMessage(webSocket, text); Log.e(TAG, "服务器收到消息:" + text); } @Override public void onClosed(@NotNull WebSocket webSocket, int code, @NotNull String reason) { super.onClosed(webSocket, code, reason); Log.e(TAG, "onClosed:"); } }); mMockWebServer.enqueue(response);这里服务器端在收到客户端连接成功消息后,给客户端发送了一条消息。要注意的是这段代码要在子线程执行,因为主线程不能进行网络操作。
然后就可以去初始化Websocket客户端了:
//获取连接url,初始化websocket客户端 String websocketUrl = "ws://" + mMockWebServer.getHostName() + ":" + mMockWebServer.getPort() + "/"; WSManager.getInstance().init(websocketUrl);ok,运行项目
//运行结果 E/jimu: mWbSocketUrl=ws://localhost:38355/ E/jimu: 服务器收到客户端连接成功回调: E/jimu: 连接成功! E/jimu: 客户端收到消息:我是服务器,你好呀 E/jimu: 服务器收到消息:我是客户端,你好啊参考
https://github.com/square/okhttp