用OkHttp实现WebSocket长连接
点击上方 蓝字 关注我,❤
今天给大家带来一篇老文章,介绍WebSocket,大家可以了解了解。
前言
最近老板又来新需求了,要做一个物联网相关的app
,其中有个需求是客户端需要收发服务器不定期发出的消息。
内心OS:
🤔 这咋整呢?通过接口轮询?定时访问接口,有数据就更新?
🤔 不行不行,这样浪费资源了,还耗电,会导致很多请求都是无效的网络操作。
🤔 那就长连接呗?WebSocket协议
好像不错,通过握手建立长连接后,可以随时收发服务器的消息。那就它了!
🤔 怎么集成呢?正好前段时间复习OkHttp
源码的时候发现了它是支持Websocket
协议的,那就用它试试吧!
开淦!
WebSocket介绍
先简单介绍下WebSocket
。我们都知道Http是处于应用层的一个通信协议
,但是只支持单向主动通信,做不到服务器主动向客户端推送消息。而且Http是无状态
的,即每次通信都没有关联性,导致跟服务器关系不紧密。
为了解决和服务器长时间通信的痛点呢,HTML5
规范引出了WebSocket
协议(知道这名字咋来的吧,人家HTML5
规范引出的,随爸姓),是一种建立在TCP
协议基础上的全双工通信的协议。他跟Http
同属于应用层协议,下层还是需要通过TCP建立连接。
但是,WebSocket
在TCP
连接建立后,还要通过Http
进行一次握手,也就是通过Http
发送一条GET请求
消息给服务器,告诉服务器我要建立WebSocket连接
了,你准备好哦,具体做法就是在头部信息中添加相关参数。然后服务器响应我知道了,并且将连接协议改成WebSocket
,开始建立长连接。
这里贴上请求头和响应头信息,从网上找了一张图:
简单说明下参数:
-
URL一般是以 ws
或者wss
开头,ws
对应Websocket
协议,wss
对应在TLS
之上的WebSocket
。类似于Http
和Https
的关系。 -
请求方法为GET方法。 -
Connection:Upgrade
,表示客户端要连接升级,不用Http协议。 -
Upgrade:websocket
, 表示客户端要升级建立Websocket
连接。 -
Sec-Websocket-Key:key
, 这个key是随机生成的,服务器会通过这个参数验证该请求是否有效。 -
Sec-WebSocket-Version:13
, websocket使用的协议,一般就是13。 -
Sec-webSocket-Extension:permessage-deflate
,客户端指定的一些扩展协议,比如这里permessage-deflate
就是WebSocket
的一种压缩协议。 -
响应码101,
表示响应协议升级,后续的数据交互都按照Upgradet指定的WebSocket
协议来。
OkHttp实现
添加OkHttp依赖
implementation("com.squareup.okhttp3:okhttp:4.7.2")
实现代码
首先是初始化OkHttpClient
和WebSocket
实例:
/**
* 初始化WebSocket
*/
public void init() {
mWbSocketUrl = "ws://echo.websocket.org";
mClient = new OkHttpClient.Builder()
.pingInterval(10, TimeUnit.SECONDS)
.build();
Request request = new Request.Builder()
.url(mWbSocketUrl)
.build();
mWebSocket = mClient.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
感谢大家的阅读,有一起学习的小伙伴可以关注下公众号—
码上积木
❤️❤️
每日三问知识点/面试题,积少成多。
每天一见
给你力量
码上积木
点点在看,你最好看
本文分享自微信公众号 - 码上积木(Lzjimu)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
揭秘京东城市时空数据引擎—JUST如何助力交通流量预测
2014年跨年夜上海外滩人流隐患事件,使得公共安全问题受到了全体社会的广泛关注。解决这一问题的很重要一项工作就是:如何实时监控和快速预测城市中每个地方的人流量。当某个地方的人流量超过给定的值或者有超过给定值的趋势时,相关部门能及时地采取相关措施,例如:疏散人群,交通引流等,这样才能防止悲剧的再次发生。 为避免类似公共安全隐患,解决因人流问题造成的交通、社会治安等问题,搭建城市实时人流监控预测系统势在必行。 ▲图1 上海外滩的踩踏事件 京东城市作为系统建设中标单位,对整个系统的需求进行了初步分析,发现一个区域的人流量与多种数据相关,如图2所示。 比如:(1)手机基站数据。当一个地方的手机信令数据越多时,说明周围的人也越多; (2)视频监控数据。从视频的画面中,能够识别出大约有多少人; (3)交通流量数据。某条路的交通流量直观的反映了某个区域的流入流出人数; (4)出行轨迹数据。轨迹数据能够反映出人的流向; (5)天气数据。人的出行受天气的影响,比如下雨天,人们都很少出门,因此天气数据也有助于人流量的预测; (6)不同的地点所能承载的最大人流量是不同的。比如大型火车站,2万的人其实不会造成...
- 下一篇
处理XML数据应用实践
摘要:GaussDB(DWS)支持XML数据类型及丰富的XML解析函数,可实现关系数据和XML数据的映射管理功能。 XML概述 XML是可扩展的标识语言(eXtensibleMarkupLanguage)的缩写,可以描述非常复杂的数据结构,广泛应用于传输和存储数据。XML是一种类似于HTML的标记语言,但XML没有使用预定义的标记,可以根据应用需求定义标记。XML的基本格式是标准化的,可以跨平台、操作系统和应用程序实现异构系统之间的数据共享。 XML数据类型 GaussDB(DWS)支持将XML文档存储在数据库的XML数据类型列中。通过XML数据类型来保存数据,相比于文本方式的优势在于具有数据结构检查功能,能够保证结构的正确,并且支持XML数据解析和处理函数。 判断一个 XML 文档正确的标准是: 文档必须是一个格式良好的文档。 文档遵循 XML 所有的语法规则并且有效。 文档遵循特定语义的规则,这些规则通常规定在 XML 或 DTD 规范中 XML可以存储由XML标准定义的格式正确的文档,以及由XML标准中定义的内容片断,内容片断可以有多个顶级元素或字符节点。 下面是一个格式良好的X...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8编译安装MySQL8.0.19
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Windows10,CentOS7,CentOS8安装Nodejs环境
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- 设置Eclipse缩进为4个空格,增强代码规范
- MySQL8.0.19开启GTID主从同步CentOS8