socket.io是一個跨浏覽器支援WebSocket的實時通訊的JS。
http://socket.io/docs/
由于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);
}
});
});