jsliang 求職系列 - 28 - WebSocket

一 目錄

不折騰的前端,和鹹魚有什麼區別css

目錄
一 目錄
二 前言
三 WebSocket 和 HTTP
3.1 (短)輪詢(Polling)
3.2 長輪詢
3.3 WebSocket
3.4 二者比對
四 Socket.io
4.1 服務端代碼
4.2 客戶端代碼
4.3 小結
五 參考文獻

二 前言

返回目錄

WebSocketHTML5 新增的一種全雙工通訊協議,客戶端和服務器基於 TCP 握手鍊接成功後,二者之間就能夠創建持久性的鏈接,實現雙向數據傳輸。html

三 WebSocket 和 HTTP

返回目錄

咱們知道 HTTP 協議是一種單向的網絡協議,在創建鏈接後,它容許客戶端向服務器發送請求資源後,服務器纔會返回相應的數據,而服務器不能主動推送數據給客戶端。前端

當時爲何這樣設計呢?假設一些不良廣告商主動將一些信息強行推送給客戶端,這不得不說是一個災難。node

因此如今咱們要作股票的實時行情,或者獲取火車票的剩餘票數等,就須要客戶端和服務器之間反覆地進行 HTTP 通信,客戶端不斷地發送 GET 請求,去獲取當前的實時數據。git

下面介紹幾種常見的方式。github

3.1 (短)輪詢(Polling)

返回目錄

短輪詢模式下,客戶端每隔一段時間向服務器發送 HTTP 請求。web

服務器收到請求後,將最新的數據發回給客戶端。面試

這種狀況下的弊端是很是明顯的:某個時間段服務器沒有更新內容,可是客戶端每隔一段時間發送請求來詢問,而這段時間內的請求是無效的。shell

這就致使了網絡帶寬的浪費。express

3.2 長輪詢

返回目錄

長輪詢模式下,客戶端向服務器發出請求,服務器並不必定會當即迴應客戶端,而是查看數據是否有更新。

若是數據更新了的話,那就當即發送數據給客戶端;若是沒有更新,那就保持這個請求,等待有新的數據到來,纔將數據返回給客戶端。

若是服務器長時間沒有更新,那麼一段時間後,請求變會超時,客戶端收到消息後,會當即發送一個新的請求給服務器。

固然這種方式也有弊端:當服務器向客戶端發送數據後,必須等待下一次請求才能將新的數據發出,這樣客戶端接收新數據就有一個最短期間隔。

若是服務器更新頻率較快,那麼就會出現問題。

3.3 WebSocket

返回目錄

基於前面的狀況,爲了完全解決服務器主動向客戶端發送數據的問題。

W3CHTML5 中提供了一種讓客戶端與服務器之間進行全雙工通信的網絡技術 WebSocket

WebSocket 基於 TCP 協議,是一種全新的、獨立的協議,與 HTTP 協議兼容卻不會融入 HTTP 協議,僅僅做爲 HTML5 的一部分。

3.4 二者比對

返回目錄

基於上面,小夥伴們大概瞭解了 WebSocket 的原因了,這裏再總結對比一下 HTTPWebSocket

  • 相同點
  1. 都須要創建 TCP 鏈接
  2. 都屬於七層協議中的應用層協議
  • 不一樣點
  1. HTTP 是單向數據流,客戶端向服務器發送請求,服務器響應並返回數據;WebSocket 鏈接後能夠實現客戶端和服務器雙向數據傳遞,除非某一端斷開鏈接。
  2. HTTPurl 使用 http// 或者 https// 開頭,而 WebSocketurl 使用 ws// 開頭

四 Socket.io

返回目錄

咱們先來看 WebSocket 的一個使用方式:

const ws = new WebSocket("ws//:xxx.xx", [protocol])

ws.onopen = () => {
  ws.send('hello')
  console.log('send')
}

ws.onmessage = (ev) =>{
  console.log(ev.data)
  ws.close()
}

ws.onclose = (ev) =>{
  console.log('close')
}

ws.onerror = (ev) =>{
  console.log('error')
}

WebSocket 實例化後,前端能夠經過上面介紹的方法進行對應的操做,看起來仍是蠻簡單的。

