溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

WebSocket原理及Tomcat的實(shí)現(xiàn)是怎樣的

發(fā)布時(shí)間:2021-11-15 16:08:33 來源:億速云 閱讀:314 作者:柒染 欄目:大數(shù)據(jù)

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ā)者工具,我們可以觀察到:

WebSocket原理及Tomcat的實(shí)現(xiàn)是怎樣的

是通過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ì)億速云的支持。

向AI問一下細(xì)節(jié)

免責(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)容。

AI