輪詢以及webSocket與socket.io原理

2022-08-10 18:04:28

概述:

首先,我們知道,起初的http協定只是為了能夠進行通訊而被創造出來(也就是請求-響應的過程)。並沒有雙向通訊這一說,後面隨著歷史業務的需求,人們使用輪詢http來解決雙向通訊也就是使用xhr或者jsonp的方法進行傳送請求到伺服器端並且進行回撥獲取伺服器端資料

通訊的幾個名稱:

單工通訊:既只能使用者端向伺服器端傳送資料或者伺服器端向用戶端傳送資料(如廣播,電視之類的,他可以給你傳播資訊,你卻不能給他迴應)
半雙工單向通訊:使用者端可以向伺服器端傳送資料,伺服器端也可以向用戶端傳送資料,但是不能同時,只能這一端傳送完後另一端才可以進行響應(對講機,他講一句你講一句,但是不能同時講)
全雙工通訊:使用者端可以向伺服器端傳送資料,伺服器端也可以向用戶端傳送資料,可以同時進行(電話,qq聊天等等,可以同時講或者傳送訊息)

1:輪詢:隔一段時間進行一次查詢或者詢問


輪詢分為長輪詢和短輪詢,長輪詢是基於短輪詢的一個優化結果。

短輪詢:

通過使用者端定期輪詢來詢問伺服器端是否有新的資訊產生,如果有則返回,沒有就不響應,
缺點:也是顯而易見,輪詢間隔大了則資訊不夠實時,輪詢間隔過小又會消耗過多的流量,增加伺服器的負擔。

長輪詢:

是需要伺服器端進行更改來進行支援,使用者端向伺服器端傳送請求時,如果此時伺服器端沒有新的資訊產生,並不立刻返回,而是Hold住一段時間等有新的資訊或者超時再返回,使用者端收到伺服器的應答後繼續輪詢。可以看到長輪詢比短輪詢可以減少大量無用的請求,並且使用者端接收取新訊息也會實時不少。減少http請求對效能的優化是很有利的,所以他是短輪詢上的一個優化
缺點:終歸來講還是一個http請求,只是進行了變化而已,而且如果使用者端不請求,伺服器端有資料的話,也會一直累積在那,不能實現實時的雙向通訊

此時的webSocket也就應需而生


2:webSocket協定原理


webSocket也是基於Tcp協定傳輸層連線的,跟http相同處於協定應用層,而且它還是基於http的握手的,只是是握手的時候會傳輸特定的資料讓協定升級成為webSocket協定
與http與之不同的是webSocket是一個持久化協定,而http協定是一個非持久化協定,也就是http他請求然後響應就結束了,而webSocket會一直保持連線而且一直傳輸資料,直到你將連線斷開

websocket連線過程:

GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: Y3JJCMbDL1IDUCH9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 20

其中的這倆段程式碼就是將http升級為webSocket的關鍵

Upgrade: websocket
Connection: Upgrade

而後面的三行程式碼則是一些驗證資訊

  • Sec-WebSocket-Key:瀏覽器隨機生成,用於給伺服器端使用,如果伺服器端支援webSocket,伺服器端會對該資料進行一些處理然後返回給使用者端進行驗證

  • Sec-WebSocket-Protocol:是一個列表,列表中列出使用者端所支援的協定

  • Sec-WebSocket-Version:指定版本

然後伺服器端就會返回

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

伺服器端返回這倆段程式碼就說明升級成功

Upgrade: websocket
Connection: Upgrade
  • Sec-WebSocket-Accept:對Sec-WebSocket-Key進行處理後的資料。用於證明他是支援升級後的協定的,驗證成功

  • Sec-WebSocket-Protocol:伺服器端最終選定的協定

做完這些以後這次連線之後就都是webSocket連線了,既進入到全雙工通訊

3:socket.io原理

介紹

首先,socket.io是一個庫,一個基於engine.io協定(封裝了webSocket協定)的庫,在協定上建立了Engine.io引擎,socket.io則是該引擎的應用層框架

它相對比原生webSocket的一些特性

  • 長輪詢回退:如果無法建立webSocket連線,socket.io將會退回到http長輪詢進行連線,這也是為了相容一些特別老的專案和極少數不支援的瀏覽器(現如今)

  • 自動連線:在一些情況下,連線某一方有可能在不知情的情況下斷開,它有一個心跳機制,可以定時去監測是否連線,只要不是使用者端主動關閉連線,socket.io就會在連線出錯後不斷重試以建立連線,伺服器端資料會進行自動緩衝,直到再次連線,為了防止斷開時間過長,緩衝時間過長,可以利用使用Socket 範例的connected屬性進行處理,或者使用Volatile事件,使伺服器端丟棄原來的緩衝,只返回最新的資料(官網有該方法,在此就不多描述)

  • 多路複用:Socket.io允許你在單個共用連線上建立多個namespace,這些namespace擁有單獨的通訊通道(room),也可設定單獨的許可權驗證,但是可以共用原來的底層連線;例如,如果您想建立一個只有授權使用者才能加入的管理員頻道

  • 支援Room功能:room是在namespace下的,舉個例子:namespace如同一片地區,room是這片地區中個房子,socket則是房子中的人,namespace是可以在別的namespace中通訊的,但是room只能在該spacename下的room之間進行通訊,socket也只能收到該namespace的廣播

socket.io連線過程:

同樣使用者端發起http請求,並帶有

Upgrade: websocket
Connection: Upgrade

伺服器端返回

"sid":"ab4507c4-d947-4deb-92e4-8a9e34a9f0b2"
"upgrades":["websocket"]
"pingInterval":25000
"pingTimeout":60000}
  • sid:sid 是本次對談的ID,因為一次連線包含了多個請求,sid 的作用就相當於 SESSION ID。也是使用者端的標識

  • pingInterval:ping的間隔時長

  • pingTimeout:判斷連線超時的時長
    當用戶端收到響應之後,scoket.io會根據當前使用者端環境是否支援Websocket。如果支援,則建立一個websocket連線,否則退回到長輪詢進行雙向資料通訊。

engine.io協定原理

engine.io的資料分為Packet和Payload,其中 Packet是封包,有6種型別:
0. open:從伺服器端發出,標識一個新的傳輸方式已經開啟。

  1. close:請求關閉這條傳輸連線,但是它本身並不關閉這個連線。

  2. ping:使用者端週期性傳送ping,伺服器端響應pong。

  3. pong:伺服器端傳送。

  4. message:真實資料

  5. upgrade:在轉換(transport)前,engine.io會傳送探測包測試新的transport(如websocket)是否可用,如果OK,則使用者端會傳送一個upgrade訊息給伺服器端,伺服器端關閉老的transport然後切換到新的transport。用於升級協定

  6. noop:空運算元據包,使用者端收到noop訊息會將之前等待暫停的輪詢暫停,用於在接收到一個新的websocket強制一個新的輪詢週期。

4:總結

socket.io可以說是一個很好的工具,無論是用做聊天或者是其他實時的資料通訊,在使用時也遇到過一些問題,後面都慢慢解決了

本文章如有錯漏,歡迎指正。