最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

如何用JS WebSocket實(shí)現(xiàn)簡(jiǎn)單聊天

 更新時(shí)間:2021年05月07日 10:48:29   作者:淺笑·  
這篇文章主要介紹了如何用JS WebSocket實(shí)現(xiàn)簡(jiǎn)單聊天,對(duì)websocket感興趣的同學(xué),可以參考下

短輪詢(Polling)

短輪詢的實(shí)現(xiàn)思路就是瀏覽器端每隔幾秒鐘向服務(wù)器端發(fā)送 HTTP 請(qǐng)求,服務(wù)端在收到請(qǐng)求后,不論是否有數(shù)據(jù)更新,都直接進(jìn)行響應(yīng)。在服務(wù)端響應(yīng)完成,就會(huì)關(guān)閉這個(gè) TCP 連接,代碼實(shí)現(xiàn)也最簡(jiǎn)單,就是利用 XHR, 通過 setInterval 定時(shí)向后端發(fā)送請(qǐng)求,以獲取最新的數(shù)據(jù)。

setInterval(function() {
  fetch(url).then((res) => {
      // success code
  })
}, 3000);

優(yōu)點(diǎn):實(shí)現(xiàn)簡(jiǎn)單。

缺點(diǎn):會(huì)造成數(shù)據(jù)在一小段時(shí)間內(nèi)不同步和大量無效的請(qǐng)求,安全性差、浪費(fèi)資源。

長(zhǎng)輪詢(Long-Polling)

客戶端發(fā)送請(qǐng)求后服務(wù)器端不會(huì)立即返回?cái)?shù)據(jù),服務(wù)器端會(huì)阻塞請(qǐng)求連接不會(huì)立即斷開,直到服務(wù)器端有數(shù)據(jù)更新或者是連接超時(shí)才返回,客戶端才再次發(fā)出請(qǐng)求新建連接、如此反復(fù)從而獲取最新數(shù)據(jù)。大致效果如下:

客戶端代碼如下:

function async() {
    fetch(url).then((res) => {
        async();
        // success code
    }).catch(() => {
        // 超時(shí)
        async();
    })
}

優(yōu)點(diǎn):比 Polling 做了優(yōu)化,有較好的時(shí)效性。

缺點(diǎn):保持連接掛起會(huì)消耗資源,服務(wù)器沒有返回有效數(shù)據(jù),程序超時(shí)。

WebSocket

前面提到的短輪詢(Polling)和長(zhǎng)輪詢(Long-Polling), 都是先由客戶端發(fā)起 Ajax 請(qǐng)求,才能進(jìn)行通信,走的是 HTTP 協(xié)議,服務(wù)器端無法主動(dòng)向客戶端推送信息。

當(dāng)出現(xiàn)類似體育賽事、聊天室、實(shí)時(shí)位置之類的場(chǎng)景時(shí),輪詢就顯得十分低效和浪費(fèi)資源,因?yàn)橐粩喟l(fā)送請(qǐng)求,連接服務(wù)器。WebSocket 的出現(xiàn),讓服務(wù)器端可以主動(dòng)向客戶端發(fā)送信息,使得瀏覽器具備了實(shí)時(shí)雙向通信的能力。

沒用過 WebSocket 的人,可能會(huì)以為它是個(gè)什么高深的技術(shù)。其實(shí)不然,WebSocket 常用的 API 不多也很容易掌握,不過在介紹如何使用之前,讓我們先看看它的通信原理。

通信原理

當(dāng)客戶端要和服務(wù)端建立 WebSocket 連接時(shí),在客戶端和服務(wù)器的握手過程中,客戶端首先會(huì)向服務(wù)端發(fā)送一個(gè) HTTP 請(qǐng)求,包含一個(gè) Upgrade 請(qǐng)求頭來告知服務(wù)端客戶端想要建立一個(gè) WebSocket 連接。

在客戶端建立一個(gè) WebSocket 連接非常簡(jiǎn)單:

let ws = new WebSocket('ws://localhost:9000');

