1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        【W(wǎng)eb技術(shù)】740- 零距離接觸 WebSocket

        共 5384字,需瀏覽 11分鐘

         ·

        2020-10-10 01:15

        作者:johnYu

        來(lái)源:https://juejin.im/post/6876301731966713869

        什么是WebSocket

        定義

        Websocket是一個(gè)持久化的網(wǎng)絡(luò)通信協(xié)議,可以在單個(gè) TCP 連接上進(jìn)行全雙工通訊,沒有了RequestResponse的概念,兩者地位完全平等,連接一旦建立,客戶端和服務(wù)端之間實(shí)時(shí)可以進(jìn)行雙向數(shù)據(jù)傳輸

        關(guān)聯(lián)和區(qū)別

        • HTTP
        1. HTTP是非持久的協(xié)議,客戶端想知道服務(wù)端的處理進(jìn)度只能通過(guò)不停地使用 Ajax進(jìn)行輪詢或者采用 long poll 的方式來(lái),但是前者對(duì)服務(wù)器壓力大,后者則會(huì)因?yàn)橐恢钡却齊esponse造成阻塞
        2. 雖然http1.1默認(rèn)開啟了keep-alive長(zhǎng)連接保持了這個(gè)TCP通道使得在一個(gè)HTTP連接中,可以發(fā)送多個(gè)Request,接收多個(gè)Response,但是一個(gè)request只能有一個(gè)response。而且這個(gè)response也是被動(dòng)的,不能主動(dòng)發(fā)起。
        3. websocket雖然是獨(dú)立于HTTP的一種協(xié)議,但是websocket必須依賴 HTTP 協(xié)議進(jìn)行一次握手(在握手階段是一樣的),握手成功后,數(shù)據(jù)就直接從 TCP通道傳輸,與 HTTP 無(wú)關(guān)了,可以用一張圖理解兩者有交集,但是并不是全部。
        • socket
        1. socket也被稱為套接字,與HTTP和WebSocket不一樣,socket不是協(xié)議,它是在程序?qū)用嫔蠈?duì)傳輸層協(xié)議(可以主要理解為TCP/IP)的接口封裝??梢岳斫鉃橐粋€(gè)能夠提供端對(duì)端的通信的調(diào)用接口(API)
        2. 對(duì)于程序員而言,其需要在 A 端創(chuàng)建一個(gè) socket 實(shí)例,并為這個(gè)實(shí)例提供其所要連接的 B 端的 IP 地址和端口號(hào),而在 B 端創(chuàng)建另一個(gè) socket 實(shí)例,并且綁定本地端口號(hào)來(lái)進(jìn)行監(jiān)聽。當(dāng) A 和 B 建立連接后,雙方就建立了一個(gè)端對(duì)端的 TCP 連接,從而可以進(jìn)行雙向通信。WebSocekt借鑒了 socket 的思想,為 client 和 server 之間提供了類似的雙向通信機(jī)制

        應(yīng)用場(chǎng)景

        WebSocket可以做彈幕、消息訂閱、多玩家游戲、協(xié)同編輯、股票基金實(shí)時(shí)報(bào)價(jià)、視頻會(huì)議、在線教育、聊天室等應(yīng)用實(shí)時(shí)監(jiān)聽服務(wù)端變化

        Websocket握手

        • Websocket握手請(qǐng)求報(bào)文:
        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

        下面是與傳統(tǒng) HTTP 報(bào)文不同的地方:

        Upgrade:?websocket
        Connection:?Upgrade

        表示發(fā)起的是 WebSocket 協(xié)議

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

        Sec-WebSocket-Key 是由瀏覽器隨機(jī)生成的,驗(yàn)證是否可以進(jìn)行Websocket通信,防止惡意或者無(wú)意的連接。

        Sec_WebSocket-Protocol 是用戶自定義的字符串,用來(lái)標(biāo)識(shí)服務(wù)所需要的協(xié)議

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

        • 服務(wù)器響應(yīng):
        HTTP/1.1?101?Switching?Protocols
        Upgrade:?websocket
        Connection:?Upgrade
        Sec-WebSocket-Accept:?HSmrc0sMlYUkAGmm5OPpG2HaGWk=
        Sec-WebSocket-Protocol:?chat

        101 響應(yīng)碼 表示要轉(zhuǎn)換協(xié)議。

        Connection: Upgrade 表示升級(jí)新協(xié)議請(qǐng)求。

        Upgrade: websocket 表示升級(jí)為 WebSocket 協(xié)議。

        Sec-WebSocket-Accept 是經(jīng)過(guò)服務(wù)器確認(rèn),并且加密過(guò)后的 Sec-WebSocket-Key。用來(lái)證明客戶端和服務(wù)器之間能進(jìn)行通信了。

        Sec-WebSocket-Protocol 表示最終使用的協(xié)議。

        至此,客戶端和服務(wù)器握手成功建立了Websocket連接,HTTP已經(jīng)完成它所有工作了,接下來(lái)就是完全按照Websocket協(xié)議進(jìn)行通信了。

        關(guān)于Websocket

        WebSocket心跳

        可能會(huì)有一些未知情況導(dǎo)致SOCKET斷開,而客戶端和服務(wù)端卻不知道,需要客戶端定時(shí)發(fā)送一個(gè)心跳 Ping 讓服務(wù)端知道自己在線,而服務(wù)端也要回復(fù)一個(gè)心跳 Pong告訴客戶端自己可用,否則視為斷開

        WebSocket狀態(tài)

        WebSocket 對(duì)象中的readyState屬性有四種狀態(tài):

        • 0: 表示正在連接
        • 1: 表示連接成功,可以通信了
        • 2: 表示連接正在關(guān)閉
        • 3: 表示連接已經(jīng)關(guān)閉,或者打開連接失敗

        WebSocket實(shí)踐

        服務(wù)端接收發(fā)送消息

        WebSocket的服務(wù)端部分,本文會(huì)以Node.js搭建

        安裝express和負(fù)責(zé)處理WebSocket協(xié)議的ws

        npm?install?express?ws

        安裝成功后的package.json:

        接著在根目錄創(chuàng)建server.js文件:

        //引入express?和?ws
        const?express?=?require('express');
        const?SocketServer?=?require('ws').Server;

        //指定開啟的端口號(hào)
        const?PORT?=?3000;

        //?創(chuàng)建express,綁定監(jiān)聽3000端口,且設(shè)定開啟后在consol中提示
        const?server?=?express().listen(PORT,?()?=>?console.log(`Listening?on?${PORT}`));

        //?將express交給SocketServer開啟WebSocket的服務(wù)
        const?wss?=?new?SocketServer({?server?});

        //當(dāng)?WebSocket?從外部連接時(shí)執(zhí)行
        wss.on('connection',?(ws)?=>?{
        ??//連接時(shí)執(zhí)行此?console?提示
        ??console.log('Client?connected');

        ??//?對(duì)message設(shè)置監(jiān)聽,接收從客戶端發(fā)送的消息
        ??ws.on('message',?(data)?=>?{
        ????//data為客戶端發(fā)送的消息,將消息原封不動(dòng)返回回去
        ????ws.send(data);
        ??});

        ??//?當(dāng)WebSocket的連接關(guān)閉時(shí)執(zhí)行
        ??ws.on('close',?()?=>?{
        ????console.log('Close?connected');
        ??});
        });

        執(zhí)行node server.js啟動(dòng)服務(wù),端口打開后會(huì)執(zhí)行監(jiān)聽時(shí)間打印提示,說(shuō)明服務(wù)啟動(dòng)成功

        在開啟WebSocket后,服務(wù)端會(huì)在message中監(jiān)聽,接收參數(shù)data捕獲客戶端發(fā)送的消息,然后使用send發(fā)送消息

        客戶端接收發(fā)送消息

        分別在根目錄創(chuàng)建index.html和index.js文件

        • index.html
        <html>
        ??<body>
        ????<script?src="./index.js">script>
        ??body>
        html>
        • index.js
        //?使用WebSocket的地址向服務(wù)端開啟連接
        let?ws?=?new?WebSocket('ws://localhost:3000');

        //?開啟后的動(dòng)作,指定在連接后執(zhí)行的事件
        ws.onopen?=?()?=>?{
        ??console.log('open?connection');
        };

        //?接收服務(wù)端發(fā)送的消息
        ws.onmessage?=?(event)?=>?{
        ??console.log(event);
        };

        //?指定在關(guān)閉后執(zhí)行的事件
        ws.onclose?=?()?=>?{
        ??console.log('close?connection');
        };

        上面的url就是本機(jī)node開啟的服務(wù)地址,分別指定連接(onopen),關(guān)閉(onclose)和消息接收(onmessage)的執(zhí)行事件,訪問(wèn)html,打印ws信息

        打印了open connection說(shuō)明連接成功,客戶端會(huì)使用onmessage處理接收

        其中event參數(shù)包含這次溝通的詳細(xì)信息,從服務(wù)端回傳的消息會(huì)在event的data屬性中。

        手動(dòng)在控制臺(tái)調(diào)用send發(fā)送消息,打印event回傳信息:

        服務(wù)端定時(shí)發(fā)送

        上面是從客戶端發(fā)送消息,服務(wù)端回傳。我們也可以通過(guò)setInterval讓服務(wù)端在固定時(shí)間發(fā)送消息給客戶端:

        server.js修改如下:

        //當(dāng)WebSocket從外部連接時(shí)執(zhí)行
        wss.on('connection',?(ws)?=>?{
        ??//連接時(shí)執(zhí)行此?console?提示
        ??console.log('Client?connected');

        +??//固定發(fā)送最新消息給客戶端
        +??const?sendNowTime?=?setInterval(()?=>?{
        +????ws.send(String(new?Date()));
        +??},?1000);

        -??//對(duì)message設(shè)置監(jiān)聽,接收從客戶端發(fā)送的消息
        -??ws.on('message',?(data)?=>?{
        -????//data為客戶端發(fā)送的消息,將消息原封不動(dòng)返回回去
        -????ws.send(data);
        -??});

        ??//當(dāng)?WebSocket的連接關(guān)閉時(shí)執(zhí)行
        ??ws.on('close',?()?=>?{
        ????console.log('Close?connected');
        ??});
        });

        客戶端連接后就會(huì)定時(shí)接收,直至我們關(guān)閉websocket服務(wù)

        多人聊天

        如果多個(gè)客戶端連接按照上面的方式只會(huì)返回各自發(fā)送的消息,先注釋服務(wù)端定時(shí)發(fā)送,開啟兩個(gè)窗口模擬:

        如果我們要讓客戶端間消息共享,也同時(shí)接收到服務(wù)端回傳的消息呢?

        我們可以使用clients找出當(dāng)前所有連接中的客戶端 ,并通過(guò)回傳消息發(fā)送到每一個(gè)客戶端 中:

        修改server.js如下:

        ...
        //當(dāng)WebSocket從外部連接時(shí)執(zhí)行
        wss.on('connection',?(ws)?=>?{
        ??//連接時(shí)執(zhí)行此?console?提示
        ??console.log('Client?connected');

        -??//固定發(fā)送最新消息給客戶端
        -??const?sendNowTime?=?setInterval(()?=>?{
        -????ws.send(String(new?Date()));
        -?},?1000);

        +??//對(duì)message設(shè)置監(jiān)聽,接收從客戶端發(fā)送的消息
        +???ws.on('message',?(data)?=>?{
        +????//取得所有連接中的?客戶端
        +????let?clients?=?wss.clients;
        +????//循環(huán),發(fā)送消息至每個(gè)客戶端
        +????clients.forEach((client)?=>?{
        +??????client.send(data);
        +????});
        +???});

        ??//當(dāng)WebSocket的連接關(guān)閉時(shí)執(zhí)行
        ??ws.on('close',?()?=>?{
        ????console.log('Close?connected');
        ??});
        });

        這樣一來(lái),不論在哪個(gè)客戶端發(fā)送消息,服務(wù)端都能將消息回傳到每個(gè)客戶端 :


        可以觀察下連接信息:

        總結(jié) ?

        紙上得來(lái)終覺淺,絕知此事要躬行,希望大家可以把理論配合上面的實(shí)例進(jìn)行消化,搭好服務(wù)端也可以直接使用測(cè)試工具好好玩耍一波

        參考文章 ?

        ?? 阮一峰-WebSocket 教程

        ?? Using WebSockets on Heroku with Node.js

        ?? WebSocket 是什么原理?為什么可以實(shí)現(xiàn)持久連接?

        擴(kuò)展 ?

        如果你覺得本文對(duì)你有幫助,可以查看我的其他文章??:

        ? Web開發(fā)應(yīng)了解的5種設(shè)計(jì)模式?

        ? 10個(gè)簡(jiǎn)單的技巧讓你的 vue.js 代碼更優(yōu)雅?

        ? Web開發(fā)應(yīng)該知道的數(shù)據(jù)結(jié)構(gòu)?

        ? 經(jīng)典面試題!從輸入U(xiǎn)RL到頁(yè)面展示你還不趕緊學(xué)起來(lái)??

        ? 淺談SSL協(xié)議的握手過(guò)程?

        1. JavaScript 重溫系列(22篇全)
        2. ECMAScript 重溫系列(10篇全)
        3. JavaScript設(shè)計(jì)模式 重溫系列(9篇全)
        4.?正則 / 框架 / 算法等 重溫系列(16篇全)
        5.?Webpack4 入門(上)||?Webpack4 入門(下)
        6.?MobX 入門(上)?||??MobX 入門(下)
        7. 80+篇原創(chuàng)系列匯總

        回復(fù)“加群”與大佬們一起交流學(xué)習(xí)~

        點(diǎn)擊“閱讀原文”查看 80+ 篇原創(chuàng)文章

        瀏覽 67
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            乱伦婷婷 | 在线色国产 | 一级AAAAA片裸体做受 | 久久激情影院 | 蜜臀久久99精品久久久酒店 | 无码人妻一区二区三区四区免费看 | 小坏蛋轻点阿受不了国产免费看 | 少妇高潮一区二区三区69 | 黑人草逼 | 北条麻妃无码观看 |