連夜擼了一個(gè)簡易聊天室_第1頁
連夜擼了一個(gè)簡易聊天室_第2頁
連夜擼了一個(gè)簡易聊天室_第3頁
連夜擼了一個(gè)簡易聊天室_第4頁
連夜擼了一個(gè)簡易聊天室_第5頁
已閱讀5頁,還剩17頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

連夜擼了一個(gè)簡易聊天室分不清輪詢、長輪詢?不知道什么時(shí)候該用websocket還是SSE,看這篇就夠了。所謂的“實(shí)時(shí)推送”,從表面意思上來看是,客戶端訂閱的內(nèi)容在發(fā)生改變時(shí),服務(wù)器能夠?qū)崟r(shí)地通知客戶端,進(jìn)而客戶端進(jìn)行相應(yīng)地反應(yīng)。客戶端不需要主觀地發(fā)送請求去獲取自己關(guān)心的內(nèi)容,而是由服務(wù)器端進(jìn)行“推送”。注意上面的推送二字打了引號,這就意味著在現(xiàn)有的幾種實(shí)現(xiàn)方式中,并不是服務(wù)器端主動(dòng)地推送,而是通過一定的手段營造了一種實(shí)時(shí)的假象。就目前現(xiàn)有的幾種技術(shù)而言,主要有以下幾類:客戶端輪詢:傳統(tǒng)意義上的輪詢(ShortPolling)服務(wù)器端輪詢:長輪詢(LongPolling)全雙工通信:Websocket單向服務(wù)器推送:Server-SentEvents(SSE)文中會以一個(gè)簡易聊天室的例子來分別通過上述的四種方式實(shí)現(xiàn),代碼地址/Rynxiao/mini-chatroom(存在些許bug,主要是為了做演示用)overview輪詢(ShortPolling)輪詢的實(shí)現(xiàn)原理:客戶端向服務(wù)器端發(fā)送一個(gè)請求,服務(wù)器返回?cái)?shù)據(jù),然后客戶端根據(jù)服務(wù)器端返回的數(shù)據(jù)進(jìn)行處理;然后客戶端繼續(xù)向服務(wù)器端發(fā)送請求,繼續(xù)重復(fù)以上的步驟,如果不想給服務(wù)器端太大的壓力,一般情況下會設(shè)置一個(gè)請求的時(shí)間間隔。shortPolling使用輪詢明顯的優(yōu)點(diǎn)是基礎(chǔ)不需要額外的開發(fā)成本,請求數(shù)據(jù),解析數(shù)據(jù),作出響應(yīng),僅此而已,然后不斷重復(fù)。缺點(diǎn)也顯而易見:不斷的發(fā)送和關(guān)閉請求,對服務(wù)器的壓力會比較大,因?yàn)楸旧黹_啟Http連接就是一件比較耗資源的事情輪詢的時(shí)間間隔不好控制。如果要求的實(shí)時(shí)性比較高,顯然使用短輪詢會有明顯的短板,如果設(shè)置interval的間隔過長,會導(dǎo)致消息延遲,而如果太短,會對服務(wù)器產(chǎn)生壓力代碼實(shí)現(xiàn)var

ShortPollingNotification

=

{

datasInterval:

null,

subscribe:

function()

{

this.datasInterval

=

setInterval(function()

{

Request.getDatas().then(function(res)

{

window.ChatroomDOM.renderData(res);

});

},

TIMEOUT);

return

this.unsubscribe;

},

unsubscribe:

function()

{

this.datasInterval

&&

clearInterval(this.datasInterval);

}

}shortPolling下面是對應(yīng)的請求,注意左下角的請求數(shù)量一直在變化shortNetwork在上圖中,每隔1s就會發(fā)送一個(gè)請求,看起來效果還不錯(cuò),但是如果將timeout的值設(shè)置成5s,效果將大打折扣,如圖:shortPolling5s長輪詢(LongPolling)長輪詢的基本原理:客戶端發(fā)送一個(gè)請求,服務(wù)器會hold住這個(gè)請求,直到監(jiān)聽的內(nèi)容有改變,才會返回?cái)?shù)據(jù),斷開連接,客戶端繼續(xù)發(fā)送請求,重復(fù)以上步驟。或者在一定的時(shí)間內(nèi),請求還得不到返回,就會因?yàn)槌瑫r(shí)自動(dòng)斷開連接。longPolling長輪詢是基于輪詢上的改進(jìn)版本,主要是減少了客戶端發(fā)起Http連接的開銷,改成了在服務(wù)器端主動(dòng)地去判斷所關(guān)心的內(nèi)容是否變化,所以其實(shí)輪詢的本質(zhì)并沒有多大變化,變化的點(diǎn)在于:搜索公眾號后端架構(gòu)師后臺回復(fù)“面試”,獲取一份驚喜禮包。對于內(nèi)容變化的輪詢由客戶端改成了服務(wù)器端(客戶端會在連接中斷之后,會再次發(fā)送請求,對比短輪詢來說,大大減少了發(fā)起連接的次數(shù))客戶端只會在數(shù)據(jù)改變時(shí)去作相應(yīng)的改變,對比短輪詢來說,并不是全盤接收代碼實(shí)現(xiàn)//

