您好,登錄后才能下訂單哦!
WebSocket原理及Tomcat的實(shí)現(xiàn)是怎樣的 ,很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來學(xué)習(xí)下,希望你能有所收獲。
現(xiàn)如今,許多場(chǎng)景下需要實(shí)現(xiàn)從服務(wù)端到客戶端的主動(dòng)推送消息。而對(duì)于傳統(tǒng)的HTTP,我們都了解,其必須是要通過主動(dòng)的請(qǐng)求,每個(gè)Request對(duì)應(yīng)一個(gè)Response,此時(shí)要實(shí)現(xiàn)服務(wù)端推,必須要有一個(gè)主動(dòng)的請(qǐng)求。
為此,人們想出了ajax長輪詢,長連接等一系列方式,但對(duì)比長輪詢的不斷無效的請(qǐng)求,都不如使用我們今天提到的更方便且不消耗資源實(shí)現(xiàn)。
對(duì)比HTTP請(qǐng)求,比較明顯的你會(huì)感覺到,無論是異步還是同步請(qǐng)求,對(duì)于HTTP,在開發(fā)者工具中你都能觀察到應(yīng)用是新發(fā)了一個(gè)請(qǐng)求到服務(wù)器,之后根據(jù)返回的信息進(jìn)行處理展示的。
而WebSocket,則在第一次握手建立連接之后,后續(xù)的收發(fā)消息,都不再重新建立連接,也就是你觀察不到它后續(xù)的請(qǐng)求了。
這也是HTTP與WebSocket的區(qū)別。
而在Tomcat內(nèi)部,我們來看Websocket是如何生效及工作的。
首先,來看WebSocket是如何初始化的。
無論哪種類型的請(qǐng)求,都會(huì)在ApplicationFilterChain中進(jìn)行處理,根據(jù)是否配置Filter來決定整個(gè)處理的流向。(前面曾介紹過Filter的工作原理及請(qǐng)求流程:責(zé)任鏈模式及Filter的工作原理)。而無論哪個(gè)應(yīng)用,其實(shí)Tomcat內(nèi)部都會(huì)為其默認(rèn)添加這樣一個(gè)Filter:
WsFilter
這個(gè)Filter就是用來處理WebSocket的,但又不全是,因?yàn)樗膄ilter-mapping是
/*
FilterRegistration.Dynamic fr = servletContext.addFilter(
"Tomcat WebSocket (JSR356) Filter", new WsFilter());
fr.setAsyncSupported(true);
EnumSet<DispatcherType> types = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD);
fr.addMappingForUrlPatterns(types, true, "/*");
上面的代碼即是使用Servlet3.0新增加的動(dòng)態(tài)聲明Filter的實(shí)現(xiàn)方式,把WsFilter這個(gè)動(dòng)態(tài)增加到應(yīng)用的filter鏈中。
而這個(gè)Filter中,也是在入口處判斷,只有WebSocket的請(qǐng)求才處理,其它的就跳過了
// This filter only needs to handle WebSocket upgrade requests
if (!sc.areEndpointsRegistered() ||
!UpgradeUtil.isWebSocketUpgradeRequest(request, response)) {
chain.doFilter(request, response);
return;
}
添加Filter這一行為,是在應(yīng)用啟動(dòng)的時(shí)候執(zhí)行的,調(diào)用棧如下:
at org.apache.catalina.core.StandardContext.addFilterMap(StandardContext.java:2836)
at org.apache.catalina.core.ApplicationFilterRegistration.addMappingForUrlPatterns(ApplicationFilterRegistration.java:104)
at org.apache.tomcat.websocket.server.WsServerContainer.<init>(WsServerContainer.java:141)
at org.apache.tomcat.websocket.server.WsSci.init(WsSci.java:131)
at org.apache.tomcat.websocket.server.WsSci.onStartup(WsSci.java:47)
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5151)
請(qǐng)求連接建立
在數(shù)據(jù)發(fā)送之前,需要先建立連接。而WebSocket本質(zhì)上仍然是TCP連接,雖然看起來是通過HTTP,只是因?yàn)槠涑醮蔚奈帐质切枰ㄟ^HTTP來建立。
我們以Tomcat自帶的websocket樣例中的echo例子來說明WebSocket的使用方式以及其在Tomcat內(nèi)部的實(shí)現(xiàn)形式。
echo例子位于:
TOMCAT_HOME\webapps\examples\websocket
java代碼位于:
TOMCAT_HOME\webapps\examples\WEB-INF\classes\websocket\echo
代碼中,對(duì)于connect和echo message的實(shí)現(xiàn)如下:
function connect() {
var target = document.getElementById('target').value;
if (target == '') {
alert('Please select server side connection implementation.');
return;
}
if ('WebSocket' in window) {
ws = new WebSocket(target);
} else if ('MozWebSocket' in window) {
ws = new MozWebSocket(target);
} else {
alert('WebSocket is not supported by this browser.');
return;
}
ws.onopen = function () {
setConnected(true);
log('Info: WebSocket connection opened.');
};
ws.onmessage = function (event) {
log('Received: ' + event.data);
};
ws.onclose = function (event) {
setConnected(false);
log('Info: WebSocket connection closed, Code: ' + event.code + (event.reason == "" ? "" : ", Reason: " + event.reason));
};
}
我們看到,整個(gè)websocket對(duì)象會(huì)處理三個(gè)事件:
connect
message
close
而信息的發(fā)送,是直接使用websocket的send方法。
在初次連接握手時(shí),通過開發(fā)者工具,我們可以觀察到:
是通過Upgrade來進(jìn)行協(xié)議的切換,同時(shí)連接到WebSocket 的Server上去的。
而此時(shí)的Upgrade就是通過我們前面提到的Filter來進(jìn)行的。
在Filter中,通過UgradeUtil的doUpgrade方法進(jìn)行后續(xù)關(guān)于WebSocket規(guī)范的調(diào)用實(shí)現(xiàn)。
而對(duì)于WebSocket Server 的支持,我們通過Echo的例子可以看到,可以直接使用Endpoint的類來進(jìn)行,也可以通過注解的等式進(jìn)行。
例如下面的代碼就是通過注解的形式,聲明了一個(gè)Websocket的Endpoint
@ServerEndpoint("/websocket/echoAnnotation")public class EchoAnnotation {
@OnMessage
public void echoTextMessage(Session session, String msg, boolean last) {
try {
if (session.isOpen()) {
session.getBasicRemote().sendText(msg, last);
}
} catch (IOException e) {
}
}
根據(jù)注解,Tomcat內(nèi)部會(huì)生成一個(gè)PojoServer,并使用反射調(diào)用當(dāng)前標(biāo)注有@OnMessage的方法。
而請(qǐng)求的分發(fā),我們?cè)谇懊娼榻BConnector的時(shí)候,曾簡單說過是經(jīng)過
Endpoint -> Handler -> Protocol
(Tomcat的Connector組件)
對(duì)于不同的請(qǐng)求,Protocol中進(jìn)行不同的轉(zhuǎn)發(fā),
} else if (processor.isAsync() ||
state == SocketState.ASYNC_END) {
state = processor.asyncDispatch(status);
} else if (processor.isComet()) {
state = processor.event(status);
} else if (processor.isUpgrade()) {
state = processor.upgradeDispatch(status);
} else if (status == SocketStatus.OPEN_WRITE) {
// Extra write event likely after async, ignore
state = SocketState.LONG;
}
在建立連接之后,后續(xù)再進(jìn)行的數(shù)據(jù)發(fā)送,通過開發(fā)者工具已經(jīng)觀察不到任何的請(qǐng)求了,這也是WebSocket之所以可以實(shí)現(xiàn)服務(wù)器推送的主要原因。其本質(zhì)上在建立連接后,已經(jīng)不再是一個(gè)HTTP請(qǐng)求了,而是一個(gè)TCP連接。
而且因于WebSocket在HTML5中的規(guī)范實(shí)現(xiàn),各個(gè)主流瀏覽器的支持,現(xiàn)在多數(shù)的應(yīng)用服務(wù)器也都已經(jīng)根據(jù)規(guī)范進(jìn)行了支持。許多要實(shí)現(xiàn)服務(wù)器推的場(chǎng)景也可以考慮使用WebSocket來實(shí)現(xiàn)。
打開Tomcat的webSocket樣例,來體驗(yàn)一下吧!
看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。