成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

手摸手教你使用WebSocket[其實WebSocket也不難]

jhhfft / 3413人閱讀

摘要:缺點可能會導致丟失數(shù)據(jù)在斷開重連的這段時間中,恰好雙方正在通信。博客前端積累文檔公眾號以上參考資料教程理解心跳及重連機制協(xié)議分鐘從入門到精通

在本篇文章之前,WebSocket很多人聽說過,沒見過,沒用過,以為是個很高大上的技術,實際上這個技術并不神秘,可以說是個很容易就能掌握的技術,希望在看完本文之后,馬上把文中的栗子拿出來自己試一試,實踐出真知。

游泳、健身了解一下:博客、前端積累文檔、公眾號、GitHub
WebSocket解決了什么問題:

客戶端(瀏覽器)和服務器端進行通信,只能由客戶端發(fā)起ajax請求,才能進行通信,服務器端無法主動向客戶端推送信息。

當出現(xiàn)類似體育賽事、聊天室、實時位置之類的場景時,客戶端要獲取服務器端的變化,就只能通過輪詢(定時請求)來了解服務器端有沒有新的信息變化。

輪詢效率低,非常浪費資源(需要不斷發(fā)送請求,不停鏈接服務器)

WebSocket的出現(xiàn),讓服務器端可以主動向客戶端發(fā)送信息,使得瀏覽器具備了實時雙向通信的能力,這就是WebSocket解決的問題

一個超簡單的栗子:

新建一個html文件,將本栗子找個地方跑一下試試,即可輕松入門WebSocket

function socketConnect(url) {
    // 客戶端與服務器進行連接
    let ws = new WebSocket(url); // 返回`WebSocket`對象,賦值給變量ws
    // 連接成功回調(diào)
    ws.onopen = e => {
        console.log("連接成功", e)
        ws.send("我發(fā)送消息給服務端"); // 客戶端與服務器端通信
    }
    // 監(jiān)聽服務器端返回的信息
    ws.onmessage = e => {
        console.log("服務器端返回:", e.data)
        // do something
    }
    return ws; // 返回websocket對象
}
let wsValue = socketConnect("ws://121.40.165.18:8800"); // websocket對象

上述栗子中WebSocket的接口地址出自:WebSocket 在線測試,在開發(fā)的時候也可以用于測試后端給的地址是否可用。

webSocket的class類:

當項目中很多地方使用WebSocket,把它封成一個class類,是更好的選擇。

下面的栗子,做了非常詳細的注釋,建個html文件也可直接使用,websocket的常用API都放進去了。

下方注釋的代碼,先不用管,涉及到心跳機制,用于保持WebSocket連接的