可是,若是想徹底搭建一個 WebSocket 服務端比較麻煩,又浪費時間。

因此:Socket.io 基於 WebSocket,加上輪詢機制以及其餘的實時通信方面的內容,實現的一個庫,它在服務端實現了實時機制的響應代碼。

也就是說,WebSocket 僅僅是 Socket.io 實現通信的一個子集。

所以,WebSocket 客戶端鏈接不上 Socket.io 服務端,Socket.io 客戶端也連不上 WebSocket 服務端。

下面咱們講解下如何實現一個簡單的聊天。

4.1 服務端代碼

返回目錄

package.json

{
  "devDependencies": {
    "express": "^4.15.2",
    "socket.io": "^2.3.0"
  }
}
index.js
let express = require('express');
let app = express();
let server = require('http').createServer(app);
let io = require('socket.io')(server);
let path = require('path');

app.use('/', (req, res, next) => {
  res.status(200).sendFile(path.resolve(__dirname, 'index.html'));
});

// 開啓 socket.io
io.on('connection', (client) => {

  // 若是有新客戶端進來,顯示 ID
  console.log(`客戶端 ID:${client.id}`);

  // 監聽客戶端的輸入信息
  client.on('channel', (data) => {
    console.log(`客戶端 ${client.id} 發送信息 ${data}`);
    io.emit('broadcast', data);
  });

  // 判斷客戶端是否關閉
  client.on('disconnect', () => {
    console.log(`客戶端關閉:${client.id}`);
  });
});

server.listen(3000, () => {
  console.log('服務監聽 3000 端口');
});

如上,咱們直接經過 npm i 安裝依賴包後,直接經過 node index.js 能夠開啓服務。

固然,若是小夥伴們想手動裝包,執行下面命令便可:

npm i express socket.io express -D

4.2 客戶端代碼

返回目錄
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Socket.io</title>
  <script src="https://cdn.bootcss.com/socket.io/2.2.0/socket.io.slim.js"></script>
</head>

<body>

  <input type="text" id="input">
  <button id="btn">send</button>
  <div id="content-wrap"></div>

  <script>
    window.onload = function () {
      let inputValue = null;

      // 鏈接 socket.io
      let socket = io('http://localhost:3000');
      // 將建立的信息以添加 p 標籤的形式展現成列表
      socket.on('broadcast', data => {
        let content = document.createElement('p');
        content.innerHTML = data;
        document.querySelector('#content-wrap').appendChild(content);
      })

      // 設置輸入框的內容
      let inputChangeHandle = (ev) => {
        inputValue = ev.target.value;
      }
      // 獲取輸入框並監聽輸入
      let inputDom = document.querySelector("#input");
      inputDom.addEventListener('input', inputChangeHandle, false);

      // 當用戶點擊發送信息的時候,進行數據交互
      let sendHandle = () => {
        socket.emit('channel', inputValue);
      }
      let btnDom = document.querySelector("#btn");
      btnDom.addEventListener('click', sendHandle, false);

      // 打頁面卸載的時候,通知服務器關閉
      window.onunload = () => {
        btnDom.removeEventListener('click', sendHandle, false);
        inputDom.removeEventListener('input', inputChangeHandle, false);
      }
    };
  </script>
</body>

</html>

4.3 小結

返回目錄

Socket.io 不只支持 WebSocket,還支持許多種輪詢機制以及其餘實時通訊方式,並封裝了通用的接口。

這些方式包含 Adobe Flash SocketAjax 長輪詢、Ajax multipart streaming、持久 IframeJSONP 輪詢等。

換句話說,當 Socket.io 檢測到當前環境不支持 WebSocket 時,可以自動地選擇最佳的方式來實現網絡的實時通訊。

這樣,咱們就對 WebSocket 有必定的瞭解,面試的時候就不慌了。

五 參考文獻

返回目錄

jsliang 的文檔庫由 梁峻榮 採用 知識共享 署名-非商業性使用-相同方式共享 4.0 國際 許可協議 進行許可。<br/>基於 https://github.com/LiangJunrong/document-library 上的做品創做。<br/>本許可協議受權以外的使用權限能夠從 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 處得到。
相關文章
相關標籤/搜索