您好,登錄后才能下訂單哦!
WebSocket中怎么利用OkHttp實(shí)現(xiàn)長(zhǎng)連接,相信很多沒(méi)有經(jīng)驗(yàn)的人對(duì)此束手無(wú)策,為此本文總結(jié)了問(wèn)題出現(xiàn)的原因和解決方法,通過(guò)這篇文章希望你能解決這個(gè)問(wèn)題。
WebSocket介紹
先簡(jiǎn)單介紹下WebSocket。我們都知道Http是處于應(yīng)用層的一個(gè)通信協(xié)議,但是只支持單向主動(dòng)通信,做不到服務(wù)器主動(dòng)向客戶端推送消息。而且Http是無(wú)狀態(tài)的,即每次通信都沒(méi)有關(guān)聯(lián)性,導(dǎo)致跟服務(wù)器關(guān)系不緊密。
為了解決和服務(wù)器長(zhǎng)時(shí)間通信的痛點(diǎn)呢,HTML5規(guī)范引出了WebSocket協(xié)議(知道這名字咋來(lái)的吧,人家HTML5規(guī)范引出的,隨爸姓),是一種建立在TCP協(xié)議基礎(chǔ)上的全雙工通信的協(xié)議。他跟Http同屬于應(yīng)用層協(xié)議,下層還是需要通過(guò)TCP建立連接。
但是,WebSocket在TCP連接建立后,還要通過(guò)Http進(jìn)行一次握手,也就是通過(guò)Http發(fā)送一條GET請(qǐng)求消息給服務(wù)器,告訴服務(wù)器我要建立WebSocket連接了,你準(zhǔn)備好哦,具體做法就是在頭部信息中添加相關(guān)參數(shù)。然后服務(wù)器響應(yīng)我知道了,并且將連接協(xié)議改成WebSocket,開(kāi)始建立長(zhǎng)連接。
這里貼上請(qǐng)求頭和響應(yīng)頭信息,從網(wǎng)上找了一張圖:
3851594110877_.pic.jpg
簡(jiǎn)單說(shuō)明下參數(shù):
URL一般是以ws或者wss開(kāi)頭,ws對(duì)應(yīng)Websocket協(xié)議,wss對(duì)應(yīng)在TLS之上的WebSocket。類似于Http和Https的關(guān)系。
請(qǐng)求方法為GET方法。
Connection:Upgrade,表示客戶端要連接升級(jí),不用Http協(xié)議。
Upgrade:websocket, 表示客戶端要升級(jí)建立Websocket連接。
Sec-Websocket-Key:key, 這個(gè)key是隨機(jī)生成的,服務(wù)器會(huì)通過(guò)這個(gè)參數(shù)驗(yàn)證該請(qǐng)求是否有效。
Sec-WebSocket-Version:13, websocket使用的協(xié)議,一般就是13。
Sec-webSocket-Extension:permessage-deflate,客戶端指定的一些擴(kuò)展協(xié)議,比如這里permessage-deflate就是WebSocket的一種壓縮協(xié)議。
響應(yīng)碼101,表示響應(yīng)協(xié)議升級(jí),后續(xù)的數(shù)據(jù)交互都按照Upgradet指定的WebSocket協(xié)議來(lái)。
OkHttp實(shí)現(xiàn)
添加OkHttp依賴
implementation("com.squareup.okhttp3:okhttp:4.7.2")
實(shí)現(xiàn)代碼
首先是初始化OkHttpClient和WebSocket實(shí)例:
/** * 初始化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的一些參數(shù),以及WebSocket的連接地址。其中newWebSocket方法就是進(jìn)行WebSocket的初始化和連接。
這里要注意的點(diǎn)是pingInterval方法的配置,這個(gè)方法主要是用來(lái)設(shè)置WebSocket連接的?;?。相信做過(guò)長(zhǎng)連接的同學(xué)都知道,一個(gè)長(zhǎng)連接一般要隔幾秒發(fā)送一條消息告訴服務(wù)器我在線,而服務(wù)器也會(huì)回復(fù)一個(gè)消息表示收到了,這樣就確認(rèn)了連接正常,客戶端和服務(wù)器端都在線。
如果服務(wù)器沒(méi)有按時(shí)收到這個(gè)消息那么服務(wù)器可能就會(huì)主動(dòng)關(guān)閉這個(gè)連接,節(jié)約資源??蛻舳藳](méi)有正常收到這個(gè)返回的消息,也會(huì)做一些類似重連的操作,所以這個(gè)保活消息非常重要。
我們稱這個(gè)消息叫作心跳包,一般用PING,PONG表示,像乒乓球一樣,一來(lái)一回。所以這里的pingInterval就是設(shè)置心跳包發(fā)送的間隔時(shí)間,設(shè)置了這個(gè)方法之后,OkHttp就會(huì)自動(dòng)幫我們發(fā)送心跳包事件,也就是ping包。當(dāng)間隔時(shí)間到了,沒(méi)有收到pong包的話,監(jiān)聽(tīng)事件中的onFailure方法就會(huì)被調(diào)用,此時(shí)我們就可以進(jìn)行重連。
但是由于實(shí)際業(yè)務(wù)需求不一樣,以及okhttp中心跳包事件給予我們權(quán)限較少,所以我們也可以自己完成心跳包事件,即在WebSocket連接成功之后,開(kāi)始定時(shí)發(fā)送ping包,在下一次發(fā)送ping包之前檢查上一個(gè)pong包是否收到,如果沒(méi)收到,就視為異常,開(kāi)始重連。感興趣的同學(xué)可以看看文末的相關(guān)源碼。
建立連接后,我們就可以正常發(fā)送和讀取消息了,也就是在上文WsListener監(jiān)聽(tīng)事件中表現(xiàn):
//監(jiān)聽(tīng)事件,用于收消息,監(jiān)聽(tīng)連接的狀態(tài) 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); //測(cè)試發(fā)消息 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,"連接成功!"); } } //發(fā)送String消息 public void send(final String message) { if (mWebSocket != null) { mWebSocket.send(message); } } /** * 發(fā)送byte消息 * @param message */ public void send(final ByteString message) { if (mWebSocket != null) { mWebSocket.send(message); } } //主動(dòng)斷開(kāi)連接 public void disconnect(int code, String reason) { if (mWebSocket != null) mWebSocket.close(code, reason); }
這里要注意,回調(diào)的方法都是在子線程回調(diào)的,如果需要更新UI,需要切換到主線程。
基本操作就這么多,還是很簡(jiǎn)單的吧,初始化Websocket——連接——連接成功——收發(fā)消息。
其中WebSocket類是一個(gè)操作接口,主要提供了以下幾個(gè)方法
send(text: String)發(fā)送一個(gè)String類型的消息
send(bytes: ByteString) 發(fā)送一個(gè)二進(jìn)制類型的消息
close(code: Int, reason: String?)關(guān)閉WebSocket連接
如果有同學(xué)想測(cè)試下WebSocket的功能但是又沒(méi)有實(shí)際的服務(wù)器,怎么辦呢?其實(shí)OkHttp官方有一個(gè)MockWebSocket服務(wù),可以用來(lái)模擬服務(wù)端,下面我們一起試一下:
模擬服務(wù)器
首先集成MockWebSocket服務(wù)庫(kù):
implementation 'com.squareup.okhttp3:mockwebserver:4.7.2'
然后就可以新建MockWebServer,并加入MockResponse作為接收消息的響應(yīng)。
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); //有客戶端連接時(shí)回調(diào) Log.e(TAG, "服務(wù)器收到客戶端連接成功回調(diào):"); mWebSocket = webSocket; mWebSocket.send("我是服務(wù)器,你好呀"); } @Override public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) { super.onMessage(webSocket, text); Log.e(TAG, "服務(wù)器收到消息:" + 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);
這里服務(wù)器端在收到客戶端連接成功消息后,給客戶端發(fā)送了一條消息。要注意的是這段代碼要在子線程執(zhí)行,因?yàn)橹骶€程不能進(jìn)行網(wǎng)絡(luò)操作。
然后就可以去初始化Websocket客戶端了:
//獲取連接url,初始化websocket客戶端 String websocketUrl = "ws://" + mMockWebServer.getHostName() + ":" + mMockWebServer.getPort() + "/"; WSManager.getInstance().init(websocketUrl);
看完上述內(nèi)容,你們掌握WebSocket中怎么利用OkHttp實(shí)現(xiàn)長(zhǎng)連接的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。