摘要: WEB 開發總有一天會用到這些技術。。javascript
Fundebug經受權轉載,版權歸原做者全部。html
隨着 Web 的發展,用戶對於 Web 的實時推送要求也愈來愈高 ,好比,工業運行監控、Web 在線通信、即時報價系統、在線遊戲等,都須要將後臺發生的變化主動地、實時地傳送到瀏覽器端,而不須要用戶手動地刷新頁面。本文對過去和如今流行的 Web 實時推送技術進行了比較與總結。前端
本文完整的源代碼請猛戳Github博客,紙上得來終覺淺,建議你們動手敲敲代碼。java
HTTP 協議有一個缺陷:通訊只能由客戶端發起。舉例來講,咱們想了解今天的天氣,只能是客戶端向服務器發出請求,服務器返回查詢結果。HTTP 協議作不到服務器主動向客戶端推送信息。這種單向請求的特色,註定了若是服務器有連續的狀態變化,客戶端要獲知就很是麻煩。在WebSocket協議以前,有三種實現雙向通訊的方式:輪詢(polling)、長輪詢(long-polling)和iframe流(streaming)。git
輪詢是客戶端和服務器之間會一直進行鏈接,每隔一段時間就詢問一次。其缺點也很明顯:鏈接數會不少,一個接受,一個發送。並且每次發送請求都會有Http的Header,會很耗流量,也會消耗CPU的利用率。github
// 1.html <div id="clock"></div> <script> let clockDiv = document.getElementById('clock'); setInterval(function(){ let xhr = new XMLHttpRequest; xhr.open('GET','/clock',true); xhr.onreadystatechange = function(){ if(xhr.readyState == 4 && xhr.status == 200){ console.log(xhr.responseText); clockDiv.innerHTML = xhr.responseText; } } xhr.send(); },1000); </script>
//輪詢 服務端 let express = require('express'); let app = express(); app.use(express.static(__dirname)); app.get('/clock',function(req,res){ res.end(new Date().toLocaleString()); }); app.listen(8080);
啓動本地服務,打開http://localhost:8080/1.html
,獲得以下結果:web
長輪詢是對輪詢的改進版,客戶端發送HTTP給服務器以後,看有沒有新消息,若是沒有新消息,就一直等待。當有新消息的時候,纔會返回給客戶端。在某種程度上減少了網絡帶寬和CPU利用率等問題。因爲http數據包的頭部數據量每每很大(一般有400多個字節),可是真正被服務器須要的數據卻不多(有時只有10個字節左右),這樣的數據包在網絡上週期性的傳輸,不免對網絡帶寬是一種浪費。chrome
// 2.html 服務端代碼同上 <div id="clock"></div> <script> let clockDiv = document.getElementById('clock') function send() { let xhr = new XMLHttpRequest() xhr.open('GET', '/clock', true) xhr.timeout = 2000 // 超時時間,單位是毫秒 xhr.onreadystatechange = function() { if (xhr.readyState == 4) { if (xhr.status == 200) { //若是返回成功了,則顯示結果 clockDiv.innerHTML = xhr.responseText } send() //無論成功仍是失敗都會發下一次請求 } } xhr.ontimeout = function() { send() } xhr.send() } send() </script>
iframe流方式是在頁面中插入一個隱藏的iframe,利用其src屬性在服務器和客戶端之間建立一條長鏈接,服務器向iframe傳輸數據(一般是HTML,內有負責插入信息的javascript),來實時更新頁面。express
// 3.html <body> <div id="clock"></div> <iframe src="/clock" style="display:none"></iframe> </body>
//iframe流 let express = require('express') let app = express() app.use(express.static(__dirname)) app.get('/clock', function(req, res) { setInterval(function() { let date = new Date().toLocaleString() res.write(` <script type="text/javascript"> parent.document.getElementById('clock').innerHTML = "${date}";//改變父窗口dom元素 </script> `) }, 1000) }) app.listen(8080)
啓動本地服務,打開http://localhost:8080/3.html
,獲得以下結果: 小程序
上述代碼中,客戶端只請求一次,然而服務端倒是源源不斷向客戶端發送數據,這樣服務器維護一個長鏈接會增長開銷。
以上咱們介紹了三種實時推送技術,然而各自的缺點很明顯,使用起來並不理想,接下來咱們着重介紹另外一種技術--websocket,它是比較理想的雙向通訊技術。
WebSocket是一種全新的協議,隨着HTML5草案的不斷完善,愈來愈多的現代瀏覽器開始全面支持WebSocket技術了,它將TCP的Socket(套接字)應用在了webpage上,從而使通訊雙方創建起一個保持在活動狀態鏈接通道。
一旦Web服務器與客戶端之間創建起WebSocket協議的通訊鏈接,以後全部的通訊都依靠這個專用協議進行。通訊過程當中可互相發送JSON、XML、HTML或圖片等任意格式的數據。因爲是創建在HTTP基礎上的協議,所以鏈接的發起方還是客戶端,而一旦確立WebSocket通訊鏈接,不論服務器仍是客戶端,任意一方均可直接向對方發送報文。
初次接觸 WebSocket 的人,都會問一樣的問題:咱們已經有了 HTTP 協議,爲何還須要另外一個協議?
相對於傳統的HTTP每次請求-應答都須要客戶端與服務端創建鏈接的模式,WebSocket是相似Socket的TCP長鏈接的通信模式,一旦WebSocket鏈接創建後,後續數據都以幀序列的形式傳輸。在客戶端斷開WebSocket鏈接或Server端斷掉鏈接前,不須要客戶端和服務端從新發起鏈接請求。在海量併發和客戶端與服務器交互負載流量大的狀況下,極大的節省了網絡帶寬資源的消耗,有明顯的性能優點,且客戶端發送和接受消息是在同一個持久鏈接上發起,實時性優點明顯。
接下來我看下websocket如何實現客戶端與服務端雙向通訊:
// websocket.html <div id="clock"></div> <script> let clockDiv = document.getElementById('clock') let socket = new WebSocket('ws://localhost:9999') //當鏈接成功以後就會執行回調函數 socket.onopen = function() { console.log('客戶端鏈接成功') //再向服務 器發送一個消息 socket.send('hello') //客戶端發的消息內容 爲hello } //綁定事件是用加屬性的方式 socket.onmessage = function(event) { clockDiv.innerHTML = event.data console.log('收到服務器端的響應', event.data) } </script>
// websocket.js let express = require('express') let app = express() app.use(express.static(__dirname)) //http服務器 app.listen(3000) let WebSocketServer = require('ws').Server //用ws模塊啓動一個websocket服務器,監聽了9999端口 let wsServer = new WebSocketServer({ port: 9999 }) //監聽客戶端的鏈接請求 當客戶端鏈接服務器的時候,就會觸發connection事件 //socket表明一個客戶端,不是全部客戶端共享的,而是每一個客戶端都有一個socket wsServer.on('connection', function(socket) { //每個socket都有一個惟一的ID屬性 console.log(socket) console.log('客戶端鏈接成功') //監聽對方發過來的消息 socket.on('message', function(message) { console.log('接收到客戶端的消息', message) socket.send('服務器迴應:' + message) }) })
啓動本地服務,打開http://localhost:3000/websocket.html
,獲得以下結果:
綜上所述:Websocket協議不只解決了HTTP協議中服務端的被動性,即通訊只能由客戶端發起,也解決了數據同步有延遲的問題,同時還帶來了明顯的性能優點,因此websocket 是Web 實時推送技術的比較理想的方案,但若是要兼容低版本瀏覽器,能夠考慮用輪詢來實現。
Fundebug專一於JavaScript、微信小程序、微信小遊戲、支付寶小程序、React Native、Node.js和Java線上應用實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了10億+錯誤事件,付費客戶有Google、360、金山軟件、百姓網等衆多品牌企業。歡迎你們免費試用!