前言
在做后台項目的時候,發現頁面過一會彈出報錯
WebSocket connection to 'ws://localhost:3000/ws' failed:
WebSocketClient @ WebSocketClient.js:16
initSocket @ socket.js:21
(anonymous) @ socket.js:45

正好面試會考,溫習一下吧
1. 網絡模型

以OSI7層協議划分:
1.1 應用層
模型中最靠近用戶的一層,是為計算機用戶提供應用接口,也為用戶直接提供各種網絡服務
- HTTP
經典面試--說一下HTTP2的區別吧:
- 二進制協議:HTTP/2 是一個二進制協議。在 HTTP/1.1 版中,報文的頭信息必須是文本(ASCII 編碼),數據體可以是文本,也可以是二進制。HTTP/2 則是一個徹底的二進制協議,頭信息和數據體都是二進制,並且統稱為"幀",可以分為頭信息幀和數據幀。 幀的概念是它實現多路復用的基礎。
- 多路復用: HTTP/2 實現了多路復用,HTTP/2 仍然復用 TCP 連接,但是在一個連接里,客戶端和服務器都可以同時發送多個請求或回應,而且不用按照順序一一發送,這樣就避免了"隊頭堵塞"的問題。
- 數據流: HTTP/2 使用了數據流的概念,因為 HTTP/2 的數據包是不按順序發送的,同一個連接里面連續的數據包,可能屬於不同的請求。因此,必須要對數據包做標記,指出它屬於哪個請求。HTTP/2 將每個請求或回應的所有數據包,稱為一個數據流。每個數據流都有一個獨一無二的編號。數據包發送時,都必須標記數據流 ID ,用來區分它屬於哪個數據流。
- 頭信息壓縮: HTTP/2 實現了頭信息壓縮,由於 HTTP 1.1 協議不帶狀態,每次請求都必須附上所有信息。所以,請求的很多字段都是重復的,比如 Cookie 和 User Agent ,一模一樣的內容,每次請求都必須附帶,這會浪費很多帶寬,也影響速度。HTTP/2 對這一點做了優化,引入了頭信息壓縮機制。一方面,頭信息使用 gzip 或 compress 壓縮后再發送;另一方面,客戶端和服務器同時維護一張頭信息表,所有字段都會存入這個表,生成一個索引號,以后就不發送同樣字段了,只發送索引號,這樣就能提高速度了。
- 服務器推送: HTTP/2 允許服務器未經請求,主動向客戶端發送資源,這叫做服務器推送。使用服務器推送提前給客戶端推送必要的資源,這樣就可以相對減少一些延遲時間。這里需要注意的是 http2 下服務器主動推送的是靜態資源,和 WebSocket 以及使用 SSE 等方式向客戶端發送即時數據的推送是不同的。
"隊頭堵塞":隊頭阻塞是由 HTTP 基本的“請求 - 應答”模型所導致的。HTTP 規定報文必須是“一發一收”,這就形成了一個先進先出的“串行”隊列。隊列里的請求是沒有優先級的,只有入隊的先后順序,排在最前面的請求會被最優先處理。如果隊首的請求因為處理的太慢耽誤了時間,那么隊列里后面的所有請求也不得不跟着一起等待,結果就是其他的請求承擔了不應有的時間成本,造成了隊頭堵塞的現象。
- HTTPS
經典面試--HTTP和HTTPS協議的區別
-
HTTPS協議需要CA證書,費用較高;而HTTP協議不需要;
-
HTTP協議是超文本傳輸協議,信息是明文傳輸的,HTTPS則是具有安全性的SSL加密傳輸協議;
-
使用不同的連接方式,端口也不同,HTTP協議端口是80,HTTPS協議端口是443;
-
HTTP協議連接很簡單,是無狀態的;HTTPS協議是有SSL和HTTP協議構建的可進行加密傳輸、身份認證的網絡協議,比HTTP更加安全。
-
HTTPS使用了非對稱加密和對稱加密:HTTPS是在HTTP的基礎上增加了SSL層,服務器和客戶端傳輸數據前先采用非對稱加密算法生產一個秘鑰,再用這個秘鑰使用對稱加密算法加密要傳輸的數據,這樣做即保證了秘鑰的安全,有提高了數據加密效率。
-
FTP 文件服務器
-
SMTP 郵件服務器
1.2 表示層
提供各種用於應用層數據的編碼和轉換功能,確保一個系統的應用層發送的數據能被另一個系統的應用層識別
1.3 會話層
負責建立、管理和終止表示層實體之間的通信會話
1.4 傳輸層
建立了主機端到端的鏈接,傳輸層的作用是為上層協議提供端到端的可靠和透明的數據傳輸服務,包括處理差錯控制和流量控制等問題。該層向高層屏蔽了下層數據通信的細節,使高層用戶看到的只是在兩個傳輸實體間的一條主機到主機的、可由用戶控制和設定的、可靠的數據通路
- TCP
- UDP
1.5 網絡層
通過IP尋址來建立兩個節點之間的連接,為源端的運輸層送來的分組,選擇合適的路由和交換節點,正確無誤地按照地址傳送給目的端的運輸層。就是通常說的IP層
1.6 數據鏈路層
將比特組合成字節,再將字節組合成幀,使用鏈路層地址 (以太網使用MAC地址)來訪問介質,並進行差錯檢測。
網絡層與數據鏈路層的對比,通過上面的描述,我們或許可以這樣理解,網絡層是規划了數據包的傳輸路線,而數據鏈路層就是傳輸路線。不過,在數據鏈路層上還增加了差錯控制的功能。
1.7 物理層
實際最終信號的傳輸是通過物理層實現的。通過物理介質傳輸比特流。規定了電平、速度和電纜針腳。常用設備有(各種物理設備)集線器、中繼器、調制解調器、網線、雙絞線、同軸電纜。這些都是物理層的傳輸介質。
2. WebSocket
WebSocket是HTML5提供的一種瀏覽器與服務器進行全雙工通訊的網絡技術,屬於應用層協議。它基於TCP傳輸協議,並復用HTTP的握手通道。瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接, 並進行雙向數據傳輸。
解決問題
解決了半雙工通信的弊端。它最大的特點是:服務器可以向客戶端主動推動消息,客戶端也可以主動向服務器推送消息。
原理
客戶端向 WebSocket 服務器通知(notify)一個帶有所有接收者ID(recipients IDs)的事件(event),服務器接收后立即通知所有活躍的(active)客戶端,只有ID在接收者ID序列中的客戶端才會處理這個事件。
3. socketIO
3.1 question
最開始我了解的socketIO是這樣的:
Socket.IO 是一個封裝了 Websocket、基於 Node 的 JavaScript 框架,包含 client 的 JavaScript 和 server 的 Node。其屏蔽了所有底層細節,讓頂層調用非常簡單。
另外,Socket.IO 還有一個非常重要的好處。其不僅支持 WebSocket,還支持許多種輪詢機制以及其他實時通信方式,並封裝了通用的接口。這些方式包含 Adobe Flash Socket、Ajax 長輪詢、Ajax multipart streaming 、持久 Iframe、JSONP 輪詢等。換句話說,當 Socket.IO 檢測到當前環境不支持 WebSocket 時,能夠自動地選擇最佳的方式來實現網絡的實時通信。--WebSocket 與 Socket.IO
或者是這樣的:
Socket.IO 是一個庫, 說到庫其實我們都不陌生, 庫是對已有的功能進行封裝, 沒錯, 它是構建在 WebSocket 協議之上, 並提供額外的保證, 既然它是構建在 websocekt 之上, 說明它同樣具有客戶機與服務器之間延遲通信的功能. -- 《 Socket.IO》 解決 WebSocket 通信!
直到我看見官方說:
Socket.IO is not a WebSocket server. Please see ws instead. --https://github.com/socketio/socket.io/issues/3022
就有帶你存疑了,目前沒有時間去實踐,先去看看官方文檔吧
3.2 運作原理(How does that work?)
The client will try to establish a WebSocket connection if possible, and will fall back on HTTP long polling if not.
WebSocket is a communication protocol which provides a full-duplex and low-latency channel between the server and the browser. More information can be found here.
So, in the best-case scenario, provided that:
- the browser supports WebSocket (97% of all browsers in 2020)
- there is no element (proxy, firewall, …) preventing WebSocket connections between the client and the server
客戶端首先將嘗試建立 WebSocket 連接,如果沒有,將回退到 HTTP 長輪詢。可以將 Socket.IO 客戶端視為WebSocket API的"輕微"包裝器。
// 錯誤寫法:https://github.com/socketio/socket.io/issues/3022
const socket = new WebSocket('ws://localhost:3000');
socket.onopen(() => {
socket.send('Hello!');
});
socket.onmessage(data => {
console.log(data);
});
// 正確寫法
const socket = io('ws://localhost:3000');
socket.on('connect', () => {
// either with send()
socket.send('Hello!');
// or with emit() and custom event names
socket.emit('salutations', 'Hello!', { 'mr': 'john' }, Uint8Array.from([1, 2, 3, 4]));
});
// handle the event sent with socket.send()
socket.on('message', data => {
console.log(data);
});
// handle the event sent with socket.emit()
socket.on('greetings', (elem1, elem2, elem3) => {
console.log(elem1, elem2, elem3);
});
3.3 socketIO是什么
Socket.IO 是一個庫,可在瀏覽器和服務器之間實現實時、雙向和基於事件的通信。
他封裝了websocket來實現通信功能,並在websocket不可用時尋找最佳替代(例如http長輪詢HTTP long polling)
3.4 socketIO不是什么
Socket.IO 不是 WebSocket 實現。盡管 Socket.IO 確實盡可能使用 WebSocket 作為傳輸,但它會向每個數據包添加額外的元數據。這就是為什么 WebSocket 客戶端將無法成功連接到 Socket.IO 服務器,而 Socket.IO 客戶端也無法連接到普通的 WebSocket 服務器的原因。
// WARNING: the client will NOT be able to connect!
const socket = io('ws://echo.websocket.org');
這對我們解決問題有幫助嗎?反正問題沒解決,知識學到了。先這樣吧
