本文首發於CSDN網站,下面的版本又通過進一步的修訂。
原文:webpack與browser-sync熱更新原理深度講解html
開發環境頁面熱更新早已經是主流,咱們不光要吃着火鍋唱着歌,享受熱更新高效率的快感,更要深刻下去探求其原理。node
要知道,觸類則旁通,常見的需求如賽事網頁推送比賽結果、網頁實時展現投票或點贊數據、在線評論或彈幕、在線聊天室等,都須要藉助熱更新功能,才能達到實時的端對端的極致體驗。webpack
恰好,最近解決webpack-hot-middleware
熱更新延遲問題的過程當中,我深刻接觸了EventSource技術。遂本文由此開篇,進一步講解webpack-hot-middleware
,browser-sync
背後的技術。nginx
webpack-hot-middleware
中間件是webpack的一個plugin,一般結合webpack-dev-middleware
一塊兒使用。藉助它能夠實現瀏覽器的無刷新更新(熱更新),即webpack裏的HMR(Hot Module Replacement)。如何配置請參考 webpack-hot-middleware,如何理解其相關插件請參考 手把手深刻理解 webpack dev middleware 原理與相關 plugins。git
webpack加入webpack-hot-middleware
後,內存中的頁面將包含HMR相關js,加載頁面後,Network欄能夠看到以下請求:github
__webpack_hmr是一個type
爲EventSource的請求, 從Time
欄能夠看出:默認狀況下,服務器每十秒推送一條信息到瀏覽器。web
若是此時關閉開發服務器,瀏覽器因爲重連機制,將持續拋出相似GET http://www.test.com/__webpack_hmr 502 (Bad Gateway)
這樣的錯誤。從新啓動開發服務器後,重連將會成功,此時便會刷新頁面。ajax
以上這些即是咱們使用時感覺到的最初的印象。固然,停留在使用層面不是咱們的目標,接下來咱們將跳出該中間件,講解其所使用到的EventSource
技術。算法
EventSource 不是一個新鮮的技術,它早就隨着H5規範提出了,正式一點應該叫Server-sent events
,即SSE
。chrome
鑑於傳統的經過ajax輪訓獲取服務器信息的技術方案已通過時,咱們迫切須要一個高效的節省資源的方式去獲取服務器信息,一旦服務器資源有更新,可以及時地通知到客戶端,從而實時地反饋到用戶界面上。EventSource就是這樣的技術,它本質上仍是HTTP,經過response流實時推送服務器信息到客戶端。
新建一個EventSource對象很是簡單。
const es = new EventSource('/message');// /message是服務端支持EventSource的接口
新建立的EventSource對象擁有以下屬性:
屬性 | 描述 |
---|---|
url(只讀) | es對象請求的服務器url |
readyState(只讀) | es對象的狀態,初始爲0,包含CONNECTING (0),OPEN (1),CLOSED (2)三種狀態 |
withCredentials | 是否容許帶憑證等,默認爲false,即不支持發送cookie |
服務端實現/message
接口,須要返回類型爲 text/event-stream
的響應頭。
var http = require('http'); http.createServer(function(req,res){ if(req.url === '/message'){ res.writeHead(200,{ 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); setInterval(function(){ res.write('data: ' + +new Date() + '\n\n'); }, 1000); } }).listen(8888);
咱們注意到,爲了不緩存,Cache-Control 特別設置成了 no-cache,爲了可以發送多個response, Connection被設置成了keep-alive.。發送數據時,請務必保證服務器推送的數據以 data:
開始,以\n\n
結束,不然推送將會失敗(緣由就不說了,這是約定的)。
以上,服務器每隔1s主動向客戶端發送當前時間戳,爲了接受這個信息,客戶端須要監聽服務器。以下:
es.onmessage = function(e){ console.log(e.data); // 打印服務器推送的信息 }
以下是消息推送的過程:
你覺得es只能監聽message事件嗎?並非,message只是缺省的事件類型。實際上,它能夠監放任何指定類型的事件。
es.addEventListener("####", function(e) {// 事件類型能夠隨你定義 console.log('####:', e.data); },false);
服務器發送不一樣類型的事件時,須要指定event字段。
res.write('event: ####\n'); res.write('data: 這是一個自定義的####類型事件\n'); res.write('data: 多個data字段將被解析成一個字段\n\n');
以下所示:
能夠看到,服務端指定event事件名爲"####"後,客戶端觸發了對應的事件回調,同時服務端設置的多個data字段,客戶端使用換行符鏈接成了一個字符串。
不只如此,事件流中還能夠混合多種事件,請看咱們是怎麼收到消息的,以下:
除此以外,es對象還擁有另外3個方法: onopen()
、onerror()
、close()
,請參考以下實現。
es.onopen = function(e){// 連接打開時的回調 console.log('當前狀態readyState:', es.readyState);// open時readyState===1 } es.onerror = function(e){// 出錯時的回調(網絡問題,或者服務下線等都有可能致使出錯) console.log(es.readyState);// 出錯時readyState===0 es.close();// 出錯時,chrome瀏覽器會每隔3秒向服務器重發原請求,直到成功. 所以出錯時,可主動斷開原鏈接. }
使用EventSource技術實時更新網頁信息十分高效。實際使用中,咱們幾乎不用擔憂兼容性問題,主流瀏覽器都了支持EventSource,固然,除了掉隊的IE系。對於不支持的瀏覽器,其PolyFill方案請參考HTML5 Cross Browser Polyfills。
另外,若是須要支持跨域調用,請設置響應頭Access-Control-Allow-Origin': '*'
。
如需支持發送cookie,請設置響應頭Access-Control-Allow-Origin': req.headers.origin
和 Access-Control-Allow-Credentials:true
,而且建立es對象時,須要明確指定是否發送憑證。以下:
var es = new EventSource('/message', { withCredentials: true }); // 建立時指定配置纔是有效的 es.withCredentials = true; // 與ajax不一樣,這樣設置是無效的
如下是主流瀏覽器對EventSource的CORS的支持:
Firefox | Opera | Chrome | Safari | iOS | Android |
---|---|---|---|---|---|
10+ | 12+ | 26+ | 7.0+ | 7.0+ | 4.4+ |
既然說到了EventSource,便有必要談談遇到的坑,接下來,就說說我遇到的webpack熱更新延遲問題。
如咱們所知,webpack藉助webpack-hot-middleware插件,實現了網頁熱更新機制,正常狀況下,瀏覽器打開 http://localhost:8080 這樣的網頁便可開始調試。然而實際開發中,因爲遠程服務器須要種cookie登陸態到特定的域名上等緣由,所以本地每每會用nginx作一層反向代理。即把 http://www.test.com 的請求轉發到 http://localhost:8080 上(配置過程這裏不詳述,具體請參考Ajax知識體系大梳理-ajax調試技巧)。轉發事後,發現熱更新便延遲了。
緣由是nginx默認開啓的buffer機制緩存了服務器推送的片斷信息,緩存達到必定的量纔會返回響應內容。只要關閉proxy_buffering便可。配置以下所示:
server { listen 80; server_name www.test.company.com; location / { proxy_pass http://localhost:8080; proxy_buffering off; } }
至此,EventSource部分便告一段落。學習講究由淺入深,按部就班。後面我將重點講解的browser-sync
熱更新機制,請耐心細讀。
開發中使用browser-sync
插件調試,一個網頁裏的全部交互動做(包括滾動,輸入,點擊等等),能夠實時地同步到其餘全部打開該網頁的設備,可以節省大量的手工操做時間,從而帶來流暢的開發調試體驗。目前browser-sync
能夠結合Gulp
或Grunt
一塊兒使用,其API請參考:Browsersync API。
經過上面的瞭解,咱們知道EventSouce
的使用是比較便捷的,那爲何browser-sync
不使用EventSource技術進行代碼推送呢?這是由於browser-sync
插件共作了兩件事:
開發更新了一段新的邏輯,服務器實時推送代碼改動信息。數據流:服務器 —> 瀏覽器,使用EventSource技術一樣可以實現。
用戶操做網頁,滾動、輸入或點擊等,操做信息實時發送給服務器,而後再由服務器將操做同步給其餘已打開的網頁。數據流:瀏覽器 —> 服務器 —> 瀏覽器,該部分功能EventSource技術已無能爲力。
以上,browser-sync
使用WebSocket技術達到實時推送代碼改動和用戶操做兩個目的。至於它是如何計算推送內容,根據不一樣推送內容採起何種響應策略,不在本次討論範圍以內。下面咱們將講解其核心的WebSocket技術。
WebSocket是基於TCP的全雙工通信的協議,它與EventSource有着本質上的不一樣.(前者基於TCP,後者依然基於HTTP) 該協議於2011年被IETF定爲標準RFC6455,後被RFC7936補充. WebSocket api也被W3C定爲標準。
WebSocket使用和HTTP相同的TCP端口,默認爲80, 統一資源標誌符爲ws,運行在TLS之上時,默認使用443,統一資源標誌符爲wss。它經過101 switch protocol
進行一次TCP握手,即從HTTP協議切換成WebSocket通訊協議。
相對於HTTP協議,WebSocket擁有以下優勢:
全雙工,實時性更強。
相對於http攜帶完整的頭部,WebSocket請求頭部明顯減小。
保持鏈接狀態,不用再驗權了。
二進制支持更強,Websocket定義了二進制幀,處理更輕鬆。
Websocket協議支持擴展,能夠自定義的子協議,如 permessage-deflate
擴展。
優秀技術的落地,調研兼容性是必不可少的環節。所幸的是,現代瀏覽器對WebSocket的支持比較友好,以下是PC端兼容性:
IE/Edge | Firefox | Chrome | Safari | Opera |
---|---|---|---|---|
10+ | 11+ | 16+ | 7+ | 12.1+ |
以下是mobile端兼容性:
iOS Safari | Android | Android Chrome | Android UC | QQ Browser | Opera Mini |
---|---|---|---|---|---|
7.1+ | 4.4+ | 57+ | 11.4+ | 1.2+ | - |
根據RFC6455文檔,WebSocket協議基於Frame而非Stream(EventSource是基於Stream的)。所以其傳輸的數據都是Frame(幀)。想要了解數據的往返,弄懂協議處理過程,Frame的解讀是必不可少。以下即是Frame的結構:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len | Extended payload length | |I|S|S|S| (4) |A| (7) | (16/64) | |N|V|V|V| |S| | (if payload len==126/127) | | |1|2|3| |K| | | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + | Extended payload length continued,if payload len == 127 | + - - - - - - - - - - - - - - - +-------------------------------+ | |Masking-key,if MASK set to 1 | +-------------------------------+-------------------------------+ | Masking-key (continued) | Payload Data | +-------------------------------- - - - - - - - - - - - - - - - + : Payload Data continued ... : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + | Payload Data continued ... | +---------------------------------------------------------------+
第一個字節包含FIN、RSV、Opcode。
FIN:size爲1bit,標示是否最後一幀。%x0
表示還有後續幀,%x1
表示這是最後一幀。
RSV一、二、3,每一個size都是1bit,默認值都是0,若是沒有定義非零值的含義,卻出現了非零值,則WebSocket連接將失敗。
Opcode,size爲4bits,表示『payload data』的類型。若是收到未知的opcode,鏈接將會斷開。已定義的opcode值以下:
%x0: 表明連續的幀 %x1: 文本幀 %x2: 二進制幀 %x3~7: 預留的非控制幀 %x8: 關閉握手幀 %x9: ping幀,後續心跳鏈接會講到 %xA: pong幀,後續心跳鏈接會講到 %xB~F: 預留的非控制幀
第二個字節包含Mask、Payload len。
Mask:size爲1bit,標示『payload data』是否添加掩碼。全部從客戶端發送到服務端的幀都會被置爲1,若是置1,Masking-key
便會賦值。
//若server是一個WebSocket服務端實例 //監聽客戶端消息 server.on('message', function(msg, flags) { console.log('client say: %s', msg); console.log('mask value:', flags.masked);// true,進一步佐證了客戶端發送到服務端的Mask幀都會被置爲1 }); //監聽客戶端pong幀響應 server.on('pong', function(msg, flags) { console.log('pong data: %s', msg); console.log('mask value:', flags.masked);// true,進一步佐證了客戶端發送到服務端的Mask幀都會被置爲1 });
Payload len:size爲7bits,即便是當作無符號整型也只能表示0~127的值,因此它不能表示更大的值,所以規定"Payload data"長度小於或等於125的時候才用來描述數據長度。若是Payload len==126
,則使用隨後的2bytes(16bits)來存儲數據長度。若是Payload len==127
,則使用隨後的8bytes(64bits)來存儲數據長度。
以上,擴展的Payload len可能佔據第三至第四個或第三至第十個字節。緊隨其後的是"Mask-key"。
Mask-key:size爲0或4bytes(32bits),默認爲0,與前面Mask呼應,從客戶端發送到服務端的幀都包含4bytes(32bits)的掩碼,一旦掩碼被設置,全部接收到的"payload data"都必須與該值以一種算法作異或運算來獲取真實值。
Payload data:size爲"Extension data" 和 "Application data" 的總和,通常"Extension data"數據爲空。
Extension data:默認爲0,若是擴展被定義,擴展必須指定"Extension data"的長度。
Application data:佔據"Extension data"以後剩餘幀的空間。
關於Frame的更多理論介紹不妨讀讀 學習WebSocket協議—從頂層到底層的實現原理(修訂版)。
關於Frame的數據幀解析不妨讀讀 WebSocket(貳) 解析數據幀 及其後續文章。
瞭解了Frame的數據結構後,咱們來實際練習下。瀏覽器上,新建一個ws對象十分簡單。以下:
let ws = new WebSocket('ws://127.0.0.1:10103/');// 本地使用10103端口進行測試
新建的WebSocket對象以下所示:
這中間包含了一次Websocket握手的過程,咱們分兩步來理解。
第一步,客戶端請求。
這是一個GET請求,主要字段以下:
Connection: Upgrade Upgrade: websocket Sec-WebSocket-Key:61x6lFN92sJHgzXzCHfBJQ== Sec-WebSocket-Version:13
Connection字段指定爲Upgrade,表示客戶端但願鏈接升級。
Upgrade字段設置爲websocket,表示但願升級至Websocket協議。
Sec-WebSocket-Key字段是隨機字符串,服務器根據它來構造一個SHA-1的信息摘要。
Sec-WebSocket-Version表示支持的Websocket版本。RFC6455要求使用的版本是13。
甚至咱們能夠從請求截圖裏看出,Origin是file://
,而Host是127.0.0.1:10103
,明顯不是同一個域下,但依然能夠請求成功,說明Websocket協議是不受同源策略限制的(同源策略限制的是http協議)。
第二步,服務端響應。
Status Code: 101 Switching Protocols 表示Websocket協議經過101狀態碼進行握手。
Sec-WebSocket-Accept字段是由Sec-WebSocket-Key字段加上特定字符串"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",計算SHA-1摘要,而後再base64編碼以後生成的. 該操做可避免普通http請求,被誤認爲Websocket協議。
Sec-WebSocket-Extensions字段表示服務端對Websocket協議的擴展。
以上,WebSocket構造器不止能夠傳入url,還能傳入一個可選的協議名稱字符串或數組。
ws = new WebSocket('ws://127.0.0.1:10103/', ['abc','son_protocols']);
等等,咱們慢一點,上面好像漏掉了一步,彷佛沒有提到服務端是怎麼實現的。請繼續往下看:
先作一些準備。ws是一個nodejs版的WebSocketServer實現。使用 npm install ws
便可安裝。
var WebSocketServer = require('ws').Server, server = new WebSocketServer({port: 10103}); server.on('connection', function(s) { s.on('message', function(msg) { //監聽客戶端消息 console.log('client say: %s', msg); }); s.send('server ready!');// 鏈接創建好後,向客戶端發送一條消息 });
以上,new WebSocketServer()
建立服務器時如需權限驗證,請指定verifyClient
爲驗權的函數。
server = new WebSocketServer({ port: 10103, verifyClient: verify }); function verify(info){ console.log(Object.keys(info));// [ 'origin', 'secure', 'req' ] console.log(info.orgin);// "file://" return true;// 返回true時表示驗權經過,不然客戶端將拋出"HTTP Authentication failed"錯誤 }
以上,verifyClient
指定的函數只有一個形參,若爲它顯式指定兩個形參,那麼第一個參數同上info,第二個參數將是一個cb
回調函數。該函數用於顯式指定拒絕時的HTTP狀態碼等,它默認擁有3個形參,依次爲:
result,布爾值類型,表示是否經過權限驗證。
code,數值類型,若result值爲false時,表示HTTP的錯誤狀態碼。
name,字符串類型,若result值爲false時,表示HTTP狀態碼的錯誤信息。
// 若verify定義以下 function verify(info, cb){ //一旦擁有第二個形參,若是不調用,默認將經過驗權 cb(false, 401, '權限不夠');// 此時表示驗權失敗,HTTP狀態碼爲401,錯誤信息爲"權限不夠" return true;// 一旦擁有第二個形參,響應就被cb接管了,返回什麼值都不會影響前面的處理結果 }
除了port
和 verifyClient
設置外,其它設置項及更多API,請參考文檔 ws-doc。
接下來,咱們來實現消息收發。以下是客戶端發送消息。
ws.onopen = function(e){ // 可發送字符串,ArrayBuffer 或者 Blob數據 ws.send('client ready!); };
客戶端監聽信息。
ws.onmessage = function(e){ console.log('server say:', e.data); };
以下是瀏覽器的運行截圖。
消息的內容都在Frames欄,第一條彩色背景的信息是客戶端發送的,第二條是服務端發送的。兩條消息的長度都是13。
以下是Timing欄,不止是WebSocket,包括EventSource,都有這樣的黃色高亮警告。
該警告說明:請求還沒完成。實際上,直到一方鏈接close掉,請求才會完成。
說到close,ws的close方法比es的略複雜。
語法:close(short code,string reason);
close默承認傳入兩個參數。code是數字,表示關閉鏈接的狀態號,默認是1000,即正常關閉。(code取值範圍從0到4999,其中有些是保留狀態號,正常關閉時只能指定爲1000或者3000~4999之間的值,具體請參考CloseEvent - Web APIs)。reason是UTF-8文本,表示關閉的緣由(文本長度需小於或等於123字節)。
因爲code 和 reason都有限制,所以該方法可能拋出異常,建議catch下.
try{ ws.close(1001, 'CLOSE_GOING_AWAY'); }catch(e){ console.log(e); }
ws對象還擁有onclose和onerror監聽器,分別監聽關閉和錯誤事件。(注:EventSource沒有onclose監聽)
ws的readyState屬性擁有4個值,比es的readyState的多一個CLOSING的狀態。
常量 | 描述 | EventSource(值) | WebSocket(值) |
---|---|---|---|
CONNECTING | 鏈接未初始化 | 0 | 0 |
OPEN | 鏈接已就緒 | 1 | 1 |
CLOSING | 鏈接正在關閉 | - | 2 |
CLOSED | 鏈接已關閉 | 2 | 3 |
另外,除了兩種都有的url屬性外,WebSocket對象還擁有更多的屬性。
屬性 | 描述 |
---|---|
binaryType | 被傳輸二進制內容的類型,有blob,arraybuffer兩種 |
bufferedAmount | 待傳輸的數據的長度 |
extensions | 表示服務器選用的擴展 |
protocol | 指的是構造器第二個參數傳入的子協議名稱 |
之前一直是使用ajax作文件上傳,實際上,Websocket上傳文件也是一把好刀. 其send方法能夠發送String,ArrayBuffer,Blob共三種數據類型,發送二進制文件徹底不在話下。
因爲各個瀏覽器對Websocket單次發送的數據有限制,因此咱們須要將待上傳文件切成片斷去發送。以下是實現。
1) html。
<input type="file" id="file"/>
2) js。
const ws = new WebSocket('ws://127.0.0.1:10103/');// 鏈接服務器 const fileSelect = document.getElementById('file'); const size = 1024 * 128;// 分段發送的文件大小(字節) let curSize, total, file, fileReader; fileSelect.onchange = function(){ file = this.files[0];// 選中的待上傳文件 curSize = 0;// 當前已發送的文件大小 total = file.size;// 文件大小 ws.send(file.name);// 先發送待上傳文件的名稱 fileReader = new FileReader();// 準備讀取文件 fileReader.onload = loadAndSend; readFragment();// 讀取文件片斷 }; function loadAndSend(){ if(ws.bufferedAmount > size * 5){// 若發送隊列中的數據太多,先等一等 setTimeout(loadAndSend,4); return; } ws.send(fileReader.result);// 發送本次讀取的片斷內容 curSize += size;// 更新已發送文件大小 curSize < total ? readFragment() : console.log('upload successed!');// 下一步操做 } function readFragment(){ const blob = file.slice(curSize, curSize + size);// 獲取文件指定片斷 fileReader.readAsArrayBuffer(blob);// 讀取文件爲ArrayBuffer對象 }
3) server(node)。
var WebSocketServer = require('ws').Server, server = new WebSocketServer({port: 10103}),// 啓動服務器 fs = require('fs'); server.on('connection', function(wsServer){ var fileName, i = 0;// 變量定義不可放在全局,因每一個鏈接都不同,這裏纔是私有做用域 server.on('message', function(data, flags){// 監聽客戶端消息 if(flags.binary){// 判斷是否二進制數據 var method = i++ ? 'appendFileSync' : 'writeFileSync'; // 當前目錄下寫入或者追加寫入文件(建議加上try語句捕獲可能的錯誤) fs[method]('./' + fileName, data,'utf-8'); }else{// 非二進制數據則認爲是文件名稱 fileName = data; } }); wsServer.send('server ready!');// 告知客戶端服務器已就緒 });
運行效果以下:
上述測試代碼中沒有過多涉及服務器的存儲過程。一般,服務器也會有緩存區上限,若是客戶端單次發送的數據量超過服務端緩存區上限,那麼服務端也須要屢次讀取。
生產環境下上傳一個文件遠比本地測試來得複雜。實際上,從客戶端到服務端,中間存在着大量的網絡鏈路,如路由器,防火牆等等。一份文件的上傳要通過中間的層層路由轉發,過濾。這些中間鏈路可能會認爲一段時間沒有數據發送,就自發切斷兩端的鏈接。這個時候,因爲TCP並不定時檢測鏈接是否中斷,而通訊的雙方又相互沒有數據發送,客戶端和服務端依然會一廂情願的信任以前的鏈接,久而久之,將使得大量的服務端資源被WebSocket鏈接佔用。
正常狀況下,TCP的四次揮手徹底能夠通知兩端去釋放鏈接。可是上述這種廣泛存在的異常場景,將使得鏈接的釋放成爲夢幻。
爲此,早在websocket協議實現時,設計者們便提供了一種 Ping/Pong Frame的心跳機制。一端發送Ping Frame,另外一端以 Pong Frame響應。這種Frame是一種特殊的數據包,它只包含一些元數據,可以在不影響原通訊的狀況下維持住鏈接。
根據規範RFC 6455,Ping Frame包含一個值爲9的opcode,它可能攜帶數據。收到Ping Frame後,Pong Frame必須被做爲響應發出。Pong Frame包含一個值爲10的opcode,它將包含與Ping Frame中相同的數據。
藉助ws包,服務端能夠這麼來發送Ping Frame。
wsServer.ping();
同時,須要監聽客戶端響應的pong Frame.
wsServer.on('pong', function(data, flags) { console.log(data);// "" console.log(flags);// { masked: true,binary: true } });
以上,因爲Ping Frame 不帶數據,所以做爲響應的Pong Frame的data值爲空串。遺憾的是,目前瀏覽器只能被動發送Pong Frame做爲響應(Sending websocket ping/pong frame from browser),沒法經過JS API主動向服務端發送Ping Frame。所以對於web服務,能夠採起服務端主動ping的方式,來保持住連接。實際應用中,服務端還須要設置心跳的週期,以保證心跳鏈接能夠一直持續。同時,還應該有重發機制,若連續幾回沒有收到心跳鏈接的回覆,則認爲鏈接已經斷開,此時即可以關閉Websocket鏈接了。
WebSocket出世已久,不少優秀的大神基於此開發出了各式各樣的庫。其中Socket.IO是一個很是不錯的開源WebSocke庫,旨在抹平瀏覽器之間的兼容性問題。它基於Node.js,支持如下方式優雅降級:
Websocket
Adobe® Flash® Socket
AJAX long polling
AJAX multipart streaming
Forever Iframe
JSONP Polling
如何在項目中使用Socket.IO,請參考第一章 socket.io 簡介及使用。
EventSource,本質依然是HTTP,它僅提供服務端到客戶端的單向文本數據傳輸,不須要心跳鏈接,鏈接斷開會持續觸發重連。
WebSocket協議,基於TCP協議,它提供雙向數據傳輸,支持二進制,須要心跳鏈接,鏈接斷開不會重連。
EventSource更輕量和簡單,WebSocket支持性更好(因其支持IE10+)。一般來講,使用EventSource可以完成的功能,使用WebSocket同樣可以作到,反之卻不行,使用時若遇到鏈接斷開或拋錯,請及時調用各自的close
方法主動釋放資源。
本問就討論這麼多內容,你們有什麼問題或好的想法歡迎在下方參與留言和評論。
本文做者: louis
本文連接: http://louiszhai.github.io/20...
參考文章