Android小知识-利用OkHttp实现WebSocket通信

7,362 阅读5分钟

本平台的文章更新会有延迟,大家可以关注微信公众号-顾林海,包括年底前会更新kotlin由浅入深系列教程,目前计划在微信公众号进行首发,如果大家想获取最新教程,请关注微信公众号,谢谢!

在讲解如何利用OkHttp实现WebSocket之前,我们聊聊轮询技术,什么是轮询?轮询就是在特定的时间间隔,由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。

轮询分为两种:

  1. 短轮询:通过不断的向服务端发送数据,客户端发送Request,服务端直接返回Response(不管服务端数据有没有改变)。

  2. 长轮询:通过不断的向服务端发送数据,客户端发送Request,服务端发现数据没有改变,就将这个Request挂起,直到有最新数据再发送Response给客户端。

通过上面短轮询的介绍,不难发现它的缺点,如果在某段时间内,服务端数据没有任何变化,但客户端还是不停的发送请求给服务端,服务端也不管数据是否变化,直接返回结果给客户端,那么在这段时间内的所有请求其实是无效的。

这个时候长轮询就弥补了短轮询的缺点,客户端发送请求,服务器会查询数据是否更新,没有更新就会挂起这个请求,直到有新数据,服务端才会把Response返回给客户端,这样是不是就完美了?其实不是,服务端将客户端的请求挂起会导致资源的浪费,比如有1W人请求服务端,那这个时候服务端这边就要开启1W个线程,导致资源占用。

无论是使用短轮询还是使用长轮询,它们通信的方式还是通过HTTP请求的,HTTP头部比较大,但实际数据比较少,造成带宽的浪费,由于不停的轮询,导致服务器CPU占用过高。

既然短轮询和长轮询有这么多问题,那有没有什么解决方案呢?这时WebSocket登场,看下面这张图(来源网络)。

image

可以看到WebSocket的连接是长期存在的,并且可以不断的进行通信,这是一个全双工的通信模式,客户端可以不停的向服务端发送消息,服务端也可以不停的发送消息给客户端。

那么WebSocket和HTTP有什么区别呢?HTTP是一个Request对应一个Response,就是说客服发送一个请求,服务端接受到请求才会向客户端发送响应,Request和Response是一对一的关系,服务端比较懒,它一定要客户端给它发送请求,服务端才会有响应。相比而言,WebSocket在客户端与服务端建立连接后,客服端与服务端就可以进行全双工通信了,WebSocket在建立连接时也是用到了HTTP的协议,但建立起连接后,双方的通信与HTTP就没有任何关系了。

继续下个问题,WebSocket与Socket有什么关系?Socket并不是一种协议,它只是方便我们使用TCP或UDP抽象出来的一层,它是应用层和传输层之间的一种接口,而WebSocket是一种协议,总的来说,这两者压根没有任何关系。

总结:WebSocket是协议,是一个基于TCP的协议,为了建立一个WebSocket连接,需要向服务器发起一个HTTP请求,并加入头部信息“Upgrade:WebSocket”,服务器会解析这些附加的头信息,同时产生Response给客户端,这样客户端与服务端之间的WebSocket链接就连接起来了。

接下来就来介绍OkHttp中怎么使用WebSocket。实例代码如下:

    private OkHttpClient mOkHttpClient;

    private void webSocketConnect() {
        mOkHttpClient = new OkHttpClient();
        Request request = new Request.Builder()
                .url("http://192.168.1.1")
                .build();
        ClientWebSocketListener listener=new ClientWebSocketListener();
        mOkHttpClient.newWebSocket(request,listener);
        mOkHttpClient.dispatcher().executorService().shutdown();
    }

既然使用的是OkHttp,那么第一步就得创建OkHttpClient对象,第二步创建Request对象,并设置url地址,第三步创建一个Listener,这个Listener用于客户端与服务端之间的异步通知,第三步通过OkHttpClient的newWebSocket方法建立客服端与服务端之间的连接,最后关闭Dispatcher当中的线程池。

ClientWebSocketListener代码如下:

    private WebSocket mWebSocket;

    private final class ClientWebSocketListener extends WebSocketListener{
        @Override
        public void onOpen(WebSocket webSocket, Response response) {
            mWebSocket=webSocket;
            mWebSocket.send("您好,我是客户端");
        }

        @Override
        public void onMessage(WebSocket webSocket, String text) {
            Message message=Message.obtain();
            message.obj=text;
            mWebSocketHandler.sendMessage(message);
        }

        @Override
        public void onMessage(WebSocket webSocket, ByteString bytes) {
            Message message=Message.obtain();
            message.obj=bytes.utf8();
            mWebSocketHandler.sendMessage(message);
        }

        @Override
        public void onClosing(WebSocket webSocket, int code, String reason) {
            if(null!=mWebSocket){
                mWebSocket.close(1000,"再见");
                mWebSocket=null;
            }
        }

        @Override
        public void onClosed(WebSocket webSocket, int code, String reason) {
        }

        @Override
        public void onFailure(WebSocket webSocket, Throwable t, @javax.annotation.Nullable Response response) {
        }
    }

ClientWebSocketListener继承自WebSocketListener接口,并实现该接口中的一些方法。

onOpen方法实在客户端与服务端建立连接时的回调,可以通过WebSocket的send方法向服务端发送消息,由于OkHttp使用的是自己的后台发送数据,所以在send的时候不用担心会阻塞当前线程。

两个onMessage方法,只是传入类型不同,可以在这边获取服务端发送过来的消息,onMessage运行在工作线程,如果需要和UI线程进行交互,就得使用Handler来发送消息给UI线程。

onClosing方法表示服务端不再发送数据给客户端时的回调,准备关闭连接,我们可以在这个方法中关闭WebSocket连接。

onClosed方法表示已经被完全关闭了,这时候回调这个方法。onFailure方法在连接失败的时候会回调这个方法。


838794-506ddad529df4cd4.webp.jpg

搜索微信“顾林海”公众号,定期推送优质文章。