socket.io是一個跨瀏覽器支持WebSocket的實時通訊的JS。
由於HTTP是無狀態的協議,要實現即時通訊非常困難。因為當對方發送一條消息時,服務器並不知道當前有哪些用戶等着接收消息,當前實現即時通訊功能最為普遍的方式就是輪詢機制。即客戶端定期發起一個請求,看看有沒有人發送消息到服務器,如果有服務端就將消息發給客戶端。這種做法的缺點顯而易見,那么多的請求將消耗大量資源,大量的請求其實是浪費的。
現在,我們有了WebSocket,它是HTML5的新API。WebSocket連接本質上就是建立一個TCP連接,WebSocket會通過HTTP請求建立,建立后的WebSocket會在客戶端和服務端建立一個持久的連接,直到有一方主動關閉該連接。所以,現在服務器就知道有哪些用戶正在連接了,這樣通訊就變得相對容易了。
Socket.io支持及時、雙向、基於事件的交流,可在不同平台、瀏覽器、設備上工作,可靠性和速度穩定。最典型的應用場景如:
- 實時分析:將數據推送到客戶端,客戶端表現為實時計數器、圖表、日志客戶。
- 實時通訊:聊天應用
- 二進制流傳輸:socket.io支持任何形式的二進制文件傳輸,例如圖片、視頻、音頻等。
- 文檔合並:允許多個用戶同時編輯一個文檔,並能夠看到每個用戶做出的修改。
Socket.io實際上是WebSocket的父集,Socket.io封裝了WebSocket和輪詢等方法,會根據情況選擇方法來進行通訊。
Node.js提供了高效的服務端運行環境,但由於Browser對HTML5的支持不一,為了兼容所有瀏覽器,提供實時的用戶體驗,並為開發者提供客戶端與服務端一致的編程體驗,於是Socket.io誕生了。
npm安裝socket.op
npm install --save socket.io
Socket.io將WebSocket和Polling機制以及其它的實時通信方式封裝成通用的接口,並在服務端實現了這些實時機制相應代碼。這就是說,WebSocket僅僅是Socket.io實現實時通信的一個子集,那么Socket.io都實現了Polling中那些通信機制呢?
- Adobe Flash Socket
大部分PC瀏覽器都支持的Socket模式,不過是通過第三方嵌入到瀏覽器,不在W3C規范內,可能將逐步被淘汰。況且,大部分手機瀏覽器並不支持此種模式。 - AJAX Long Polling
定時向服務端發送請求,缺點是給服務端帶來壓力並出現信息更新不及時的現象。 - AJAX multipart streaming
在XMLHttpRequest對象上使用某些瀏覽器支持的multi-part標志,AJAX請求被發送給服務端並保持打開狀態(掛起狀態),每次需要向客戶端發送信息,就尋找一個掛起的HTTP請求響應給客戶端,並且所有的響應都會通過統一連接來寫入。 - Forever Iframem
永存的Iframe設計了一個置於頁面中隱藏的iframe標簽,該標簽的src屬性指向返回服務端時間的Servlet路徑。每次在事件到達時,Servlet寫入並刷新一個新的Script標簽,該標簽內部帶有JS代碼,iframe的內容被附加上script標簽,標簽中的內容就會得到執行。這種方式的缺點是接收數據都是由瀏覽器通過HTML標簽來處理的,因此無法知道連接何時在哪一端被斷開,而且iframe標簽在瀏覽器中將被逐步取消。 - JSONP Polling
JSONP輪詢基本與HTTP輪詢一樣,不同之處則是JSONP可發出跨域請求。
Socket.io 基本應用
socket.io提供了基於事件的實時雙向通訊,它同時提供了服務端和客戶端的API。
服務端
服務端socket.io必須綁定一個http.Server
實例,因為WebSocket協議是構建在HTTP協議之上的,所以在創建WebSocket服務時需調用HTTP模塊並調用其下createServer()
方法,將生成的server作為參數傳入socket.io。
var httpServer = require('http').createServer(); var io = require('socket.io')(httpServer); httpServer.listen(3000);
綁定http.Server
可使用隱式綁定和顯式綁定
- 隱式綁定
socket.io內部實例化並監聽http.Server
,通過實例化時傳入端口或者在實例化后調用listen
或attach
函數進行隱式綁定。
// 實例化時傳入端口 require('socket.io')(3000) // 通過listen或attach函數綁定 let io = require('socket.io') io.listen(3000); // io.attach(3000);
- 顯式綁定
// 實例化時綁定 let httpServer = require('http').Server(); let io = require('socket.io')(httpServer); httpServer.listen(3000); //通過listen或attach綁定 let httpServer = require('http').Server(); let io = require('socket.io')(); io.listen(httpServer); // io.attach(httpServer); httpServer.listen(3000);
Express框架中使用
let app = require('express'); let httpServer= require('http').Server(app); let io = require('socket.io')(httpServer); app.listen(3000);
KOA框架中使用
let app = require('koa')(); let httpServer = require('http').Server(app.callback()); let io = require('socket.io')(httpServer); app.listen(3000);
建立連接
當服務端和客戶端連接成功時,服務端會監聽到connection
和connect
事件,客戶端會監聽到connect
事件,斷開連接時服務端對應到客戶端的socket與客戶端均會監聽到disconcect
事件。
/*客戶端*/ <script src="http://cdn.socket.io/stable/socket.io.js"></script> <script> // socket.io引入成功后,可通過io()生成客戶端所需的socket對象。 let socket = io('http://127.0.0.0:3000'); // socket.emmit()用戶客戶端向服務端發送消息,服務端與之對應的是socket.on()來接收信息。 socket.emmit('client message', {msg:'hi, server'}); // socket.on()用於接收服務端發來的消息 socket.on('connect', ()=>{ console.log('client connect server'); }); socket.on('disconnect', ()=>{ console.log('client disconnect'); }); </script> /*服務端*/ // 服務端綁定HTTP服務器實例 let httpServer = require('http').Server(); let io = require('socket.io')(httpServer); httpServer.listen(3000); // 服務端監聽連接狀態:io的connection事件表示客戶端與服務端成功建立連接,它接收一個回調函數,回調函數會接收一個socket參數。 io.on('connection', (socket)=>{ console.log('client connect server, ok!'); // io.emit()方法用於向服務端發送消息,參數1表示自定義的數據名,參數2表示需要配合事件傳入的參數 io.emmit('server message', {msg:'client connect server success'}); // socket.broadcast.emmit()表示向除了自己以外的客戶端發送消息 socket.broadcast.emmit('server message', {msg:'broadcast'}); // 監聽斷開連接狀態:socket的disconnect事件表示客戶端與服務端斷開連接 socket.on('disconnect', ()=>{ console.log('connect disconnect'); }); // 與客戶端對應的接收指定的消息 socket.on('client message', (data)=>{ cosnole.log(data);// hi server }); socket.disconnect(); });
傳輸數據
服務端和客戶端的socket是一個關聯的EventEmitter
對象,客戶端socket派發的事件可以通過被服務端的socket接收,服務端socket派發的事件也可以被客戶端接收。基於這種機制,可以實現雙向交流。
# 模擬:客戶端不斷發送隨機數,當隨機數大於0.95時,服務端延遲1s后向客戶端發送警告以及警告次數。
/*客戶端*/ <script src="http://cdn.socket.io/stable/socket.io.js"></script> <script> let socket = io('http://127.0.0.1:3000'); let interval = setTimeInterval(()=>{ socket.emit('random', Math.random()); }, 500); socket.on('warn', count=>{ console.log('warning count : '+count); }); socket.on('disconnect', ()=>{ clearInterval(interval); }); </script> /*服務端*/ let httpServer = require('http').Server(); let io = require('socket.io')(httpServer); httpServer.listen(3000); io.on('connection', socket=>{ socket.on('random', value=>{ console.log(value); if(value>0.95){ if(typeof socket.warnign==='undefined'){ socket.warning = 0;// socket對象可用來存儲狀態和自定義數據 } setTimeout(()=>{ socket.emit('warn', ++socket.warning); }, 1000); } }); });