類似于 HTTP 和 HTTPS,ws 相對(duì)應(yīng)的也有 wss 用以建立安全連接,本地已 ws 為例。這時(shí)的請(qǐng)求頭如下:

Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: no-cache
Connection: Upgrade    // 表示該連接要升級(jí)協(xié)議
Cookie: _hjMinimizedPolls=358479; ts_uid=7852621249; CNZZDATA1259303436=1218855313-1548914234-%7C1564625892; csrfToken=DPb4RhmGQfPCZnYzUCCOOade; jsESSIONID=67376239124B4355F75F1FC87C059F8D; _hjid=3f7157b6-1aa0-4d5c-ab9a-45eab1e6941e; acw_tc=76b20ff415689655672128006e178b964c640d5a7952f7cb3c18ddf0064264
Host: localhost:9000
Origin: http://localhost:9000
Pragma: no-cache
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: 5fTJ1LTuh3RKjsJxydyifQ==        // 與響應(yīng)頭 Sec-WebSocket-Accept 相對(duì)應(yīng)
Sec-WebSocket-Version: 13    // 表示 websocket 協(xié)議的版本
Upgrade: websocket    // 表示要升級(jí)到 websocket 協(xié)議
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (Khtml, like Gecko) Chrome/76.0.3809.132 Safari/537.36

響應(yīng)頭如下:

Connection: Upgrade
Sec-WebSocket-Accept: ZUip34t+bCjhkvxxwhmdEOyx9hE=
Upgrade: websocket

此時(shí)響應(yīng)行(General)中可以看到狀態(tài)碼 status code 是 101 Switching Protocols, 表示該連接已經(jīng)從 HTTP 協(xié)議轉(zhuǎn)換為 WebSocket 通信協(xié)議。 轉(zhuǎn)換成功之后,該連接并沒有中斷,而是建立了一個(gè)全雙工通信,后續(xù)發(fā)送和接收消息都會(huì)走這個(gè)連接通道。

注意,請(qǐng)求頭中有個(gè) Sec-WebSocket-Key 字段,和相應(yīng)頭中的 Sec-WebSocket-Accept 是配套對(duì)應(yīng)的,它的作用是提供了基本的防護(hù),比如惡意的連接或者無效的連接。Sec-WebSocket-Key 是客戶端隨機(jī)生成的一個(gè) base64 編碼,服務(wù)器會(huì)使用這個(gè)編碼,并根據(jù)一個(gè)固定的算法:

GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";    //  一個(gè)固定的字符串
accept = base64(sha1(key + GUID));    // key 就是 Sec-WebSocket-Key 值,accept 就是 Sec-WebSocket-Accept 值

其中 GUID 字符串是RFC6455官方定義的一個(gè)固定字符串,不得修改。

客戶端拿到服務(wù)端響應(yīng)的 Sec-WebSocket-Accept 后,會(huì)拿自己之前生成的 Sec-WebSocket-Key 用相同算法算一次,如果匹配,則握手成功。然后判斷 HTTP Response 狀態(tài)碼是否為 101(切換協(xié)議),如果是,則建立連接,大功告成。

實(shí)現(xiàn)簡(jiǎn)單單聊

下面來實(shí)現(xiàn)一個(gè)純文字消息類型的一對(duì)一聊天(單聊)功能,廢話不多說,直接上代碼,注意看注釋。

客戶端:

function connectWebsocket() {
    ws = new WebSocket('ws://localhost:9000');
    // 監(jiān)聽連接成功
    ws.onopen = () => {
        console.log('連接服務(wù)端WebSocket成功');
        ws.send(JSON.stringify(msgData));    // send 方法給服務(wù)端發(fā)送消息
    };

    // 監(jiān)聽服務(wù)端消息(接收消息)
    ws.onmessage = (msg) => {
        let message = JSON.parse(msg.data);
        console.log('收到的消息:', message)
        elUl.innerhtml += `<li>小秋:${message.content}</li>`;
    };

    // 監(jiān)聽連接失敗
    ws.onerror = () => {
        console.log('連接失敗,正在重連...');
        connectWebsocket();
    };

    // 監(jiān)聽連接關(guān)閉
    ws.onclose = () => {
        console.log('連接關(guān)閉');
    };
};
connectWebsocket();

