限时领|《AI 百问百答》专栏课+实体书(包邮)! 了解详情
写点什么

WebSocket 与 Java

  • 2013-12-22
  • 本文字数:2455 字

    阅读完需:约 8 分钟

Bozhidar Bozhanov 是 Ontotext AD 的高级软件工程师,拥有多年的从业经验,也是 stackoverflow 上的活跃用户。他精通于 Java 与 Java 技术栈,如 Spring、JPA、JavaEE 等,同时还是 http://computoser.com http://welshare.com 的创始人。曾开发过爱立信的项目、保加利亚电子政务项目以及大型招聘平台等。近日 Bozhidar撰文谈到了WebSocket 与Java,并给出了相应的代码示例。Bozhidar 在文中详细分析了WebSocket 的原理、适用范围,以及如何通过Java 来使用WebSocket。

WebSocket 是一个很酷的新技术,可以实现浏览器与服务器之间实时、双向的通信,几乎没有任何额外的代价。我这里要做的事情就是提供一个非常简洁,但却内容丰富的概览,介绍如何开始使用这门技术。首先读者需要了解如下一些事情:

  • 首先在浏览器与服务器之间需要开启一个 TCP Socket 连接,每一方都可以向对方发送消息(比如说,服务器可以在有数据时将其推送出去,无需使用轮询、长轮询、iframes 等技术)。
  • 并不是所有浏览器都支持 WebSocket 技术,IE 10 是首个支持 WebSocket 的 IE 版本,Android 依然还存在着一些问题。幸好有 SockJS ,在不支持 WebSocket 的情况下,它会退回到其他的推送技术。
  • 并不是所有的代理服务器都支持或是允许 WebSocket,因此这时还是需要退回到其他的推送技术。
  • WebSocket 适合于游戏、交易型应用,事实上,它适合于任何服务器要向浏览器推送数据的场景。
  • Java 有标准 API( JSR-356 ),你可以在服务端通过它来处理 WebSocket 连接。
  • Spring 在 Java API 的基础之上提供了一个 API 。对于 Spring 提供的支持来说,好消息是它对 SockJS 提供了服务器端的支持,你也可以毫无压力地使用依赖注入。Spring 还对消息驱动的架构提供了 STOMP 支持。上面的两篇 Spring 文章都给出了 GitHub 上示例项目的链接,我强烈建议大家看看。
  • 长久以来, Atmosphere 框架一直是服务器推送技术的解决方案。这里是WebSocket 上手指南。另外,Cometd 也提供了 WebSocket 支持

在给出具体的示例代码前,我首先来介绍一下 Socket 的生命周期,包括客户端的与服务器端的:

  1. 浏览器发出一个 HTTP 请求,带有一个特殊的 Upgrade 头,其值是“websocket”。
  2. 如果服务器能够“理解”WebSocket,那么它会使用状态 101 进行应答——交换协议。从现在开始,我们就不再使用 HTTP 了。
  3. 当服务器接收这个 TCP Socket 连接后,一个初始化方法会得到调用,当前的 WebSocket Session 会被传递进来。每个 Socket 都有唯一一个 Session id。
  4. 当浏览器向服务器发送消息时,另一个方法会得到调用,你在这里获得 Session 与消息负载。
  5. 根据某个负载参数,应用代码会执行一个动作。负载的格式完全取决于开发者。一般来说会使用 JSON 序列化的对象。
  6. 当服务器需要发送消息时,它需要获得这个 Session 对象,然后通过它来发送消息。
  7. 当浏览器关闭连接时,服务器会得到通知,这样它就可以清理与特定 Session 关联的一些资源了。

目前,还没有任何一个 API 或框架能够支持基于注解的路由。Java API 支持基于注解的端点处理器,不过每个连接 URL 需要一个类来处理,通常情况下,你希望在单个连接上执行多个操作。也就是说,你连接到 ws://yourserver.com/game/,然后想要传递“joinGame”和“leaveGame”等消息。类似地,服务器需要发送回多种类型的消息。我使用了枚举来实现这一点,枚举中包含了所有可能的动作与事件类型,然后使用 switch 来确定该调用哪一个。

因此,我决定为我的算法音乐作曲家开发一个简单的游戏。它使用了 Spring API,感兴趣的读者可以看看这个介绍,这是我在公司所做的一次演讲。下面是一些示例代码:

