WebSocket
WebSocket是HTML5開始提供的一種瀏覽器與服務(wù)器間進(jìn)行全雙工通訊的網(wǎng)絡(luò)技術(shù)。
現(xiàn)很多網(wǎng)站為了實(shí)現(xiàn)即時(shí)通訊,所用的技術(shù)都是輪詢(polling)。輪詢是在特定的的時(shí)間間隔(如每1秒),由瀏覽器對(duì)服務(wù)器發(fā)出HTTP請(qǐng)求,然后由服務(wù)器返回最新的數(shù)據(jù)給客服端的瀏覽器,這種方式有一個(gè)很大的弊端,就是會(huì)占用很多的帶寬。
最新的輪詢效果是Comet – 用了AJAX。但這種技術(shù)雖然可達(dá)到全雙工通信,但依然需要發(fā)出請(qǐng)求。
使用WebSocket,瀏覽器和服務(wù)器只需要要做一個(gè)握手的動(dòng)作,然后,瀏覽器和服務(wù)器之間就形成了一條快速通道,兩者之間就直接可以數(shù)據(jù)互相傳送。而且它為我們實(shí)現(xiàn)即時(shí)服務(wù)帶來了兩大好處:
節(jié)省資源:互相溝通的Header是很小的-大概只有 2 Bytes。
推送信息:不需要客戶端請(qǐng)求,服務(wù)器可以主動(dòng)傳送數(shù)據(jù)給客戶端。
socket.io
Socket.IO是一個(gè)WebSocket庫,包括了客戶端的js和服務(wù)器端的nodejs,它的目標(biāo)是構(gòu)建可以在不同瀏覽器和移動(dòng)設(shè)備上使用的實(shí)時(shí)應(yīng)用。
socket.io的特點(diǎn)
易用性:socket.io封裝了服務(wù)端和客戶端,使用起來非常簡(jiǎn)單方便。
跨平臺(tái):socket.io支持跨平臺(tái),這就意味著你有了更多的選擇,可以在自己喜歡的平臺(tái)下開發(fā)實(shí)時(shí)應(yīng)用。
自適應(yīng):它會(huì)自動(dòng)根據(jù)瀏覽器從WebSocket、AJAX長(zhǎng)輪詢、Iframe流等等各種方式中選擇最佳的方式來實(shí)現(xiàn)網(wǎng)絡(luò)實(shí)時(shí)應(yīng)用,非常方便和人性化,而且支持的瀏覽器最低達(dá)IE5.5。
WebSocket 安裝部署
服務(wù)監(jiān)聽
socket.io的服務(wù)端啟動(dòng)非常的簡(jiǎn)單,引用socket.io模塊。
var io = require('socket.io');
然后調(diào)用listen函數(shù),傳入監(jiān)聽的端口號(hào),開始服務(wù)監(jiān)聽。啟用了80端口用于測(cè)試:
var io = require('socket.io')(80);
注冊(cè)事件
io.on('connection', function (socket) {
socket.on('disconnect', function () {
})
})
connection事件在客戶端成功連接到服務(wù)端時(shí)觸發(fā),有了這個(gè)事件,我們可以隨時(shí)掌握用戶連接到服務(wù)端的信息。
當(dāng)客戶端成功建立連接時(shí),在connection事件的回調(diào)函數(shù)中,我們還是可以為socket注冊(cè)一些常用的事件,如:disconnect事件,它在客戶端連接斷開是觸發(fā),這時(shí)候我就知道用戶已經(jīng)離開了。
WebSocket 啟動(dòng)服務(wù)
目前為止,我們已經(jīng)搭建好了一個(gè)最簡(jiǎn)單的socket服務(wù)器,為了在瀏覽器中能夠訪問到我們的服務(wù),我們還需要在服務(wù)端搭建一個(gè)簡(jiǎn)單的web服務(wù)器,讓瀏覽器能夠訪問我們的客戶端頁面。
為了便捷,我們選用node.js中常用的express框架來實(shí)現(xiàn)web服務(wù),示例如下:
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);
WebSocket 客戶端引用
服務(wù)端構(gòu)建完畢,下面看一看客戶端應(yīng)該如何使用。
服務(wù)端運(yùn)行后會(huì)在根目錄動(dòng)態(tài)生成socket.io的客戶端js文件,客戶端可以通過固定路徑/socket.io/socket.io.js添加引用。
首先添加網(wǎng)頁index.html,并在網(wǎng)頁中引用客戶端js文件:
script src="http://cdn.bootcss.com/socket.io/2.0.2/socket.io.js">/script>
WebSocket 連接服務(wù)
當(dāng)客戶端成功加載socket.io客戶端文件后會(huì)獲取到一個(gè)全局對(duì)象io,我們將通過io.connect函數(shù)來向服務(wù)端發(fā)起連接請(qǐng)求。
var socket = io.connect('/');
socket.on('connect',function(){
//連接成功
});
socket.on('disconnect',function(data){
//連接斷開
});
connect函數(shù)可以接受一個(gè)url參數(shù),url可以socket服務(wù)的http完整地址,也可以是相對(duì)路徑,如果省略則表示默認(rèn)連接當(dāng)前路徑。與服務(wù)端類似,客戶端也需要注冊(cè)相應(yīng)的事件來捕獲信息,不同的是客戶端連接成功的事件是connect。
了解了客戶端如何使用,下面我們創(chuàng)建網(wǎng)頁index.html,并添加如下內(nèi)容(保存):
html>
head>
script src="http://cdn.bootcss.com/socket.io/2.0.2/socket.io.js">/script>
script>
window.onload = function(){
var socket = io.connect('/');
socket.on('connect',function(){
document.write('連接成功!');
});
};
/script>
/head>
body>
/body>
/html>
頁面添加完畢還要記得在服務(wù)端app.js中為它添加路由,讓我們可以訪問測(cè)試網(wǎng)頁:
app.get('/index',function(req,res){
res.sendFile('index.html',{root:__dirname});
});
WebSocket 實(shí)時(shí)通訊
服務(wù)端和客戶端都構(gòu)建完畢了,下面開始發(fā)送消息。
當(dāng)我們成功建立連接后,我們可以通過socket對(duì)象的send函數(shù)來互相發(fā)送消息,示例-客戶端向服務(wù)端發(fā)送消息(index.html):
var socket = io.connect('/');
socket.on('connect',function(){
//客戶端連接成功后發(fā)送消息'hello world!'
socket.send('hello world!');
});
socket.on('message',function(data){
alert(data);
});
連接成功后,我們向服務(wù)端發(fā)送消息hello world!,還為socket注冊(cè)了message事件,它是send函數(shù)對(duì)應(yīng)的接收消息的事件,當(dāng)服務(wù)端向客戶端send消息時(shí),我們就可以在message事件中接收到發(fā)送過來的消息。
服務(wù)端向客戶端發(fā)送消息也可以通過send的方式,示例 - 服務(wù)端向客戶端發(fā)送消息(app.js):
var io = require('scoket.io');
io.on('connection', function (socket) {
socket.send('Hello World!');
socket.on('message', function (data) {
console.log(data);
})
});
與客戶端相同,服務(wù)端也需要為socket注冊(cè)message事件來接收客戶端發(fā)送過來的消息。
WebSocket 發(fā)送信息
socket.io既然是用來實(shí)現(xiàn)通訊的,那么如何發(fā)送、接收信息才是根本。
在socket.io中,emit函數(shù)用于發(fā)送數(shù)據(jù),我們使用send的方式實(shí)現(xiàn)了信息的互發(fā),其實(shí)send函數(shù)只是emit的封裝,實(shí)際上還是使用了emit,且看send函數(shù)是如何實(shí)現(xiàn)的:
function send(){
var args = toArray(arguments);
args.unshift('message');
this.emit.apply(this, args);
return this;
}
在send函數(shù)中,獲取到原來的參數(shù),并在原來的基礎(chǔ)上插入了一個(gè)參數(shù)message,然后調(diào)用了emit函數(shù)。通過send函數(shù)的實(shí)現(xiàn),我們也學(xué)會(huì)了emit函數(shù)的用法,它有多個(gè)參數(shù),第一個(gè)參數(shù)是事件名稱,在接收端注冊(cè)該事件就可以接收到發(fā)送過去的信息,事件名稱可以自由定義,在不同的場(chǎng)景下,我們可以定義不同的事件來接收消息。第二個(gè)參數(shù)才是發(fā)送的數(shù)據(jù)。了解清楚了工作原理,下面來將send替換成emit函數(shù)發(fā)送信息:
//app.js
io.on('connection',function(socket){
socket.emit('message','連接成功!');
socket.on('message',function(data){
});
});
WebSocket 服務(wù)端事件
事件監(jiān)聽是實(shí)現(xiàn)通訊的基礎(chǔ),因此充分了解socket.io的事件,學(xué)習(xí)如何在正確的時(shí)候使用它們至關(guān)重要。在一些關(guān)鍵的的狀態(tài)下,socket.io可以注冊(cè)相應(yīng)的事件,通過事件監(jiān)聽,我們可以在這些事件中作出反應(yīng),常用的事件如下:
connection——客戶端成功連接到服務(wù)器。
message——捕獲客戶端send信息。。
disconnect——客戶端斷開連接。
error——發(fā)生錯(cuò)誤。
WebSocket 客戶端
較服務(wù)端而言,客戶端提供更多的監(jiān)聽事件,在實(shí)時(shí)應(yīng)用中,我們可以為這些事件注冊(cè)監(jiān)聽并作出反應(yīng),例如:connect提示用戶連接成功,disconnect時(shí)提示用戶停止服務(wù)等等。
connection——成功連接到服務(wù)器。
connecting——正在連接。
disconnect——斷開連接。
connect_failed——連接失敗。
error——連接錯(cuò)誤。
message——監(jiān)聽服務(wù)端send的信息。
reconnect_failed——重新連接失敗。
reconnect——重新連接成功。
reconnecting——正在重連。
那么客戶端socket發(fā)起連接時(shí)的順序是怎么樣的呢?當(dāng)?shù)谝淮芜B接時(shí),事件觸發(fā)順序?yàn)椋?connecting → connect;
當(dāng)失去連接時(shí),事件觸發(fā)順序?yàn)椋篸isconnect → reconnecting → connecting → reconnect → connect。
WebSocket 命名空間
命名空間著實(shí)是一個(gè)非常實(shí)用好用的功能。我們可以通過命名空間,劃分出不同的房間,在房間里的廣播和通信都不會(huì)影響到房間以外的客戶端。
那么如何創(chuàng)建房間呢?在服務(wù)端,通過of("")的方式來劃分新的命名空間:
io.of('chat').on('connection',function(socket){
});
示例中,我們創(chuàng)建一個(gè)名為chat的房間,客戶端可以通過如下方式連接到指定的房間:
var socket = io.connect('/chat');
雖然連接到指定的房間,但是我們也可以在服務(wù)端操作,自由的進(jìn)出房間:
socket.join('chat');//進(jìn)入chat房間
socket.leave('chat');//離開chat房間
WebSocket 廣播消息
在實(shí)時(shí)應(yīng)用中,廣播是一個(gè)不可或缺的功能,socket.io提供兩種服務(wù)端廣播方式。
第一種廣播方式可以稱之為'全局廣播',顧名思義,全局廣播就是所有連接到服務(wù)器的客戶端都會(huì)受到廣播的信息:
socket.broadcast.emit('DATA',data);
但是,在實(shí)際應(yīng)用場(chǎng)景中,我們很多時(shí)候并不需要所有用戶都收到廣播信息,有的廣播信息只發(fā)送給一部分客戶端,比如某個(gè)房間里面的用戶,那么可以使用如下方式:
socket.broadcast.to('chat').emit('DATA',data);
中間件
socket.io提供中間件功能,我們可以通過中間件來對(duì)請(qǐng)求進(jìn)行預(yù)處理,比如身份驗(yàn)證:
io.use(function(socket, next){
if (socket.request.headers.cookie) return next();
next(new Error('Authentication error'));
});
示例中展示了通過中間件進(jìn)行身份驗(yàn)證,當(dāng)沒有cookie的時(shí)候拋出異常。
傳遞參數(shù)
在很多應(yīng)用場(chǎng)景中,客戶端發(fā)起連接請(qǐng)求時(shí)都需要傳遞參數(shù),這些參數(shù)可能是身份驗(yàn)證、初始化設(shè)置等等,那么socket.io發(fā)起連接時(shí)如何傳遞參數(shù)呢?
var socket = io.connect('/');
由于connect函數(shù)發(fā)起連接的參數(shù)是一個(gè)url,你可能會(huì)想到把參數(shù)拼接到url上,如http://xxxx?xx=xxxx,但是很遺憾這樣是行不通的,我們可以通過這樣的方式來傳遞參數(shù):
var socket = io.connect('/',{ _query:'sid=123456' });
在服務(wù)端可以這樣獲取到傳遞的參數(shù):
io.use(function(socket){
var query = socket.request._query;
var sid = query.sid;
});
客戶端傳遞的參數(shù)已經(jīng)被解析成了一個(gè)json對(duì)象,這個(gè)對(duì)象就是_query。
您可能感興趣的文章:- php使用websocket示例詳解
- Javascript WebSocket使用實(shí)例介紹(簡(jiǎn)明入門教程)
- Nginx反向代理websocket配置實(shí)例
- Python通過websocket與js客戶端通信示例分析
- 使用Java和WebSocket實(shí)現(xiàn)網(wǎng)頁聊天室實(shí)例代碼
- Java后端Tomcat實(shí)現(xiàn)WebSocket實(shí)例教程
- Android中使用WebSocket實(shí)現(xiàn)群聊和消息推送功能(不使用WebView)
- 讓ie6也支持websocket采用flash封裝實(shí)現(xiàn)
- Spring和Websocket相結(jié)合實(shí)現(xiàn)消息的推送
- 完美解決spring websocket自動(dòng)斷開連接再創(chuàng)建引發(fā)的問題
- JavaScript之WebSocket技術(shù)詳解
- 淺析nodejs實(shí)現(xiàn)Websocket的數(shù)據(jù)接收與發(fā)送
- 微信小程序 WebSocket詳解及應(yīng)用
- 使用swoole擴(kuò)展php websocket示例
- php中使用websocket詳解
- php+html5基于websocket實(shí)現(xiàn)聊天室的方法
- 詳解WebSocket+spring示例demo(已使用sockJs庫)
- C#實(shí)現(xiàn)WebSocket協(xié)議客戶端和服務(wù)器websocket sharp組件實(shí)例解析