您好,登錄后才能下訂單哦!
WebSocket原理及Tomcat的實現是怎樣的 ,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
現如今,許多場景下需要實現從服務端到客戶端的主動推送消息。而對于傳統的HTTP,我們都了解,其必須是要通過主動的請求,每個Request對應一個Response,此時要實現服務端推,必須要有一個主動的請求。
為此,人們想出了ajax長輪詢,長連接等一系列方式,但對比長輪詢的不斷無效的請求,都不如使用我們今天提到的更方便且不消耗資源實現。
對比HTTP請求,比較明顯的你會感覺到,無論是異步還是同步請求,對于HTTP,在開發者工具中你都能觀察到應用是新發了一個請求到服務器,之后根據返回的信息進行處理展示的。
而WebSocket,則在第一次握手建立連接之后,后續的收發消息,都不再重新建立連接,也就是你觀察不到它后續的請求了。
這也是HTTP與WebSocket的區別。
而在Tomcat內部,我們來看Websocket是如何生效及工作的。
首先,來看WebSocket是如何初始化的。
無論哪種類型的請求,都會在ApplicationFilterChain中進行處理,根據是否配置Filter來決定整個處理的流向。(前面曾介紹過Filter的工作原理及請求流程:責任鏈模式及Filter的工作原理)。而無論哪個應用,其實Tomcat內部都會為其默認添加這樣一個Filter:
WsFilter
這個Filter就是用來處理WebSocket的,但又不全是,因為它的filter-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新增加的動態聲明Filter的實現方式,把WsFilter這個動態增加到應用的filter鏈中。
而這個Filter中,也是在入口處判斷,只有WebSocket的請求才處理,其它的就跳過了
// This filter only needs to handle WebSocket upgrade requests
if (!sc.areEndpointsRegistered() ||
!UpgradeUtil.isWebSocketUpgradeRequest(request, response)) {
chain.doFilter(request, response);
return;
}
添加Filter這一行為,是在應用啟動的時候執行的,調用棧如下:
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)
請求連接建立
在數據發送之前,需要先建立連接。而WebSocket本質上仍然是TCP連接,雖然看起來是通過HTTP,只是因為其初次的握手是需要通過HTTP來建立。
我們以Tomcat自帶的websocket樣例中的echo例子來說明WebSocket的使用方式以及其在Tomcat內部的實現形式。
echo例子位于:
TOMCAT_HOME\webapps\examples\websocket
java代碼位于:
TOMCAT_HOME\webapps\examples\WEB-INF\classes\websocket\echo
代碼中,對于connect和echo message的實現如下:
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));
};
}
我們看到,整個websocket對象會處理三個事件:
connect
message
close
而信息的發送,是直接使用websocket的send方法。
在初次連接握手時,通過開發者工具,我們可以觀察到:
是通過Upgrade來進行協議的切換,同時連接到WebSocket 的Server上去的。
而此時的Upgrade就是通過我們前面提到的Filter來進行的。
在Filter中,通過UgradeUtil的doUpgrade方法進行后續關于WebSocket規范的調用實現。
而對于WebSocket Server 的支持,我們通過Echo的例子可以看到,可以直接使用Endpoint的類來進行,也可以通過注解的等式進行。
例如下面的代碼就是通過注解的形式,聲明了一個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) {
}
}
根據注解,Tomcat內部會生成一個PojoServer,并使用反射調用當前標注有@OnMessage的方法。
而請求的分發,我們在前面介紹Connector的時候,曾簡單說過是經過
Endpoint -> Handler -> Protocol
(Tomcat的Connector組件)
對于不同的請求,Protocol中進行不同的轉發,
} 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;
}
在建立連接之后,后續再進行的數據發送,通過開發者工具已經觀察不到任何的請求了,這也是WebSocket之所以可以實現服務器推送的主要原因。其本質上在建立連接后,已經不再是一個HTTP請求了,而是一個TCP連接。
而且因于WebSocket在HTML5中的規范實現,各個主流瀏覽器的支持,現在多數的應用服務器也都已經根據規范進行了支持。許多要實現服務器推的場景也可以考慮使用WebSocket來實現。
打開Tomcat的webSocket樣例,來體驗一下吧!
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。