十分鐘快速瞭解websocket!!

2021-02-15 12:00:48

什麼是WebSocket

定義

Websocket是一個持久化的網路通訊協定,可以在單個 TCP 連線上進行全雙工通訊,沒有了RequestResponse的概念,兩者地位完全平等,連線一旦建立,使用者端和伺服器端之間實時可以進行雙向資料傳輸

關聯和區別

  • HTTP
  1. HTTP是非持久的協定,使用者端想知道伺服器端的處理進度只能通過不停地使用 Ajax進行輪詢或者採用 long poll 的方式來,但是前者對伺服器壓力大,後者則會因為一直等待Response造成阻塞
  2. 雖然http1.1預設開啟了keep-alive長連線保持了這個TCP通道使得在一個HTTP連線中,可以傳送多個Request,接收多個Response,但是一個request只能有一個response。而且這個response也是被動的,不能主動發起。
  3. websocket雖然是獨立於HTTP的一種協定,但是websocket必須依賴 HTTP 協定進行一次握手(在握手階段是一樣的),握手成功後,資料就直接從 TCP通道傳輸,與 HTTP 無關了,可以用一張圖理解兩者有交集,但是並不是全部。
  • socket
  1. socket也被稱為通訊端,與HTTP和WebSocket不一樣,socket不是協定,它是在程式層面上對傳輸層協定(可以主要理解為TCP/IP)的介面封裝。可以理解為一個能夠提供端對端的通訊的呼叫介面(API)
  2. 對於程式設計師而言,其需要在 A 端建立一個 socket 範例,併為這個範例提供其所要連線的 B 端的 IP 地址和埠號,而在 B 端建立另一個 socket 範例,並且繫結本地埠號來進行監聽。當 A 和 B 建立連線後,雙方就建立了一個端對端的 TCP 連線,從而可以進行雙向通訊。WebSocekt借鑑了 socket 的思想,為 client 和 server 之間提供了類似的雙向通訊機制

應用場景

WebSocket可以做彈幕、訊息訂閱、多玩家遊戲、協同編輯、股票基金實時報價、視訊會議、線上教育、聊天室等應用實時監聽伺服器端變化

Websocket握手

  • Websocket握手請求報文:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

下面是與傳統 HTTP 報文不同的地方:

Upgrade: websocket
Connection: Upgrade

表示發起的是 WebSocket 協定

Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13

Sec-WebSocket-Key 是由瀏覽器隨機生成的,驗證是否可以進行Websocket通訊,防止惡意或者無意的連線。

Sec_WebSocket-Protocol 是使用者自定義的字串,用來標識服務所需要的協定

Sec-WebSocket-Version 表示支援的 WebSocket 版本。

  • 伺服器響應:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat

101 響應碼 表示要轉換協定。

Connection: Upgrade 表示升級新協定請求。

Upgrade: websocket 表示升級為 WebSocket 協定。

Sec-WebSocket-Accept 是經過伺服器確認,並且加密過後的 Sec-WebSocket-Key。用來證明使用者端和伺服器之間能進行通訊了。

Sec-WebSocket-Protocol 表示最終使用的協定。

至此,使用者端和伺服器握手成功建立了Websocket連線,HTTP已經完成它所有工作了,接下來就是完全按照Websocket協定進行通訊了。

關於Websocket

WebSocket心跳

可能會有一些未知情況導致SOCKET斷開,而使用者端和伺服器端卻不知道,需要使用者端定時傳送一個心跳 Ping 讓伺服器端知道自己線上,而伺服器端也要回復一個心跳 Pong告訴使用者端自己可用,否則視為斷開

WebSocket狀態

WebSocket 物件中的readyState屬性有四種狀態:

  • 0: 表示正在連線
  • 1: 表示連線成功,可以通訊了
  • 2: 表示連線正在關閉
  • 3: 表示連線已經關閉,或者開啟連線失敗

WebSocket實踐

伺服器端接收傳送訊息

WebSocket的伺服器端部分,本文會以Node.js搭建

安裝express和負責處理WebSocket協定的ws

npm install express ws

安裝成功後的package.json:

接著在根目錄建立server.js檔案:

