WebSocket
WebSocket是HTML5開始提供的一種瀏覽器與服務器間進行全雙工通訊的網絡技術。使用WebSocket,瀏覽器和服務器只需要要做一個握手的動作,然後,瀏覽器和服務器之間就形成了一條快速通道,兩者之間就直接可以數據互相傳送。而且它爲我們實現即時服務帶來了兩大好處:
-
節省資源:互相溝通的Header是很小的-大概只有 2 Bytes。
-
推送信息:不需要客戶端請求,服務器可以主動傳送數據給客戶端。
socket.io
Socket.IO是一個WebSocket庫,包括了客戶端的js和服務器端的nodejs,它的目標是構建可以在不同瀏覽器和移動設備上使用的實時應用。
服務監聽
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文件:
<script
src="/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);
return
this;
}
在send函數中,獲取到原來的參數,並在原來的基礎上插入了一個參數message,然後調用了emit函數。通過send函數的實現,我們也學會了emit函數的用法,它有兩個參數,第一個參數是事件名稱,在接收端註冊該事件就可以接收到發送過去的信息,事件名稱可以自由定義,在不同的場景下,我們可以定義不同的事件來接收消息。第二個參數纔是發送的數據。瞭解清楚了工作原理,下面來將send替換成emit函數發送信息:
//app.js
io.on('connection',function(socket){
socket.emit('message','連接成功!');
socket.on('message',function(data){
});
});
服務端事件
事件監聽是實現通訊的基礎。在一些關鍵的的狀態下,socket.io可以註冊相應的事件,通過事件監聽,我們可以在這些事件中作出反應,常用的事件如下:
connection |
客戶端成功連接到服務器。 |
message |
捕獲客戶端send信息。 |
disconnect |
客戶端斷開連接。 |
error |
發生錯誤。 |
客戶端事件
較服務端而言,客戶端提供更多的監聽事件,在實時應用中,我們可以爲這些事件註冊監聽並作出反應。
connect |
成功連接到服務器。 |
connecting |
正在連接。 |
disconnect |
斷開連接。 |
connect_failed |
連接失敗。 |
error |
連接錯誤。 |
message |
監聽服務端send的信息。 |
reconnect_failed |
重新連接失敗。 |
reconnect |
重新連接成功。 |
reconnecting |
正在重連。 |
那麼客戶端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。