什麼是socketIO?

SocketIO是在客戶端和服務端之間建立的雙向通信數據交換技術,底層使用EngineIO。SocketIO的的客戶端使用Engine.IO-Client,服務端使用Engine.IO實現。

SocketIO如何工作
當一個瀏覽器嘗試建立SocketIO時,SocketIO首先使用xhr-polling創建一個長輪詢。長輪詢一旦建立,它將升級爲WebSocket連接。
SocketIO底層是使用EngineIO庫實現的,這個庫使用WebSocket和XMLHttprequest封裝了一套自己的Socket協議(暫時叫 EIO Socket)。一個完整的 EIO Socket 包括多個 XHR 和 WebSocket 連接。

客戶端
EIO Socket 通過一個 XHR (XMLHttprequest) 握手。前端發送一個 XHR,告訴服務端我要開始 XHR 長輪詢了。後端返回的數據裏面包括一個 open 標誌(數字 0 表示), 以及sid 和 upgrades 字段,ping時間間隔,ping超時時間。

0{
"sid": "8b7ab1ae-fbcf-4d23-8192-3c14a2a90721",
"upgrades": [
"websocket"
],
"pingInterval": 10000,
"pingTimeout": 60000
}

sid 是本次 EIO Socket 的會話 ID,因爲一次 EIO Socket 包含了多個請求,而後端又會同時連接多個 EIO Socket,sid 的作用就相當於 SESSION ID。
另一個字段 upgrades,正常情況下是 [‘websocket’],表示可以把連接方式從長輪詢升級到 WebSocket。
前端在發送第一個 XHR 的時候就開始了 XHR 長輪詢,這個時候如果有收發數據的需求,是通過長輪詢實現的。所謂長輪詢,是指前端發送一個 request,服務端會等到有數據需要返回時再 response. 前端收到 response 後馬上發送下一次 request。這樣就可以實現雙向通信。
前端收到握手的 upgrades 後,EIO 會檢測瀏覽器是否支持 WebSocket,如果支持,就會啓動一個 WebSocket 連接,然後通過這個 WebSocket 往服務器發一條內容爲 probe, 類型爲 ping 的數據。如果這時服務器返回了內容爲 probe, 類型爲 pong 的數據,前端就會把前面建立的 HTTP 長輪詢停掉,後面只使用 WebSocket 通道進行收發數據。
EIO Socket 生命週期內,會間隔一段時間 ping - pong 一次,用來測試網絡是否正常。
在這裏插入圖片描述
這是 WebSocket 幀的結構,綠色是發送,白色是接收。前面的數字是數據包類型,2 是 ping, 3 是 pong, 42是 message。

服務端
服務端使用 ws 庫實現 WebSocket 協議。http://socket.io 服務啓動時,會先啓動一個 ws 服務。http://socket.io 會監聽 HTTP 服務器的 upgrade 和 request 事件。當 upgrade 事件觸發時,說明可能是 WebSocket 握手,先簡單校驗下,然後把請求交給 ws 服務進行處理,拿到 WebSocket 對象。當 request 事件觸發時,根據 url 路徑判斷是不是 http://socket.io 的 XHR 請求,拿到 res 和 res 對象。這樣就可以正確接收和返回客戶端數據了,具體處理過程和前端部分是對應的。

示例一SocketIO進行聊天
服務器

服務器需要安裝node.js,我們使用Express簡化設置
使用以下命令創建一個新文件夾:
mkdir socket.io-example
cd socket.io-example
npm install socket.io express
安裝服務器並導入所需的程序包。

const app = require("express")();const http = require("http").createServer(app);const io = require("socket.io")(http);

將服務器根目錄設置爲index.html

app.get("/", (req, res) => res.sendFile(__dirname + "/index.html"));

設置SocketIO的鏈接建立事件,這裏我們只打印一條log

io.on("connection", function(socket) { console.log(“socket connected”); });

設置服務器偵聽端口爲3000

http.listen(3000, () => console.log("listening on http://localhost:3000"));

運行命令node index.js,然後在瀏覽器中打開頁面。

客戶端
在頁面上導入SocketIO腳本

<script src="/socket.io/socket.io.js"></script> <script> const socket =io.connect("http://127.0.0.1:1234/easemob",{path: "/socket.io/"}); </script>

這樣,便建立起了一個SocketIO連接。

服務器
在服務器內部,我們使用io.emit()向所有連接的客戶端發送消息,當有用戶連接到服務器時,以下代碼將對所有服務器進行通知,包括連接者本人。

io.on("connection", function(socket) { io.emit(“user connected”); });

如果你想向除了連接者之外的所有人廣播,你可以使用

socket.broadcast.emit()

在此基礎上,我們還可以對客服端發送的消息添加監聽器,並將響應消息發送給所有用戶。

io.on("connection", function(socket) {
 io.emit(“user connected”); socket.on(“message", function(msg) {
  io.emit("message", msg);
}); 
});

