返回介绍

18.2 应对不支持 WebSocket 的场景

发布于 2024-08-17 00:45:49 字数 3886 浏览 0 评论 0 收藏 0

WebSocket是一个相对比较新的规范。虽然它早在2011年底就实现了规范化,但即便如此,在Web浏览器和应用服务器上依然没有得到一致的支持。Firefox和Chrome早就已经完整支持WebSocket了,但是其他的一些浏览器刚刚开始支持WebSocket。如下列出了几个流行的浏览器支持WebSocket功能的最低版本:

Internet Explorer:10.0

Firefox: 4.0(部分支持),6.0(完整支持)。

Chrome: 4.0(部分支持),13.0(完整支持)。

Safari: 5.0(部分支持),6.0(完整支持)。

Opera: 11.0(部分支持),12.10(完整支持)。

iOS Safari: 4.2(部分支持),6.0(完整支持)。

Android Browser: 4.4。

令人遗憾的是,很多的网上冲浪者并没有认识到或理解新Web浏览器的特性,因此升级很慢。另外,有的公司规定使用特定版本的浏览器,这样它们的员工很难(或不可能)使用更新的浏览器。鉴于这些情况,如果你的应用程序使用WebSocket的话,用户可能会无法使用。

服务器端对WebSocket的支持也好不到哪里去。GlassFish在几年前就开始支持一定形式的WebSocket,但是很多其他的应用服务器在最近的版本中刚刚开始支持WebSocket。例如,我在测试上述例子的时候,所使用的就是Tomcat 8的发布候选构建版本。

即便浏览器和应用服务器的版本都符合要求,两端都支持WebSocket,在这两者之间还有可能出现问题。防火墙代理通常会限制所有除HTTP以外的流量。它们有可能不支持或者(还)没有配置允许进行WebSocket通信。

在当前的WebSocket领域,我也许描述了一个很阴暗的前景。但是,不要因为这一些不支持,你就停止使用WebSocket的功能。当它能够正常使用的时候,WebSocket是一项非常棒的技术,但是如果它无法得到支持的话,我们所需要的仅仅是一种备用方案(fallback plan)。

幸好,提到WebSocket的备用方案,这恰是SockJS所擅长的。SockJS是WebSocket技术的一种模拟,在表面上,它尽可能对应WebSocket API,但是在底层它非常智能,如果WebSocket技术不可用的话,就会选择另外的通信方式。SockJS会优先选用WebSocket,但是如果WebSocket不可用的话,它将会从如下的方案中挑选最优的可行方案:

XHR流。

XDR流。

iFrame事件源。

iFrame HTML文件。

XHR轮询。

XDR轮询。

iFrame XHR轮询。

JSONP轮询。

好消息是在使用SockJS之前,我们并没有必要全部了解这些方案。SockJS让我们能够使用统一的编程模型,就好像在各个层面都完整支持WebSocket一样,SockJS在底层会提供备用方案。

例如,为了在服务端启用SockJS通信,我们在Spring配置中可以很简单地要求添加该功能。重新回顾一下程序清单18.2中的registerWebSocketHandlers()方法,稍微加一点内容就能启用SockJS:

addHandler()方法会返回WebSocketHandlerRegistration,通过简单地调用其withSockJS()方法就能声明我们想要使用SockJS功能,如果WebSocket不可用的话,SockJS的备用方案就会发挥作用。

如果你使用XML来配置Spring的话,启用SockJS只需在配置中添加<websocket:sockjs>元素即可:

要在客户端使用SockJS,需要确保加载了SockJS客户端库。具体的做法在很大程度上依赖于使用JavaScript模块加载器(如require.js或curl.js)还是简单地使用<script>标签加载JavaScript库。加载SockJS客户端库的最简单办法是使用<script>标签从SockJS CDN中进行加载,如下所示:

用WebJars解析Web资源

在我的样例代码中,使用了WebJars来解析JavaScript库,使其作为项目Maven或Gradle构建的一部分,就像其他的依赖一样。为了支持该功能,我在Spring MVC配置中搭建了一个资源处理器,让它负责解析路径以“/webjars/**”开头的请求,这也是WebJars的标准路径:

在这个资源处理器准备就绪后,我们可以在Web页面中使用如下的<script>标签加载SockJS库:

注意,这个特殊的<script>标签来源于一个Thymeleaf模板,并使用“@{...}”表达式来为JavaScript文件计算完整的相对于上下文的URL路径。

除了加载SockJS客户端库以外,在程序清单18.4中,要使用SockJS只需修改两行代码:

所做的第一个修改就是URL。SockJS所处理的URL是“http://”或“https://”模式,而不是“ws://”和“wss://”。即便如此,我们还是可以使用相对URL,避免书写完整的全限定URL。在本例中,如果包含JavaScript的页面位于“http://localhost:8080/websocket”路径下,那么给定的“marco”路径将会形成到“http://localhost:8080/websocket/marco”的连接。

但是,这里最核心的变化是创建SockJS实例来代替WebSocket。因为SockJS尽可能地模拟了WebSocket,所以程序清单18.4中的其他代码并不需要变化。相同的onopen、onmessage和onclose事件处理函数用来响应对应的事件,相同的send()方法用来发送“Marco!”到服务器端。

我们并没有改变很多的代码,但是客户端-服务器之间通信的运行方式却有了很大的变化。我们可以完全相信客户端和服务器之间能够进行类似于WebSocket这样的通信,即便浏览器、服务器或位于中间的代理不支持WebSocket,我们也无需再担心了。

WebSocket提供了浏览器-服务器之间的通信方式,当运行环境不支持WebSocket的时候,SockJS提供了备用方案。但是不管哪种场景,对于实际应用来说,这种通信形式都显得层级过低。让我们看一下如何在WebSocket之上使用STOMP(Simple Text Oriented Messaging Protocol),为浏览器-服务器之间的通信增加恰当的消息语义。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文