實時通訊之Socket.io
阿新 • • 發佈:2019-02-09
WebSocket
WebSocket是HTML5開始提供的一種瀏覽器與伺服器間進行全雙工通訊的網路技術。使用WebSocket,瀏覽器和伺服器只需要要做一個握手的動作,然後,瀏覽器和伺服器之間就形成了一條快速通道,兩者之間就直接可以資料互相傳送。而且它為我們實現即時服務帶來了兩大好處:
socket.io的服務端啟動非常的簡單,引用socket.io模組。然後呼叫listen函式,傳入監聽的埠號,開始服務監聽。
var io
=require('socket.io')(80);
註冊事件
connection事件在客戶端成功連線到服務端時觸發,有了這個事件,我們可以隨時掌握使用者連線到服務端的資訊。
當客戶端成功建立連線時,在connection事件的回撥函式中,我們還是可以為socket註冊一些常用的事件,如:disconnect事件,它在客戶端連線斷開是觸發,這時候我就知道使用者已經離開了。
var io
=require('socket.io')(80);
io.on ('connection',function(socket){
//連線成功...
socket.on('disconnect',function(){
//使用者已經離開...
});
});
啟動服務
為了在瀏覽器中能夠訪問到我們的服務,我們還需要在服務端搭建一個簡單的web伺服器,讓瀏覽器能夠訪問我們的客戶端頁面。
為了便捷,我們選用node.js中常用的express框架來實現web服務,示例如下:
var express
=require('express');
var app
= express();
app.get('/',function(req,res){
res.status(200 ).send('歡迎!');
});
var server
=require('http').createServer(app);
var io
=require('socket.io')(server);
io.on('connection',function(socket){
});
server.listen(80);
客戶端引用
服務端執行後會在根目錄動態生成socket.io的客戶端js檔案,客戶端可以通過固定路徑/socket.io/socket.io.js新增引用。
首先新增網頁index.html,並在網頁中引用客戶端js檔案:
<scriptsrc="/socket.io/socket.io.js"></script>
當然這樣的客戶端引用方式並不是必須的,我們也可以引用官方的cdn或者下載到本地的客戶端檔案。一般情況下推薦引用動態生成的客戶端檔案,因為這樣客戶端和服務端的版本可以保持一致,減少出錯的機率。
<script src="https://cdn.socket.io/socket.io-1.2.1.js"></script>
連線服務
當客戶端成功載入socket.io客戶端檔案後會獲取到一個全域性物件io,我們將通過io.connect函式來向服務端發起連線請求。
var socket
= io.connect('/');
socket.on('connect',function(){
//連線成功
});
socket.on('disconnect',function(data){
//連線斷開
});
connect函式可以接受一個url引數,url可以socket服務的http完整地址,也可以是相對路徑,如果省略則表示預設連線當前路徑。與服務端類似,客戶端也需要註冊相應的事件來捕獲資訊,不同的是客戶端連線成功的事件是connect。
實時通訊
當我們成功建立連線後,我們可以通過socket物件的send函式來互相傳送訊息,示例-客戶端向服務端傳送訊息(index.html):
var socket
= io.connect('/');
socket.on('connect',function(){
//客戶端連線成功後傳送訊息'hello world!'
socket.send('hello
world!');
});
socket.on('message',function(data){
alert(data);
});
連線成功後,我們向服務端傳送訊息hello world!,還為socket註冊了message事件,它是send函式對應的接收訊息的事件,當服務端向客戶端send訊息時,我們就可以在message事件中接收到傳送過來的訊息。
服務端向客戶端傳送訊息也可以通過send的方式,示例 - 服務端向客戶端傳送訊息(app.js):
var io
=require('scoket.io');
io.on('connection',function(socket){
socket.send('歡迎!');
socket.on('message',function(data){
//收到訊息
console.log(data);
});
});
與客戶端相同,服務端也需要為socket註冊message事件來接收客戶端傳送過來的訊息。
傳送資訊
socket.io既然是用來實現通訊的,那麼如何傳送、接收資訊才是根本。
在socket.io中,emit函式用於傳送資料,還上述講解中,我們使用send的方式實現了資訊的互發,其實send函式只是emit的封裝,實際上還是使用了emit,且看send函式是如何實現的:
function send(){
var
args = toArray(arguments);
args.unshift('message');
this.emit.apply(this,
args);
returnthis;
}
在send函式中,獲取到原來的引數,並在原來的基礎上插入了一個引數message,然後呼叫了emit函式。通過send函式的實現,我們也學會了emit函式的用法,它有兩個引數,第一個引數是事件名稱,在接收端註冊該事件就可以接收到傳送過去的資訊,事件名稱可以自由定義,在不同的場景下,我們可以定義不同的事件來接收訊息。第二個引數才是傳送的資料。瞭解清楚了工作原理,下面來將send替換成emit函式傳送資訊:
//app.js
io.on('connection',function(socket){
socket.emit('message','連線成功!');
socket.on('message',function(data){
});
});
服務端事件
事件監聽是實現通訊的基礎。在一些關鍵的的狀態下,socket.io可以註冊相應的事件,通過事件監聽,我們可以在這些事件中作出反應,常用的事件如下:
客戶端事件
較服務端而言,客戶端提供更多的監聽事件,在實時應用中,我們可以為這些事件註冊監聽並作出反應。
那麼客戶端socket發起連線時的順序是怎麼樣的呢?當第一次連線時,事件觸發順序為: connecting → connect;
當失去連線時,事件觸發順序為:disconnect → reconnecting →connecting → reconnect → connect。
名稱空間
名稱空間著實是一個非常實用好用的功能。我們可以通過名稱空間,劃分出不同的房間,在房間裡的廣播和通訊都不會影響到房間以外的客戶端。
在服務端,通過of("")的方式來劃分新的名稱空間:
io.of('chat').on('connection',function(socket){
});
示例中,我們建立一個名為chat的房間,客戶端可以通過如下方式連線到指定的房間:
var socket = io.connect('/chat');
雖然連線到指定的房間,但是我們也可以在服務端操作,自由的進出房間:
socket.join('chat');//進入chat房間
socket.leave('chat');//離開chat房間
廣播訊息
在實時應用中,廣播是一個不可或缺的功能,socket.io提供兩種服務端廣播方式。
第一種廣播方式可以稱之為'全域性廣播',顧名思義,全域性廣播就是所有連線到伺服器的客戶端都會受到廣播的資訊:
socket.broadcast.emit('DATA',data);
但是,在實際應用場景中,我們很多時候並不需要所有使用者都收到廣播資訊,有的廣播資訊只發送給一部分客戶端,比如某個房間裡面的使用者,那麼可以使用如下方式:
socket.broadcast.to('chat').emit('DATA',data);
當使用to()的方式廣播資訊時,只有該名稱空間下的客戶端才會收到廣播資訊,是不是很方便呢。
傳遞引數
在很多應用場景中,客戶端發起連線請求時都需要傳遞引數,這些引數可能是身份驗證、初始化設定等等,那麼socket.io發起連線時如何傳遞引數呢?
var socket
= io.connect('/');
由於connect函式發起連線的引數是一個url,你可能會想到把引數拼接到url上,如http://xxxx?xx=xxxx,但是很遺憾這樣是行不通的,我們可以通過這樣的方式來傳遞引數:
var socket
= io.connect('/',{
_query:'sid=123456'});
在服務端可以這樣獲取到傳遞的引數:
io.use(function(socket){
var
query = socket.request._query;
var
sid = query.sid;
});
客戶端傳遞的引數已經被解析成了一個json物件,這個物件就是_query。
- 節省資源:互相溝通的Header是很小的-大概只有 2 Bytes。
- 推送資訊:不需要客戶端請求,伺服器可以主動傳送資料給客戶端。
connection | 客戶端成功連線到伺服器。 |
message | 捕獲客戶端send資訊。 |
disconnect | 客戶端斷開連線。 |
error | 發生錯誤。 |
connect | 成功連線到伺服器。 |
connecting | 正在連線。 |
disconnect | 斷開連線。 |
connect_failed | 連線失敗。 |
error | 連線錯誤。 |
message | 監聽服務端send的資訊。 |
reconnect_failed | 重新連線失敗。 |
reconnect | 重新連線成功。 |
reconnecting | 正在重連。 |