客戶端

var

LongPollingNotification

=

{

//

....

subscribe:

function()

{

var

that

=

this;

//

設(shè)置超時(shí)時(shí)間

Request.getV2Datas(this.getKey(),{

timeout:

10000

}).then(function(res)

{

var

data

=

res.data;

window.ChatroomDOM.renderData(res);

//

成功獲取數(shù)據(jù)后會再次發(fā)送請求

that.subscribe();

}).catch(function

(error)

{

//

timeout

之后也會再次發(fā)送請求

that.subscribe();

});

return

this.unsubscribe;

}

//

....

}筆者采用的是express,默認(rèn)不支持hold住請求,因此用了一個(gè)express-longpoll的庫來實(shí)現(xiàn)。下面是一個(gè)原生不用庫的實(shí)現(xiàn)(這里只是介紹原理),整體的思路是:如果服務(wù)器端支持hold住請求的話,那么在一定的時(shí)間內(nèi)會自輪詢,然后期間通過比較key值,判斷是否返回新數(shù)據(jù)客戶端第一次會帶一個(gè)空的key值,這次會立即返回,獲取新內(nèi)容,服務(wù)器端將計(jì)算出的contentKey返回給客戶端然后客戶端發(fā)送第二次請求,帶上第一次返回的contentKey作為key值,然后進(jìn)行下一輪的比較如果兩次的key值相同,就會hold請求,進(jìn)行內(nèi)部輪詢,如果期間有新內(nèi)容或者客戶端timeout,就會斷開連接重復(fù)以上步驟//

服務(wù)器端

router.get('/v2/datas',

function(req,

res)

{

const

key

=

_.get(req.query,

'key',

'');

let

contentKey

=

chatRoom.getContentKey();

while

(key

===

contentKey)

{

sleep.sleep(5);

contentKey

=

chatRoom.getContentKey();

}

const

connectors

=

chatRoom.getConnectors();

const

messages

=

chatRoom.getMessages();

res.json({

code:

200,

data:

{

connectors:

connectors,

messages:

messages,

key:

contentKey

},

});

});以下是用

express-longpoll

的實(shí)現(xiàn)片段//

mini-chatroom/public/javascripts/server/longPolling.js

function

pushDataToClient(key,

longpoll)

{

var

contentKey

=

chatRoom.getContentKey();

if

(key

!==

contentKey)

{

var

connectors

=

chatRoom.getConnectors();

var

messages

=

chatRoom.getMessages();

longpoll.publish(

'/v2/datas',

{

code:

200,

data:

{connectors:

connectors,

messages:

messages,

key:

contentKey},

}

);

}

}

longpoll.create("/v2/datas",

function(req,

res,

next)

{

key

=

_.get(req.query,

'key',

'');

pushDataToClient(key,

longpoll);

next();

});

intervalId

=

setInterval(function()

{

pushDataToClient(key,

longpoll);

},

LONG_POLLING_TIMEOUT);為了方便演示,我將客戶端發(fā)起請求的timeout改成了4s,注意觀察下面的截圖:longPollingNetwork可以看到,斷開連接的兩種方式,要么是超時(shí),要么是請求有數(shù)據(jù)返回。基于iframe的長輪詢模式這種模式的具體的原理為:在頁面中嵌入一個(gè)iframe,地址指向輪詢的服務(wù)器地址,然后在父頁面中放置一個(gè)執(zhí)行函數(shù),比如execute(data)當(dāng)服務(wù)器有內(nèi)容改變時(shí),會向iframe發(fā)送一個(gè)腳本通過發(fā)送的腳本,主動(dòng)執(zhí)行父頁面中的方法,達(dá)到推送的效果具體可以參看:https://juejin.im/post/6844903955240058893#heading-4WebsocketTheWebSocketProtocolenablestwo-waycommunicationbetweenaclientrunninguntrustedcodeinacontrolledenvironmenttoaremotehostthathasopted-intocommunicationsfromthatcode.Theprotocolconsistsofanopeninghandshakefollowedbybasicmessageframing,layeredoverTCP.Thegoalofthistechnologyistoprovideamechanismforbrowser-basedapplicationsthatneedtwo-waycommunicationwithserversthatdoesnotrelyonopeningmultipleHTTPconnections(e.g.,usingXMLHttpRequestoriframeandlongpolling).TheWebSocketProtocolattemptstoaddressthegoalsofexistingbidirectionalHTTPtechnologiesinthecontextoftheexistingHTTPinfrastructure;assuch,itisdesignedtoworkoverHTTPports80and443aswellastosupportHTTPproxiesandintermediaries,evenifthisimpliessomecomplexityspecifictothecurrentenvironment.特征websocket是雙向通信的,設(shè)計(jì)的目的主要是為了減少傳統(tǒng)輪詢時(shí)http連接數(shù)量的開銷建立在TCP協(xié)議之上,握手階段采用HTTP協(xié)議,因此握手時(shí)不容易屏蔽,能通過各種HTTP代理服務(wù)器與HTTP兼容性良好,同樣可以使用80和443端口沒有同源限制,客戶端可以與任意服務(wù)器通信可以發(fā)送文本,也可以發(fā)送二進(jìn)制數(shù)據(jù)。協(xié)議標(biāo)識符是ws(如果加密,則為wss),服務(wù)器網(wǎng)址就是URLwebsocket關(guān)于WebsocketAPI方面的知識,這里不再作講解,可以自己查閱WebsocketAPIMDN兼容性websocket兼容性良好,基本支持所有現(xiàn)代瀏覽器websocket1代碼實(shí)現(xiàn)筆者這里采用的是socket.io,是基于websocket的封裝,提供了客戶端以及服務(wù)器端的支持//