复制代码
@Component
public class GameHandler extends WebSocketHandlerAdapter {
private Map

下面来看一个示例场景,其中服务器需要向客户端发送消息。这就好比一个玩家加入了游戏一样,这时其他所有玩家都会收到有新人加入的通知。系统中的中心类是 Game,它拥有一个玩家列表。如你所见,一个 Player 包含了一个对 WebSocket Session 的引用。这样,当新的玩家加入时,下面的 Game 中的方法就会得到调用:

复制代码
public boolean playerJoined(Player player) {
for (Player otherPlayer : players.values()) {
otherPlayer.playerJoined(player);
}
players.put(player.getSession().getId(), player);
return true;
}

player.playerJoined(…) 会在连接之上发送一条消息,通知浏览器有新的玩家加入了:

复制代码
public void playerJoined(Player player) {
GameEvent event = new GameEvent(GameEventType.PLAYER_JOINED);
event.setPlayerId(player.getSession().getId());
event.setPlayerName(player.getName());
try {
session.sendMessage(new TextMessage(event.toJson()));
} catch (IOException e) {
new IllegalStateException(e);
}
}

从服务器向浏览器发送消息可能还需要一个调度 job 进行触发。

关键在于你维护了一个所有已连接的浏览器列表,这样就可以向回发送信息了。这个列表可以是个静态属性,不过对于单例的 Spring Bean 来说就没必要这么做了。

现在,有两个重要的方面需要我们注意——安全与认证。这是来自于Heroku 的一篇很不错的文章,对安全与认证进行了详细的介绍。如果还有其他敏感信息,你就应该使用wss(Websocket over TLS)了。你还应该在服务器端与客户端验证输入,而不应该依赖于Origin 头,因为攻击者可以轻而易举地骗过浏览器。

认证可以依赖于HTTP Session cookie,不过显而易见的是,有些人更喜欢实现自己的类cookie 工作流,从而获取一个短暂的令牌,它可以用于执行认证操作。

WebSocket 使得 DDD 变得更加自然。你不必再处理贫血对象了,你的对象有各自的状态以及对这些状态的操作。与之相关的是,WebSocket 应用的测试变得更加容易了。

在开发 WebSocket 应用时还有不少需要注意的事情。值得注意的是,你没有必要在任何地方都使用 WebSocket,我将其限定在需要“推送”的场景下。

总的来说,WebSocket 是一个很棒、很有趣的技术,它非常有希望灭掉所有采用 hack 手段实现的推送模拟技术。

2013-12-22 10:158043
用户头像

发布了 88 篇内容, 共 269.3 次阅读, 收获喜欢 8 次。

关注

评论

发布
暂无评论
发现更多内容

怎样使用过程自动化来实现过程的习惯性和持久性?

IPD产品研发管理

自动化 开发 CMMI

百度大脑开放日厦门站-企业服务专场报名

百度大脑

百度大脑 开放日 企业服务

干好开发者关系的十个职业发展秘诀

开发者关系

开发者关系 技术运营 DevRel

IM扫码登录技术专题(三):通俗易懂,IM扫码登录功能详细原理一篇就够

JackJiang

即时通讯 IM 扫码

苹果移动设备用什么管理比较好?有什么推荐?

懒得勤快

imazing 手机管理

【技术干货】文件系统中的“锁”

焱融科技

容器 分布式 云原生 高性能 文件存储

图算法系列之计算图中最短路径

Silently9527

数据结构和算法 图算法 广度优先搜素

414天前,我以为这是编程玄学...

why技术

Java JVM JMM

【智慧农业】从“看天吃饭”到“知天而作”,乡村振兴全靠 TA

IoT云工坊

人工智能 物联网 智慧农业 庭院灌溉 温室大棚

Nginx基础配置-资源缓存配置

梁龙先森

nginx 大前端 缓存;

秘笈分享! 24 小时无人自习室为什么这么火?

IoT云工坊

小程序 人工智能 物联网 无人自习室

GitHub开源的文言文编程语言、程序生成中国山水画、格律诗编辑程序

不脱发的程序猿

GitHub 开源 编程语言 传统文化

Java程序员面试必备——过得了面试官,过不了HR?我教你

比伯

Java 编程 架构 程序人生 计算机

停止维护的CentOS6,怎么使用yum?

运维研习社

Linux 5月日更

一文带你全面了解java对象的序列化和反序列化

华为云开发者联盟

Java 序列化 java对象 反序列化 Serializable接口

读完你就知道对话式人工智能的数据采集如何解决啦!

澳鹏Appen

人工智能 自然语言处理 聊天机器人 nlp nlu

【LeetCode】叶子相似的树Java题解

Albert

算法 LeetCode 5月日更

青海大学智慧微能源数字孪生可视化系统

ThingJS数字孪生引擎

大前端 可视化 3D可视化 数字孪生

JavaScript设计模式之单例模式

程序员海军

JavaScript 大前端 设计模式 单例模式

一线大厂最新总结Spring Security Oauth2.0认证授权全彩笔记

Java架构追梦

Java 阿里巴巴 架构 面试 spring security

优柔寡断的人,能成什么大事

Kareza

个人成长 5月日更 反思总结

关于中台,聊聊我认为相对客观的三点认知

架构精进之路

中台 5月日更

想要做网页游戏怎么办 ?PixiJs 篇(三)

空城机

大前端 游戏 pixi 5月日更

Python OOP-4

若尘

面向对象 oop Python编程 5月日更

STM32F103C8/BT6最小系统原理图、PCB

不脱发的程序猿

嵌入式 单片机 STM32F103C8T6 MCU ST

STM32如何计算RTC时钟异步预分频和同步预分频

不脱发的程序猿

嵌入式 RTC stm32 单片机 ST

10个 解放双手的 IDEA 插件,这些代码都不用写(第二弹)

程序员小富

Java 后端 IDEA

阿里大牛亲码 Spring AOP详解笔记全网开源,学透并发只需3天

飞飞JAva

spring aop

打破思维定式(五)

Changing Lin

5月日更

缓存系统稳定性 - 架构师峰会演讲实录

万俊峰Kevin

缓存 微服务 分布式缓存 Go 语言

阿里P7:每个码农都应该知道的MySQL主从复制方法,看这篇就够了

牛哄哄的java大师

Java MySQL 数据库

WebSocket与Java_Java_张龙_InfoQ精选文章