<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          零距離接觸 WebSocket

          共 5188字,需瀏覽 11分鐘

           ·

          2020-10-01 09:52

          作者:johnYu

          來源:https://juejin.im/post/6876301731966713869

          什么是WebSocket

          定義

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

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

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

          應(yīng)用場景

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

          Websocket握手

          • Websocket握手請求報文:
          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 報文不同的地方:

          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通信,防止惡意或者無意的連接。

          Sec_WebSocket-Protocol 是用戶自定義的字符串,用來標(biāo)識服務(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 表示升級新協(xié)議請求。

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

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

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

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

          關(guān)于Websocket

          WebSocket心跳

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

          WebSocket狀態(tài)

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

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

          WebSocket實(shí)踐

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

          WebSocket的服務(wù)端部分,本文會以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;

          //指定開啟的端口號
          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?從外部連接時執(zhí)行
          wss.on('connection',?(ws)?=>?{
          ??//連接時執(zhí)行此?console?提示
          ??console.log('Client?connected');

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

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

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

          在開啟WebSocket后,服務(wù)端會在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');

          //?開啟后的動作,指定在連接后執(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í)行事件,訪問html,打印ws信息

          打印了open connection說明連接成功,客戶端會使用onmessage處理接收

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

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

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

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

          server.js修改如下:

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

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

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

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

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

          多人聊天

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

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

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

          修改server.js如下:

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

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

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

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

          這樣一來,不論在哪個客戶端發(fā)送消息,服務(wù)端都能將消息回傳到每個客戶端 : 可以觀察下連接信息:

          總結(jié) ?

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

          參考文章 ?

          ?? 阮一峰-WebSocket 教程

          ?? Using WebSockets on Heroku with Node.js

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

          擴(kuò)展 ?

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

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

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

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

          ? 經(jīng)典面試題!從輸入URL到頁面展示你還不趕緊學(xué)起來??

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


          》》面試官都在用的題庫,快來看看《

          瀏覽 97
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  大鸡巴在线观看 | 国产小穴 | 在线播放国内精品 | 四虎影院无码 | 91靠逼视频 |