客戶端

var

WebsocketNotification

=

{

//

...

subscribe:

function(args)

{

var

connector

=

args[1];

this.socket

=

io();

this.socket.emit('register',

connector);

this.socket.on('register

done',

function()

{

window.ChatroomDOM.renderAfterRegister();

});

this.socket.on('data',

function(res)

{

window.ChatroomDOM.renderData(res);

});

this.socket.on('disconnect',

function()

{

window.ChatroomDOM.renderAfterLogout();

});

}

//

...

}

//

服務(wù)器端

var

io

=

socketIo(httpServer);

io.on('connection',

(socket)

=>

{

socket.on('register',

function(connector)

{

chatRoom.onConnect(connector);

io.emit('register

done');

var

data

=

chatRoom.getDatas();

io.emit('data',

{

data

});

});

socket.on('chat',

function(message)

{

chatRoom.receive(message);

var

data

=

chatRoom.getDatas();

io.emit('data',

{

data

});

});

});響應(yīng)格式如下:websocket-request-responseServer-SentEvents(SSE)傳統(tǒng)意義上服務(wù)器端不會主動(dòng)推送給客戶端消息,一般都是客戶端主動(dòng)去請求服務(wù)器端獲取最新的數(shù)據(jù)。SSE就是一種可以主動(dòng)從服務(wù)端推送消息的技術(shù)。搜索公眾號頂級架構(gòu)師后臺回復(fù)“架構(gòu)整潔”,獲取一份驚喜禮包。SSE的本質(zhì)其實(shí)就是一個(gè)HTTP的長連接,只不過它給客戶端發(fā)送的不是一次性的數(shù)據(jù)包,而是一個(gè)stream流,格式為text/event-stream,所以客戶端不會關(guān)閉連接,會一直等著服務(wù)器發(fā)過來的新的數(shù)據(jù)流,視頻播放就是這樣的例子。SSE使用HTTP協(xié)議,現(xiàn)有的服務(wù)器軟件都支持。WebSocket是一個(gè)獨(dú)立協(xié)議。SSE屬于輕量級,使用簡單;WebSocket協(xié)議相對復(fù)雜。SSE默認(rèn)支持?jǐn)嗑€重連,WebSocket需要自己實(shí)現(xiàn)。SSE一般只用來傳送文本,二進(jìn)制數(shù)據(jù)需要編碼后傳送,WebSocket默認(rèn)支持傳送二進(jìn)制數(shù)據(jù)。SSE支持自定義發(fā)送的消息類型?;镜氖褂梅椒ǎ瑓⒖碨SEAPIsse兼容性目前除了IE以及低版本的瀏覽器不支持,基本支持絕大多數(shù)的現(xiàn)代瀏覽器。sse2代碼實(shí)現(xiàn)//

客戶端

var

SSENotification

=

{

source:

null,

subscribe:

function()

{

if

('EventSource'

in

window)

{

this.source

=

new

EventSource('/sse');

this.source.addEventListener('message',

function(res)

{

const

d

=

res.data;

window.ChatroomDOM.renderData(JSON.parse(d));

});

}

return

this.unsubscribe;

},

unsubscribe:

function

()

{

this.source

&&

this.source.close();

}

}

//

服務(wù)器端

router.get('/sse',

function(req,

res)

{

const

connectors

=

chatRoom.getConnectors();

const

messages

=

chatRoom.getMessages();

const

response

=

{

code:

200,

data:

{

connectors:

connectors,

messages:

messages

}

};

res.writeHead(200,

{

"Content-Type":"text/event-stream",

"Cache-Control":"no-cache",

"Connection":"keep-alive",

"Access-Control-Allow-Origin":

'*',

});

res.write("retry:

10000\n");

res.write("data:

"

+

JSON.stringify

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論