class WebSocketClass {
    /**
     * @description: 初始化實例屬性,保存參數(shù)
     * @param {String} url ws的接口
     * @param {Function} msgCallback 服務器信息的回調(diào)傳數(shù)據(jù)給函數(shù)
     * @param {String} name 可選值 用于區(qū)分ws,用于debugger
     */
    constructor(url, msgCallback, name = "default") {
        this.url = url;
        this.msgCallback = msgCallback;
        this.name = name;
        this.ws = null;  // websocket對象
        this.status = null; // websocket是否關閉
    }
    /**
     * @description: 初始化 連接websocket或重連webSocket時調(diào)用
     * @param {*} 可選值 要傳的數(shù)據(jù)
     */
    connect(data) {
        // 新建 WebSocket 實例
        this.ws = new WebSocket(this.url);
        this.ws.onopen = e => {
            // 連接ws成功回調(diào)
            this.status = "open";
            console.log(`${this.name}連接成功`, e)
            // this.heartCheck();
            if (data !== undefined) {
                // 有要傳的數(shù)據(jù),就發(fā)給后端
                return this.ws.send(data);
            }
        }
        // 監(jiān)聽服務器端返回的信息
        this.ws.onmessage = e => {
            // 把數(shù)據(jù)傳給回調(diào)函數(shù),并執(zhí)行回調(diào)
            // if (e.data === "pong") {
            //     this.pingPong = "pong"; // 服務器端返回pong,修改pingPong的狀態(tài)
            // }
            return this.msgCallback(e.data);
        }
        // ws關閉回調(diào)
        this.ws.onclose = e => {
            this.closeHandle(e); // 判斷是否關閉
        }
        // ws出錯回調(diào)
        this.onerror = e => {
            this.closeHandle(e); // 判斷是否關閉
        }
    }
    // heartCheck() {
    //     // 心跳機制的時間可以自己與后端約定
    //     this.pingPong = "ping"; // ws的心跳機制狀態(tài)值
    //     this.pingInterval = setInterval(() => {
    //         if (this.ws.readyState === 1) {
    //             // 檢查ws為鏈接狀態(tài) 才可發(fā)送
    //             this.ws.send("ping"); // 客戶端發(fā)送ping
    //         }
    //     }, 10000)
    //     this.pongInterval = setInterval(() => {
    //         this.pingPong = false;
    //         if (this.pingPong === "ping") {
    //             this.closeHandle("pingPong沒有改變?yōu)閜ong"); // 沒有返回pong 重啟webSocket
    //         }
    //         // 重置為ping 若下一次 ping 發(fā)送失敗 或者pong返回失敗(pingPong不會改成pong),將重啟
    //         console.log("返回pong")
    //         this.pingPong = "ping"
    //     }, 20000)
    // }
    // 發(fā)送信息給服務器
    sendHandle(data) {
        console.log(`${this.name}發(fā)送消息給服務器:`, data)
        return this.ws.send(data);
    }
    closeHandle(e = "err") {
        // 因為webSocket并不穩(wěn)定,規(guī)定只能手動關閉(調(diào)closeMyself方法),否則就重連
        if (this.status !== "close") {
            console.log(`${this.name}斷開,重連websocket`, e)
            // if (this.pingInterval !== undefined && this.pongInterval !== undefined) {
            //     // 清除定時器
            //     clearInterval(this.pingInterval);
            //     clearInterval(this.pongInterval);
            // }
            this.connect(); // 重連
        } else {
            console.log(`${this.name}websocket手動關閉`)
        }
    }
    // 手動關閉WebSocket
    closeMyself() {
        console.log(`關閉${this.name}`)
        this.status = "close";
        return this.ws.close();
    }
}
function someFn(data) {
    console.log("接收服務器消息的回調(diào):", data);
}
// const wsValue = new WebSocketClass("ws://121.40.165.18:8800", someFn, "wsName"); // 這個鏈接一天只能發(fā)送消息50次
const wsValue = new WebSocketClass("wss://echo.websocket.org", someFn, "wsName"); // 阮一峰老師教程鏈接
wsValue.connect("立即與服務器通信"); // 連接服務器
// setTimeout(() => {
//     wsValue.sendHandle("傳消息給服務器")
// }, 1000);
// setTimeout(() => {
//     wsValue.closeMyself(); // 關閉ws
// }, 10000)

栗子里面我直接寫在了一起,可以把class放在一個js文件里面,export出去,然后在需要用的地方再import進來,把參數(shù)傳進去就可以用了。

WebSocket不穩(wěn)定

WebSocket并不穩(wěn)定,在使用一段時間后,可能會斷開連接,貌似至今沒有一個為何會斷開連接的公論,所以我們需要讓WebSocket保持連接狀態(tài),這里推薦兩種方法。

WebSocket設置變量,判斷是否手動關閉連接:

class類中就是用的這種方式:設置一個變量,在webSocket關閉/報錯的回調(diào)中,判斷是不是手動關閉的,如果不是的話,就重新連接,這樣做的優(yōu)缺點如下:

優(yōu)點:請求較少(相對于心跳連接),易設置。

缺點:可能會導致丟失數(shù)據(jù),在斷開重連的這段時間中,恰好雙方正在通信。

WebSocket心跳機制:
因為第一種方案的缺點,并且可能會有其他一些未知情況導致斷開連接而沒有觸發(fā)Error或Close事件。這樣就導致實際連接已經(jīng)斷開了,而客戶端和服務端卻不知道,還在傻傻的等著消息來。

然后聰明的程序猿們想出了一種叫做心跳機制的解決方法:

客戶端就像心跳一樣每隔固定的時間發(fā)送一次ping,來告訴服務器,我還活著,而服務器也會返回pong,來告訴客戶端,服務器還活著。

具體的實現(xiàn)方法,在上面class的注釋中,將其打開,即可看到效果。

關于WebSocket

怕一開始就堆太多文字性的內(nèi)容,把各位嚇跑了,現(xiàn)在大家已經(jīng)會用了,我們再回頭來看看WebSocket的其他知識點。

WebSocket的當前狀態(tài):WebSocket.readyState

下面是WebSocket.readyState的四個值(四種狀態(tài)):

0: 表示正在連接

1: 表示連接成功,可以通信了

2: 表示連接正在關閉

3: 表示連接已經(jīng)關閉,或者打開連接失敗

我們可以利用當前狀態(tài)來做一些事情,比如上面栗子中當WebSocket鏈接成功后,才允許客戶端發(fā)送ping

if (this.ws.readyState === 1) {
    // 檢查ws為鏈接狀態(tài) 才可發(fā)送
    this.ws.send("ping"); // 客戶端發(fā)送ping
}
WebSocket還可以發(fā)送/接收 二進制數(shù)據(jù)

