WebSocket原理及Tomcat的实现是怎样的

WebSocket原理及Tomcat的实现是怎样的 ,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。

目前创新互联公司已为近1000家的企业提供了网站建设、域名、网站空间、网站托管、服务器托管、企业网站设计、南城网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。

现如今,许多场景下需要实现从服务端到客户端的主动推送消息。而对于传统的HTTP,我们都了解,其必须是要通过主动的请求,每个Request对应一个Response,此时要实现服务端推,必须要有一个主动的请求。

为此,人们想出了ajax长轮询,长连接等一系列方式,但对比长轮询的不断无效的请求,都不如使用我们今天提到的更方便且不消耗资源实现。

对比HTTP请求,比较明显的你会感觉到,无论是异步还是同步请求,对于HTTP,在开发者工具中你都能观察到应用是新发了一个请求到服务器,之后根据返回的信息进行处理展示的。

而WebSocket,则在第一次握手建立连接之后,后续的收发消息,都不再重新建立连接,也就是你观察不到它后续的请求了。

这也是HTTP与WebSocket的区别。

而在Tomcat内部,我们来看Websocket是如何生效及工作的。

首先,来看WebSocket是如何初始化的。

无论哪种类型的请求,都会在ApplicationFilterChain中进行处理,根据是否配置Filter来决定整个处理的流向。(前面曾介绍过Filter的工作原理及请求流程:责任链模式及Filter的工作原理)。而无论哪个应用,其实Tomcat内部都会为其默认添加这样一个Filter:

WsFilter

这个Filter就是用来处理WebSocket的,但又不全是,因为它的filter-mapping是

/*

FilterRegistration.Dynamicfr = servletContext.addFilter(

                "Tomcat WebSocket (JSR356) Filter", new WsFilter());

        fr.setAsyncSupported(true);

        EnumSet 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.(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样例,来体验一下吧!

看完上述内容是否对您有帮助呢?如果还想对相关知识有进一步的了解或阅读更多相关文章,请关注创新互联行业资讯频道,感谢您对创新互联的支持。


当前名称:WebSocket原理及Tomcat的实现是怎样的
文章URL:http://azwzsj.com/article/ijespp.html