你不知道WebSocket嗎?

什麼是WebSocket?

WebSocket是一種在單個TCP鏈接上進行全雙工通訊的協議。這裏咱們發現了一個有趣的詞:」全雙工」,那咱們就來簡單瞭解下通訊方式有哪些!javascript

單工

通訊雙方中,一方固定爲發送端,一方則固定爲接收端。信息只能沿一個方向傳輸。 例如計算機與打印機之間的通訊是單工模式html

說的簡單些就是:我打你你只能忍着!前端

半雙工

容許數據在兩個方向上傳輸,可是同一時間數據只能在一個方向上傳輸,其其實是切換的單工。 例如HTTP協議:客戶端向服務器發送請求(單向的),而後服務器響應請求(單向的)java

說的簡單些就是:我打你,你忍完後能夠打我,我忍着…node

全雙工

容許數據在兩個方向上同時傳輸。 例如手機通話,WebSocket就是這個樣子!web

說的簡單些就是:兩我的同時能夠互相打對方ajax

說了這麼多其實目的就是讓你們知道,WebSocket是支持雙向通訊的!express

雙向通訊的優勢

爲何要支持雙向通訊?單向通訊有什麼問題?仍是從HTTP提及,咱們知道HTTP協議是半雙工的,並且服務器不能主動推送消息給瀏覽器!這個就是他的缺陷。倘若我但願實現一個股票交易系統,可能股價每秒鐘都有變化,可是價格變化了如何通知咱們的客戶端?跨域

我們來看看之前是怎麼實現的!瀏覽器

輪詢

什麼叫輪詢?就是不停的輪番詢問!說的直白些就是客戶端按期發送請求給服務端。

image

短輪詢

配段代碼,Talk is cheap,show me your code.

const express = require("express");
const app = express();
// express 靜態服務中間件用來返回靜態文件
app.use(express.static(__dirname));
// 當前價格是100元
let currentPrice = 100;
// 獲取最新價格接口
app.get("/getPrice", (req, res, next) => {
    res.send('¥'+currentPrice * Math.random());
});
app.listen(3000);
複製代碼

客戶端不停的發送請求,去服務端獲取最新價格。

<div>當前交易價格: <span id="price"></span></div>
<script> setInterval(() => { fetch('/getPrice'). then(res=>res.text()). then(data=>price.innerHTML = data) }, 1000); </script>
複製代碼

很快咱們就看出了這樣編寫代碼的缺陷!若是數據變化的不快呢,那就會發送不少無心義的請求。每次發送請求都會有HTTP的Header會消耗大量流量,同時也會消耗CPU的利用率!

長輪詢

長輪詢是對短輪詢的改進版,就是當第一個請求回來時再發送下一個請求!

(function poll(){
    fetch('/getPrice').
    then(res=>res.text()).
    then(data=>{price.innerHTML = data;poll()})
})()
複製代碼

問題依舊是顯而易見的!若是服務端數據變化很快,那麼請求數目會更多;若是變化很慢,可能ajax會出現超時的問題。

Iframe方式

咱們並不但願每次都建立一個新的請求,此時就可使用Iframe來實現長鏈接

app.get("/getPrice", (req, res, next) => {
    setInterval(()=>{
        // 不能使用end 不然會中斷請求,咱們要實現的是長鏈接
        res.write(` <script> parent.document.getElementById('price').innerHTML = ${currentPrice * Math.random()} </script> `);
    },1000);
});
複製代碼
<body>
    <div>當前交易價格: <span id="price"></span></div>
    <iframe src="/getPrice" frameborder="0"></iframe>
</body>
複製代碼

如今確實能夠利用Iframe實現了長鏈接通訊,可是頁面的狀態一直是加載態!

EventSource流

EventSource 接口用於接收服務器發送的事件。它經過HTTP鏈接到一個服務器,以text/event-stream 格式接收事件, 不關閉鏈接。

<div>當前交易價格: <span id="price"></span></div>
<script> const eventSource = new EventSource('/getPrice'); eventSource.onmessage = function(e){ // 拿到接受到的數據 price.innerHTML = e.data; } </script>
複製代碼
app.get("/getPrice", (req, res, next) => {
    res.header('Content-Type','text/event-stream',);
    timer = setInterval(()=>{
        res.write( // 發送message事件 \n\n表示當前的event-stream通訊結束
            `event:message\nid:${id++}\ndata:${currentPrice*Math.random()}\n\n`
        );
    },1000);
    res.on('close',()=>{
        clearInterval(timer);
    });
});
複製代碼

固然這種方式依舊是單向的,主要是服務端向客戶端推送數據。而且兼容性也不是很美麗~

WebSocket

終於等到你! 雙向通訊的WebSocket讓你欲罷不能!

image
WebSocket讓客戶端和服務器保有一個持久的鏈接,兩邊能夠在任意時間開始發送數據!它是基於TCP協議的:

先來聊聊WebSocket的優點!

  • http協議不支持雙向通訊 -> 我支持雙向通訊
  • http協議數據包頭部較大 -> 個人header很小!我最少只需兩個字節
  • http不支持跨域 -> 我支持跨域,哈哈!

ws模塊

ws: a Node.js WebSocket library,ok就是在node中可使用的WebSocket庫!

安裝ws模塊

yarn add ws
複製代碼

服務端開啓WebSocket服務

const WebSocketServer = require('ws').Server;
const ws = new WebSocketServer({port:8888});
ws.on('connection',(socket)=>{ // socket連接個人那我的
    console.log('服務端:有人連接我!');
    socket.on('message',(data)=>{
        console.log(data); // 收到客戶端發來的消息
        socket.send('我是服務端'); // 給客戶端發消息
    });
});
複製代碼

客戶端連接8888端口的ws服務!

const socket = new WebSocket('ws://localhost:8888');
socket.onopen = function(){ // 連接成功後,發送消息
    console.log('客戶端:連接成功');
    socket.send('我是客戶端');
}
socket.onmessage = function(e){ // 監聽客戶端發來的信息
    console.log(e.data);
}
複製代碼

客戶端和服務端能夠開心的互相通訊啦!

socket.io

socket.io是一個WebSocket庫,包括了客戶端的js和服務器端的nodejs,剛纔是否是高興的太早了而忘記了兼容性問題?沒錯socket.io就是幫你解決自動根據瀏覽器從WebSocket、AJAX長輪詢、Iframe流等等各類方式中選擇最佳的方式來實現網絡實時應用!

安裝socket.io模塊

yarn add socket.io
複製代碼

經過socket.io創建連接

const express = require("express");
const app = express();
app.use(express.static(__dirname))
const server = require('http').createServer(app); // app自己就是監聽函數
// socket 須要藉助http服務
const io = require('socket.io')(server);
// 劃分路徑 /
io.of('/').on('connection',function(socket){
    console.log('連接成功')
    socket.on('message',function(msg){
        console.log(msg);
        socket.send('我是服務端');
    });
});
// 監聽3000 端口
server.listen(3000);
複製代碼
// 默認會像瀏覽器中注入socket.io.js腳本
<script src="/socket.io/socket.io.js"></script>
<script>
    const socket = io.connect('/');
    socket.on('connect',()=>{
        console.log('連接成功');
        socket.send('我是客戶端');
    });
    // 接收到消息後打印出來
    socket.on('message',(data)=>{
        console.log(data);
    });
</script>
複製代碼

咱們有了socket.io實現雙向通訊是否是很簡單!


以爲本文對你有幫助嗎?請分享給更多人

關注「前端優選」加星標,提高前端技能

關注公衆號,得到更多前端高級技能

加我微信:webyouxuan

相關文章
相關標籤/搜索