Android小知識-利用OkHttp實現WebSocket通訊

公眾號_顧林海發表於2018-10-29

本平臺的文章更新會有延遲,大家可以關注微信公眾號-顧林海,包括年底前會更新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

搜尋微信“顧林海”公眾號,定期推送優質文章。

相關文章