美文网首页Android开发Android开发经验谈Android技术知识
雨露均沾的OkHttp—WebSocket长连接(使用篇)

雨露均沾的OkHttp—WebSocket长连接(使用篇)

作者: jett老师 | 来源:发表于2020-07-09 14:50 被阅读0次

    作者:积木zz
    链接:https://juejin.im/post/5f0452615188252e5522b747

    前言

    最近老板又来新需求了,要做一个物联网相关的app,其中有个需求是客户端需要收发服务器不定期发出的消息。 内心OS:
    🤔 这咋整呢?通过接口轮询?定时访问接口,有数据就更新?
    🤔 不行不行,这样浪费资源了,还耗电,会导致很多请求都是无效的网络操作。
    🤔 那就长连接呗?WebSocket协议好像不错,通过握手建立长连接后,可以随时收发服务器的消息。那就它了!
    🤔 怎么集成呢?正好前段时间复习OkHttp源码的时候发现了它是支持Websocket协议的,那就用它试试吧!(戏好多,演不下去了🤮)

    开淦!

    WebSocket介绍

    先简单介绍下WebSocket。 我们都知道Http是处于应用层的一个通信协议,但是只支持单向主动通信,做不到服务器主动向客户端推送消息。而且Http是无状态的,即每次通信都没有关联性,导致跟服务器关系不紧密。

    为了解决和服务器长时间通信的痛点呢,HTML5规范引出了WebSocket协议(知道这名字咋来的吧,人家HTML5规范引出的,随爸姓),是一种建立在TCP协议基础上的全双工通信的协议。他跟Http同属于应用层协议,下层还是需要通过TCP建立连接。

    但是,WebSocketTCP连接建立后,还要通过Http进行一次握手,也就是通过Http发送一条GET请求消息给服务器,告诉服务器我要建立WebSocket连接了,你准备好哦,具体做法就是在头部信息中添加相关参数。然后服务器响应我知道了,并且将连接协议改成WebSocket,开始建立长连接。

    这里贴上请求头和响应头信息,从网上找了一张图:

    简单说明下参数:

    • URL一般是以ws或者wss开头,ws对应Websocket协议,wss对应在TLS之上的WebSocket。类似于HttpHttps的关系。
    • 请求方法为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")

    实现代码

    首先是初始化OkHttpClientWebSocket实例:

     //初始化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消息
     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: 服务器收到消息:我是客户端,你好啊 复制代码`</pre>
    

    相关的WebSocket管理类和模拟服务器类我也上传到github了,有需要的同学可以文末自取。

    下期预告

    下期会讲Websocket连接相关的源码,包括怎么连接,怎么收发消息等。 希望各位同学能点个赞👍,支持下我,谢谢❤️ !

    点击【Android高级工程师进阶学习】加入我们的圈子领取资料和我们一起吧学习交流吧!
    【Android高级工程师进阶系统面试题】:下载链接

    • Android进阶学习全套手册
      关于实战,我想每一个做开发的都有话要说,对于小白而言,缺乏实战经验是通病,那么除了在实际工作过程当中,我们如何去更了解实战方面的内容呢?实际上,我们很有必要去看一些实战相关的电子书。目前,我手头上整理到的电子书还算比较全面,HTTP、自定义view、c++、MVP、Android源码设计模式、Android开发艺术探索、Java并发编程的艺术、Android基于Glide的二次封装、Android内存优化——常见内存泄露及优化方案、.Java编程思想 (第4版)等高级技术都囊括其中。
    • Android高级架构师进阶知识体系图
      关于视频这块,我也是自己搜集了一些,都按照Android学习路线做了一个分类。按照Android学习路线一共有八个模块,其中视频都有对应,就是为了帮助大家系统的学习。接下来看一下导图和对应系统视频吧!!!

    • Android对标阿里P7学习视频

    • BATJ大厂Android高频面试题
      这个题库内容是比较多的,除了一些流行的热门技术面试题,如Kotlin,数据库,Java虚拟机面试题,数组,Framework ,混合跨平台开发,等

    相关文章

      网友评论

        本文标题:雨露均沾的OkHttp—WebSocket长连接(使用篇)

        本文链接:https://www.haomeiwen.com/subject/pxtscktx.html