以下實例顯示了怎麼將這些事件添加到客戶端。
客戶端
在index.html中引入我們的腳本,同時創建簡單的表單消息和顯示消息的容器。

<!DOCTYPE html>
<html>
 <head>
   <meta charset="UTF-8" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
   <meta http-equiv="X-UA-Compatible" content="ie=edge" />
   <title>Socket.io Example</title>
 </head>
 
 <body>
   <h1>Our Socket.io Chat Application</h1>
   <div>
     <h2>Messages</h2>
     <ul></ul>
   </div>
   <form action="">
     <input type="text" />
     <button>Send</button>
   </form>
   <script src="/socket.io/socket.io.js"></script>
   <script>
     const socket = io();
   </script>
 </body>
</html>

再添加一些其他邏輯

<script>
 
 const form = document.querySelector("form");
 const input = document.querySelector("input");
 messageList = document.querySelector("ul");
 
 
 const socket = io();
 
 
 function sendMessage(e) {
   
   e.preventDefault();
   
   socket.emit("message", input.value);
   
   input.value = "";
 }
 
 
 form.addEventListener("submit", sendMessage);
 
 
 function addMessageToHTML(message) {
   
   const li = document.createElement("li");
   
   li.innerText = message;
   
   messageList.append(li);
 }
  
 socket.on("message", addMessageToHTML);
 
 
 function alertUserConnected() {
   addMessageToHTML("User connected");
 }
  
 socket.on("user connected", alertUserConnected);
 
</script>

這其中最重要的是socket.on(event,callback)功能。當服務器發出的事件與我們設置的’event’匹配時,將觸發回調,這時將在屏幕上顯示消息。

維護和操作SocketIO
綜上所述,SocketIO入門相對簡單,只需要一個Node.js服務器即可在其上運行。如果要開始爲有限數量的用戶使用實時應用程序,則Socket.IO是一個不錯的選擇。大規模工作時會出現問題。舉例來說,您要建立一個CRM類似的應用程序,可實現企業之間的通信。SocketIO基於異步網絡庫構建,將會增加服務器負載。維持與用戶的連接以及發送和接收消息會增加服務器壓力,並且如果客戶端開始通過SocketIO發送大量數據,它將以數據塊的形式傳輸數據,在傳輸數據塊傳輸完畢時釋放資源。因此,當您的應用程序存在大量用戶並且服務器達到其最大負載時,您需要在多個服務器上對連接進行負載均衡,否則可能會丟失重要信息。
不幸的是,這不只是添加其他服務器那麼簡單。套接字是服務器和客戶端之間的開放連接。服務器僅知道直接與其連接的客戶端,而不知道與其他服務器連接的客戶端。回到會話的功能,假設您在某太服務器上想向所有用戶廣播某人加入聊天的消息,那麼對於連接到其他服務器的用戶,則不會收到此消息。
要解決此問題,您需要擁有一個消息訂閱發佈服務(例如Redis)。該服務將通過通知所有服務器在有人加入聊天時需要發送消息來解決上述問題。不幸的是,這意味着要維護一個額外的服務,該服務很可能需要自己的服務器。
SocketIO包含一個適配器socketio-adapter,可與發佈/訂閱存儲和服務器共享信息。您可以編寫自己的適配器實現,也可以使用它們作爲Redis的適配器,幸運的是,Socket.IO易於集成。
Socket.IO的其他可靠性增強可能包括CoreOS,可將體系結構拆分爲在不同硬件之間分佈的單元,並隨着負載的增加引入新實例。
擴展Socket.IO的另一個問題是,儘管WebSocket保持連接打開,但如果連接回退到輪詢,則在連接生命週期中會有多個請求。當這些請求之一轉到另一臺服務器時,您將收到錯誤“在WebSocket握手期間發生錯誤:意外的響應代碼:400”。

SocketIO的限制
與所有技術一樣,選擇正確的一種意味着明確您對產品未來的期望。與您自己創建Socket鏈接相比,SocketIO確實使許多事情變得更容易,但是除了上面提到的擴展問題之外,還有侷限性和缺點。
首先是初始連接比WebSockets更長。這是因爲它首先使用長輪詢和XHRPolling建立連接,然後升級到WebSocket(如果可用)。如果您不需要支持較舊的瀏覽器並且不擔心不支持WebSockets的客戶端環境,則可能不需要SocketIO的額外開銷。您可以通過指定僅與WebSockets連接來最大程度地減少這種影響。這將更改與WebSocket的初始連接,但是會關閉備選方案。

客戶端

Const socket = io({transports: [“websocket”], upgrade: false});

服務器

io.set("transports", ["websocket"]);

在這種情況下,客戶端仍將需要下載61.2 KB的socketioJavaScript文件。
對於其他繁重的數據傳輸(例如,視頻流傳輸),Socket不是好的解決方案。如果要在此級別上支持數據交換,則更好的解決方案是webRTC或流數據傳輸服務商,Ably是其中之一。(本文由環信編譯,轉載請註明出處)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章