這里我也沒有試過,我是看阮一峰老師的WebSocket教程才知道有這么個東西,有興趣的可以再去谷歌,大家知道一下就可以。

二進制數(shù)據(jù)包括:blob對象和Arraybuffer對象,所以我們需要分開來處理。

    // 接收數(shù)據(jù)
ws.onmessage = function(event){
    if(event.data instanceof ArrayBuffer){
        // 判斷 ArrayBuffer 對象
    }
    
    if(event.data instanceof Blob){
        // 判斷 Blob 對象
    }
}

// 發(fā)送 Blob 對象的例子
let file = document.querySelector("input[type="file"]").files[0];
ws.send(file);

// 發(fā)送 ArrayBuffer 對象的例子
var img = canvas_context.getImageData(0, 0, 400, 320);
var binary = new Uint8Array(img.data.length);
for (var i = 0; i < img.data.length; i++) {
    binary[i] = img.data[i];
}
ws.send(binary.buffer);

如果你要發(fā)送的二進制數(shù)據(jù)很大的話,如何判斷發(fā)送完畢:

webSocket.bufferedAmount屬性,表示還有多少字節(jié)的二進制數(shù)據(jù)沒有發(fā)送出去:

var data = new ArrayBuffer(10000000);
socket.send(data);
if (socket.bufferedAmount === 0) {
    // 發(fā)送完畢
} else {
    // 發(fā)送還沒結束
}

上述栗子出自阮一峰老師的WebSocket教程

WebSocket的優(yōu)點:

最后再吹一波WebSocket:

雙向通信(一開始說的,也是最重要的一點)。

數(shù)據(jù)格式比較輕量,性能開銷小,通信高效

協(xié)議控制的數(shù)據(jù)包頭部較小,而HTTP協(xié)議每次通信都需要攜帶完整的頭部

更好的二進制支持

沒有同源限制,客戶端可以與任意服務器通信

與 HTTP 協(xié)議有著良好的兼容性。默認端口也是80和443,并且握手階段采用 HTTP 協(xié)議,因此握手時不容易屏蔽,能通過各種 HTTP 代理服務器

結語

看了本文之后,如果還是有點迷糊的話,一定要把文中的兩個栗子,新建個html文件跑起來,自己鼓搗鼓搗一下。不然讀多少博客/教程都沒有用,實踐才出真知,切勿紙上談兵。

希望看完的朋友可以點個喜歡/關注,您的支持是對我最大的鼓勵。

博客、前端積累文檔、公眾號、GitHub

以上2018.10.22

參考資料:

WebSocket 教程

理解WebSocket心跳及重連機制

WebSocket協(xié)議:5分鐘從入門到精通

文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/98690.html

相關文章

  • 手摸教你用 js 寫一個 js 解釋器

    摘要:手摸手教你用寫一個解釋器用來編譯看起來是個高大上的東西,實際原理其實很簡單,無非就是利用對象屬性可以用字符串表示這個特性來實現(xiàn)的黑魔法罷了。 手摸手教你用 js 寫一個 js 解釋器 用 js 來 編譯 js 看起來是個高大上的東西,實際原理其實很簡單,無非就是利用 js 對象屬性可以用字符串表示 這個特性來實現(xiàn)的黑魔法罷了。之所以看起來那么 深奧, 大概是由于網(wǎng)上現(xiàn)有的教程,都是動不...

    hss01248 評論0 收藏0
  • 手摸教你用canvas實現(xiàn)給圖片添加平鋪水印

    摘要:最近項目中遇到一個需求,需要把一張圖片加上平鋪的水印類似這樣的效果首先想到的是用完成這種功能,因為我之前也沒有接觸過,所以做這個功能的時候,就是一步一步的摸索中學習,過程還是挺的,接下來跟我一步步來實現(xiàn)這個功能以及發(fā)現(xiàn)一些的坑吧。 最近項目中遇到一個需求,需要把一張圖片加上平鋪的水印 類似這樣的效果showImg(https://segmentfault.com/img/remote/...

    崔曉明 評論0 收藏0
  • 前端之從零開始系列

    摘要:只有動手,你才能真的理解作者的構思的巧妙只有動手,你才能真正掌握一門技術持續(xù)更新中項目地址求求求源碼系列跟一起學如何寫函數(shù)庫中高級前端面試手寫代碼無敵秘籍如何用不到行代碼寫一款屬于自己的類庫原理講解實現(xiàn)一個對象遵循規(guī)范實戰(zhàn)手摸手,帶你用擼 Do it yourself!!! 只有動手,你才能真的理解作者的構思的巧妙 只有動手,你才能真正掌握一門技術 持續(xù)更新中…… 項目地址 https...

    Youngdze 評論0 收藏0

發(fā)表評論

0條評論

jhhfft

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<