需求:
需求實(shí)現(xiàn)監(jiān)聽服務(wù)端推送過來的消息霹肝,效果見下圖:
用戶某一個(gè)動(dòng)作觸發(fā)消息發(fā)送瑟幕,在監(jiān)聽接口收到消息并展示出來。
解決方案:
ajax輪詢(短輪詢)
原理:客戶端每隔幾秒就發(fā)送一次請(qǐng)求创夜,服務(wù)器接到請(qǐng)求后馬上返回響應(yīng)信息并關(guān)閉連接溶耘。
優(yōu)點(diǎn):程序編寫比較容易。
缺點(diǎn):請(qǐng)求次數(shù)多鼻忠,請(qǐng)求中有大半是無用涵但,浪費(fèi)帶寬和服務(wù)器資源。
long poll (長(zhǎng)輪詢)
原理:跟ajax輪詢差不多帖蔓,都是采用輪詢的方式矮瘟,區(qū)別在于:服務(wù)端收到請(qǐng)求后,檢測(cè)數(shù)據(jù)是否有新數(shù)據(jù)塑娇,有則立即返回并關(guān)閉澈侠,無則掛起一直不返回Response直到超時(shí)關(guān)閉。關(guān)閉后客戶端再次發(fā)起請(qǐng)求建立連接埋酬,周而復(fù)始哨啃。
優(yōu)點(diǎn):客戶端的請(qǐng)求次數(shù)會(huì)大量減少
缺點(diǎn):服務(wù)端請(qǐng)求掛起同樣會(huì)導(dǎo)致資源的浪費(fèi)
小結(jié):
1.HTTP協(xié)議是基于請(qǐng)求/響應(yīng)模式的,因此只要服務(wù)端給了響應(yīng)写妥,本次HTTP連接就結(jié)束了拳球。
2.Ajax輪詢與long poll都屬于不斷發(fā)送http請(qǐng)求,然后等待服務(wù)器處理耳标,可以看到http協(xié)議一個(gè)特點(diǎn)醇坝,被動(dòng)性!
webSocket
webSocket是html5一種新的協(xié)議次坡,實(shí)現(xiàn)了瀏覽器與服務(wù)器之間的全雙工通信呼猪,能很好的節(jié)省服務(wù)器資源與帶寬,并在服務(wù)器端與瀏覽器端實(shí)現(xiàn)實(shí)時(shí)通行砸琅,他建立在TCP之上宋距, 同http一樣,通過tcp來傳輸數(shù)據(jù)症脂。
WebSocket 使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡(jiǎn)單谚赎,允許服務(wù)端主動(dòng)向客戶端推送數(shù)據(jù)。在 WebSocket API 中诱篷,瀏覽器和服務(wù)器只需要完成一次握手壶唤,兩者之間就直接可以創(chuàng)建持久性的連接,并進(jìn)行雙向數(shù)據(jù)傳輸棕所。
webSocket客戶端實(shí)例
使用如下的代碼可以實(shí)現(xiàn)上面圖片收到實(shí)時(shí)消息的效果闸盔。
//實(shí)時(shí)消息
startWebSocket(){
let this_=this;
let wsUrl=urlConfig.wsUrl+localStorage.getItem("access_token");//websocket接口地址
let ws = new WebSocket(wsUrl);
ws.onclose = function () {
this_.reConnect(wsUrl);//重新連接
console.log("連接已關(guān)閉 "+new Date());
};
ws.onerror = function () {
this_.reConnect(wsUrl);//重新連接
console.log("連接異常 "+new Date());
};
ws.onopen = function () {
heartCheck.reset().start();//開始心跳檢測(cè)
console.log("連接成功 "+new Date());
};
ws.onmessage = function (e) {
heartCheck.reset().start();//開始心跳檢測(cè)
let data = JSON.parse(e.data);
this_.onMessageNotice(data);//獲取到數(shù)據(jù)的回調(diào)
};
/*心跳檢測(cè):
1.如果當(dāng)前連接著(onopen或onMessage),就開始心跳檢測(cè)琳省,即設(shè)置定時(shí)器一段時(shí)間后發(fā)送一條數(shù)據(jù)迎吵,發(fā)送后再過一段時(shí)間關(guān)閉連接躲撰;
如果在關(guān)閉之前,后端正常返回?cái)?shù)據(jù)(會(huì)觸發(fā)onMessage)击费,會(huì)重置心跳檢測(cè)就一直不會(huì)關(guān)閉連接拢蛋。
2.如果當(dāng)前異常或關(guān)閉(onclose,onerror),就重新建立連接蔫巩;
*/
let heartCheck = {
timeout: 180000, //3分鐘發(fā)一次心跳
timeoutObj: null,//定時(shí)器:延時(shí)發(fā)送一次心跳
serverTimeoutObj: null,//定時(shí)器:延時(shí)檢測(cè)后端是否返回?cái)?shù)據(jù)谆棱,如返回則還連著,如未返回則后端斷了
reset: function(){
clearTimeout(this.timeoutObj);
clearTimeout(this.serverTimeoutObj);
return this;
},
start: function(){
let this_ = this;
this.timeoutObj = setTimeout(function(){
ws.send("peng");
console.log("peng");
this_.serverTimeoutObj = setTimeout(function(){//如果超過一定時(shí)間還沒重置批幌,說明后端主動(dòng)斷開了
ws.close(); //如果onclose會(huì)執(zhí)行reconnect础锐,我們執(zhí)行ws.close()就行了.如果直接執(zhí)行reconnect 會(huì)觸發(fā)onclose導(dǎo)致重連兩次
}, this_.timeout)
}, this.timeout)
}
};
}
//重新連接websocket
reConnect(url) {
let this_=this;
if(this.lockReconnect) return;
this.setState({
lockReconnect:true//上鎖
});
setTimeout(function () { //沒連接上會(huì)一直重連,設(shè)置延遲避免請(qǐng)求過多
this_.startWebSocket(url);
this_.setState({
lockReconnect:false//開鎖
});
}, 5000);
}
//收到消息的回調(diào)
onMessageNotice(data){
notification.info({//antd 中的組件
message: "這是一條消息",
description: "消息描述",
});
//收到消息重新獲取消息列表
this.getNewsList();
//播放聲音
this.refs.audio.play();
}