從上面可以看到 WebSocket 實(shí)例的 API 很容易理解,簡(jiǎn)單好用,通過 send() 方法可以發(fā)送消息,onmessage 事件用來接收消息,然后對(duì)消息進(jìn)行處理顯示在頁(yè)面上。 當(dāng) onerror 事件(監(jiān)聽連接失?。┯|發(fā)時(shí),最好進(jìn)行執(zhí)行重連,以保持連接不中斷。

服務(wù)端 Node: (這里使用ws庫(kù))

const path = require('path');
const express = require('express');
const app = express();
const server = require('http').Server(app);
const WebSocket = require('ws');

const wss = new WebSocket.Server({ server: server });

wss.on('connection', (ws) => { 

  // 監(jiān)聽客戶端發(fā)來的消息
  ws.on('message', (message) => {
    console.log(wss.clients.size);
    let msgData = JSON.parse(message);   
    if (msgData.type === 'open') {
      // 初始連接時(shí)標(biāo)識(shí)會(huì)話
      ws.sessionId = `${msgData.fromUserId}-${msgData.toUserId}`;
    } else {
      let sessionId = `${msgData.toUserId}-${msgData.fromUserId}`;
      wss.clients.forEach(client => {
        if (client.sessionId === sessionId) {
          client.send(message);     // 給對(duì)應(yīng)的客戶端連接發(fā)送消息
        }
      })  
    }
  })

  // 連接關(guān)閉
  ws.on('close', () => {
    console.log('連接關(guān)閉');  
  });
});

同理,服務(wù)端也有對(duì)應(yīng)的發(fā)送和接收的方法。完整示例代碼見這里

這樣瀏覽器和服務(wù)端就可以愉快的發(fā)送消息了,效果如下:

其中綠色箭頭表示發(fā)出的消息,紅色箭頭表示收到的消息。

心跳?;?/h3>

在實(shí)際使用 WebSocket 中,長(zhǎng)時(shí)間不通消息可能會(huì)出現(xiàn)一些連接不穩(wěn)定的情況,這些未知情況導(dǎo)致的連接中斷會(huì)影響客戶端與服務(wù)端之前的通信,

為了防止這種的情況的出現(xiàn),有一種心跳?;畹姆椒ǎ嚎蛻舳司拖裥奶粯用扛艄潭ǖ臅r(shí)間發(fā)送一次 ping,來告訴服務(wù)器,我還活著,而服務(wù)器也會(huì)返回 pong,來告訴客戶端,服務(wù)器還活著。ping/pong 其實(shí)是一條與業(yè)務(wù)無關(guān)的假消息,也稱為心跳包。

可以在連接成功之后,每隔一個(gè)固定時(shí)間發(fā)送心跳包,比如 60s:

setInterval(() => {
    ws.send('這是一條心跳包消息');
}, 60000)

總結(jié)

通過上面的介紹,大家應(yīng)該對(duì) WebSocket 有了一定認(rèn)識(shí),其實(shí)并不神秘,這里對(duì)文章內(nèi)容簡(jiǎn)單總結(jié)一下。當(dāng)創(chuàng)建 WebSocket 實(shí)例的時(shí)候,會(huì)發(fā)一個(gè) HTTP 請(qǐng)求,請(qǐng)求報(bào)文中有個(gè)特殊的字段 Upgrade,然后這個(gè)連接會(huì)由 HTTP 協(xié)議轉(zhuǎn)換為 WebSocket 協(xié)議,這樣客戶端和服務(wù)端建立了全雙工通信,通過 WebSocket 的 send 方法和 onmessage 事件就可以通過這條通信連接交換信息。

以上就是如何用JS WebSocket實(shí)現(xiàn)簡(jiǎn)單聊天的詳細(xì)內(nèi)容,更多關(guān)于WebSocket的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • js下為表格內(nèi)部動(dòng)態(tài)添加行的代碼

    js下為表格內(nèi)部動(dòng)態(tài)添加行的代碼

    最近的一個(gè)項(xiàng)目中在保存表單數(shù)據(jù)時(shí),要用到一個(gè)動(dòng)態(tài)添加行的功能。平時(shí)動(dòng)態(tài)添加行只是在表格的最下面添加,現(xiàn)在在表格中間動(dòng)態(tài)添加行,而且表格內(nèi)部是包含并且單元格的,其實(shí)很簡(jiǎn)單,下面貼出代碼。
    2010-06-06
  • 聊一聊對(duì)js包裝對(duì)象的理解

    聊一聊對(duì)js包裝對(duì)象的理解

    JavaScript中的基本類型通過自動(dòng)裝箱和拆箱機(jī)制,可以臨時(shí)轉(zhuǎn)換為對(duì)應(yīng)的包裝對(duì)象,以訪問屬性和方法,了解包裝對(duì)象的臨時(shí)性和手動(dòng)創(chuàng)建方式有助于編寫更高效和正確的代碼,感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • 淺談ES6中箭頭函數(shù)與普通函數(shù)的區(qū)別

    淺談ES6中箭頭函數(shù)與普通函數(shù)的區(qū)別

    箭頭函數(shù)是ES6中一種新的函數(shù)的表達(dá)式,本文就來介紹一下ES6中箭頭函數(shù)與普通函數(shù)的區(qū)別,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2023-05-05
  • JS實(shí)現(xiàn)復(fù)制功能

    JS實(shí)現(xiàn)復(fù)制功能

    本文主要介紹了JS實(shí)現(xiàn)復(fù)制功能的實(shí)例,具有很好的參考價(jià)值,下面跟著小編一起來看下吧
    2017-03-03
  • 一文詳解JavaScript數(shù)組對(duì)象和字符串對(duì)象

    一文詳解JavaScript數(shù)組對(duì)象和字符串對(duì)象

    這篇文章主要介紹了JavaScript數(shù)組對(duì)象和字符串對(duì)象,文章中有詳細(xì)的代碼示例,對(duì)學(xué)習(xí)或工作有一定的幫助,需要的小伙伴可以參考一下
    2023-04-04
  • UniApp與WebView雙向通信及數(shù)據(jù)傳輸超詳細(xì)講解

    UniApp與WebView雙向通信及數(shù)據(jù)傳輸超詳細(xì)講解

    這篇文章主要介紹了UniApp與WebView雙向通信及數(shù)據(jù)傳輸?shù)南嚓P(guān)資料,詳細(xì)講解了UniApp與WebView的通信原理、方法對(duì)比、數(shù)據(jù)傳輸實(shí)戰(zhàn)、調(diào)試技巧、性能優(yōu)化策略及技術(shù)風(fēng)險(xiǎn)控制,通過合理選型和優(yōu)化,需要的朋友可以參考下
    2025-04-04
  • js獲取當(dāng)前頁(yè)面路徑示例講解

    js獲取當(dāng)前頁(yè)面路徑示例講解

    本篇文章主要是對(duì)js獲取當(dāng)前頁(yè)面路徑的示例代碼進(jìn)行了介紹,需要的朋友可以過來參考下,希望對(duì)大家有所幫助
    2014-01-01
  • javascript實(shí)現(xiàn)倒計(jì)時(shí)跳轉(zhuǎn)頁(yè)面

    javascript實(shí)現(xiàn)倒計(jì)時(shí)跳轉(zhuǎn)頁(yè)面

    本文給大家介紹了如何使用javascript實(shí)現(xiàn)倒計(jì)時(shí)跳轉(zhuǎn)到其他頁(yè)面的方法以及實(shí)現(xiàn)原理,非常的簡(jiǎn)單實(shí)用,有需要的小伙伴可以參考下。
    2016-01-01
  • php實(shí)例分享之實(shí)現(xiàn)顯示網(wǎng)站運(yùn)行時(shí)間

    php實(shí)例分享之實(shí)現(xiàn)顯示網(wǎng)站運(yùn)行時(shí)間

    這篇文章主要介紹了php實(shí)現(xiàn)顯示網(wǎng)站運(yùn)行時(shí)間,需要的朋友可以參考下
    2014-05-05
  • JavaScript對(duì)象數(shù)組排序函數(shù)及六個(gè)用法

    JavaScript對(duì)象數(shù)組排序函數(shù)及六個(gè)用法

    本文給大家分享一個(gè)用于數(shù)組或者對(duì)象的排序的函數(shù)。該函數(shù)可以以任意深度的數(shù)組或者對(duì)象的值作為排序基數(shù)對(duì)數(shù)組或的元素進(jìn)行排序
    2015-12-12

最新評(píng)論

北流市| 靖边县| 孟州市| 张北县| 内黄县| 平远县| 什邡市| 尤溪县| 邯郸市| 香格里拉县| 贡嘎县| 新泰市| 静宁县| 马关县| 科尔| 潢川县| 余庆县| 广昌县| 哈巴河县| 盐津县| 新疆| 昌都县| 绍兴市| 赫章县| 宁化县| 广宗县| 苏尼特左旗| 睢宁县| 西峡县| 沧州市| 铜鼓县| 惠安县| 泽普县| 九龙坡区| 洪湖市| 侯马市| 行唐县| 白朗县| 满洲里市| 天台县| 河东区|