//引入express 和 ws
const express = require('express');
const SocketServer = require('ws').Server;
//指定開啟的埠號
const PORT = 3000;
// 建立express,繫結監聽3000埠,且設定開啟後在consol中提示
const server = express().listen(PORT, () => console.log(`Listening on ${PORT}`));
// 將express交給SocketServer開啟WebSocket的服務
const wss = new SocketServer({ server });
//當 WebSocket 從外部連線時執行
wss.on('connection', (ws) => {
  //連線時執行此 console 提示
  console.log('Client connected');
  // 對message設定監聽,接收從使用者端傳送的訊息
  ws.on('message', (data) => {
    //data為使用者端傳送的訊息,將訊息原封不動返回回去
    ws.send(data);
  });
  // 當WebSocket的連線關閉時執行
  ws.on('close', () => {
    console.log('Close connected');
  });
});

執行node server.js啟動服務,埠開啟後會執行監聽時間列印提示,說明服務啟動成功

在開啟WebSocket後,伺服器端會在message中監聽,接收引數data捕獲使用者端傳送的訊息,然後使用send傳送訊息

使用者端接收傳送訊息

分別在根目錄建立index.html和index.js檔案

  • index.html
<html>
  <body>
    <script src="./index.js"></script>
  </body>
</html>
  • index.js
// 使用WebSocket的地址向伺服器端開啟連線
let ws = new WebSocket('ws://localhost:3000');
// 開啟後的動作,指定在連線後執行的事件
ws.onopen = () => {
  console.log('open connection');
};
// 接收伺服器端傳送的訊息
ws.onmessage = (event) => {
  console.log(event);
};
// 指定在關閉後執行的事件
ws.onclose = () => {
  console.log('close connection');
};

上面的url就是本機node開啟的服務地址,分別指定連線(onopen),關閉(onclose)和訊息接收(onmessage)的執行事件,存取html,列印ws資訊

列印了open connection說明連線成功,使用者端會使用onmessage處理接收

其中event引數包含這次溝通的詳細資訊,從伺服器端回傳的訊息會在event的data屬性中。

手動在控制檯呼叫send傳送訊息,列印event回傳資訊:

伺服器端定時傳送

上面是從使用者端傳送訊息,伺服器端回傳。我們也可以通過setInterval讓伺服器端在固定時間傳送訊息給使用者端:

server.js修改如下:

//當WebSocket從外部連線時執行
wss.on('connection', (ws) => {
  //連線時執行此 console 提示
  console.log('Client connected');
+  //固定傳送最新訊息給使用者端
+  const sendNowTime = setInterval(() => {
+    ws.send(String(new Date()));
+  }, 1000);
-  //對message設定監聽,接收從使用者端傳送的訊息
-  ws.on('message', (data) => {
-    //data為使用者端傳送的訊息,將訊息原封不動返回回去
-    ws.send(data);
-  });
  //當 WebSocket的連線關閉時執行
  ws.on('close', () => {
    console.log('Close connected');
  });
});

使用者端連線後就會定時接收,直至我們關閉websocket服務

多人聊天

如果多個使用者端連線按照上面的方式只會返回各自傳送的訊息,先註釋伺服器端定時傳送,開啟兩個視窗模擬:

如果我們要讓使用者端間訊息共用,也同時接收到伺服器端回傳的訊息呢?

我們可以使用clients找出當前所有連線中的使用者端 ,並通過回傳訊息傳送到每一個使用者端 中:

修改server.js如下:

...
//當WebSocket從外部連線時執行
wss.on('connection', (ws) => {
  //連線時執行此 console 提示
  console.log('Client connected');
-  //固定傳送最新訊息給使用者端
-  const sendNowTime = setInterval(() => {
-    ws.send(String(new Date()));
- }, 1000);
+  //對message設定監聽,接收從使用者端傳送的訊息
+   ws.on('message', (data) => {
+    //取得所有連線中的 使用者端
+    let clients = wss.clients;
+    //迴圈,傳送訊息至每個使用者端
+    clients.forEach((client) => {
+      client.send(data);
+    });
+   });
  //當WebSocket的連線關閉時執行
  ws.on('close', () => {
    console.log('Close connected');
  });
});

這樣一來,不論在哪個使用者端傳送訊息,伺服器端都能將訊息回傳到每個使用者端 : 可以觀察下連線資訊:

總結

紙上得來終覺淺,絕知此事要躬行,希望大家可以把理論配合上面的範例進行消化,搭好伺服器端也可以直接使用測試工具好好玩耍一波

更多程式設計相關知識,請存取:!!

以上就是十分鐘快速瞭解websocket!!的詳細內容,更多請關注TW511.COM其它相關文章!