创建 WebSocket 应用程序
在 Kotlin 中使用 Ktor 创建 WebSocket 应用程序
代码示例: tutorial-server-websockets
使用的插件:
本文将指导您如何在 Kotlin 中使用 Ktor 创建 WebSocket 应用程序。它基于
本文将教您如何执行以下操作:
- 创建使用 JSON 序列化的服务。
- 通过 WebSocket 连接发送和接收内容。
- 同时向多个客户端广播内容。
先决条件
您可以独立完成本教程,但我们建议您先完成
我们建议您安装 IntelliJ IDEA,但您也可以使用其他您喜欢的 IDE。
你好 WebSockets
在本教程中,您将通过添加通过 WebSocket 连接与客户端交换 Task 对象的功能,扩展在
创建带有插件的初始项目
导航至 Ktor 项目生成器。
在 Project artifact 字段中,输入 com.example.ktor-websockets-task-app 作为项目工件的名称。

在插件部分搜索并通过点击 Add 按钮添加以下插件:
- Content Negotiation
- kotlinx.serialization
- WebSockets
- Static Content

添加插件后,它们将显示在插件部分的右上角。
然后您将看到将添加到项目中的所有插件列表:

点击 Download 按钮以生成并下载您的 Ktor 项目。
添加起始代码
下载完成后,在 IntelliJ IDEA 中打开您的项目并按照以下步骤操作:
- 导航至 src/main/kotlin 并创建一个名为 model 的新子软件包。
在 model 软件包内创建一个新的 Task.kt 文件。
打开 Task.kt 文件并添加一个
enum来表示优先级,以及一个data class来表示任务:kotlin请注意,
Task类使用了来自kotlinx.serialization库的Serializable注解。这意味着实例可以转换为 JSON 以及从 JSON 转换,从而允许通过网络传输其内容。因为您包含了 WebSockets 插件,生成器已经在 src/main/kotlin 目录下的 Websockets.kt 文件中添加了配置,并在 Routing.kt 文件中添加了
webSocket路由。- 打开 Websockets.kt 文件,并将现有的
.configureWebsockets()函数替换为以下内容:kotlin- 安装 WebSockets 插件并使用标准设置进行配置。
- 设置了
contentConverter属性,使插件能够通过kotlinx.serialization库对发送和接收的对象进行序列化。
打开 Routing.kt 文件,并将现有的
Application.configureRouting()函数替换为下面的实现:kotlin- 路由配置了一个单一端点,其相对 URL 为
/tasks。 - 在收到请求后,任务列表会通过 WebSocket 连接进行序列化发送。
- 一旦所有项目发送完毕,服务器将关闭连接。
出于演示目的,在发送任务之间引入了一秒钟的延迟。这允许您观察任务在客户端中逐步出现的过程。如果没有这个延迟,该示例看起来将与之前文章中开发的
RESTful 服务和了解如何使用 Kotlin 和 Ktor 构建后端服务,其中包含一个生成 JSON 文件的 RESTful API 示例。Web 应用程序完全相同。了解如何使用 Kotlin、Ktor 和 Thymeleaf 模板构建网站。此迭代的最后一步是为此端点创建一个客户端。因为您包含了
Static Content插件,Ktor 项目生成器已在 src/main/resources/static 中添加了一个 index.html 文件。了解如何提供静态内容,如样式表、脚本、图像等。- 路由配置了一个单一端点,其相对 URL 为
打开 index.html 文件并将现有内容替换为以下内容:
html该页面使用了所有现代浏览器中都可用的
WebSocket类型。您在 JavaScript 中创建此对象,并将端点的 URL 传递到构造函数中。随后,您为onopen、onclose和onmessage事件附加事件处理程序。在触发onmessage事件时,您使用 document 对象的方法向表格追加一行。在 IntelliJ IDEA 中,点击运行按钮 (
) 以启动应用程序。
导航至 http://0.0.0.0:8080/static/index.html。您应该会看到一个带有一个按钮的表单和一个空表格:

当您点击表单时,任务会从服务器加载,并以每秒一个的速度出现。因此,表格会被增量填充。您还可以通过打开浏览器 Developer Tools 中的 JavaScript Console 来查看记录的消息。

至此,服务的表现符合预期。WebSocket 连接已打开,项目已发送到客户端,然后连接关闭。底层网络中存在很多复杂性,但 Ktor 默认处理了所有这些复杂性。
了解 WebSockets
在进行下一次迭代之前,回顾一下 WebSockets 的一些基础知识可能会有所帮助。如果您已经熟悉 WebSockets,可以继续 改进服务的设计。
在之前的教程中,您的客户端发送 HTTP 请求并接收 HTTP 响应。这种模式运行良好,使互联网具备了可扩展性和弹性。
然而,它不适用于以下场景:
- 内容是随时间增量生成的。
- 内容根据事件频繁更改。
- 客户端需要在内容产生时与服务器交互。
- 一个客户端发送的数据需要迅速传播给其他客户端。
这些场景的示例包括股票交易、购买电影和音乐会门票、在线拍卖出价以及社交媒体中的聊天功能。WebSockets 的开发就是为了处理这些情况。
WebSocket 连接是建立在 TCP 之上的,可以持续很长时间。该连接提供全双工通信,这意味着客户端可以同时向服务器发送消息并从中接收消息。
WebSocket API 定义了四个事件(open、message、close 和 error)和两个操作(send 和 close)。如何访问此功能可能因不同的语言和库而异。例如,在 Kotlin 中,您可以将传入消息序列作为 Flow 来消费。
改进设计
接下来,您将重构现有代码,为更高级的示例腾出空间。
在 model 软件包中,创建一个新的 TaskRepository.kt 文件。
打开 TaskRepository.kt 并添加
TaskRepository类型:kotlin您可能还记得之前教程中的这段代码。
- 导航至 src/main/kotlin 并打开 Routing.kt 文件。
您现在可以通过利用
TaskRepository来简化Application.configureRouting()中的路由:kotlin
通过 WebSockets 发送消息
为了展示 WebSockets 的强大功能,您将创建一个新端点,其中:
- 当客户端启动时,它会收到所有现有任务。
- 客户端可以创建并发送任务。
- 当一个客户端发送任务时,其他客户端会收到通知。
在 Routing.kt 文件中,将当前的
.configureRouting()方法替换为下面的实现:kotlin通过这段代码,您完成了以下工作:
- 将发送所有现有任务的功能重构为一个辅助方法。
- 在
routing {}块中,您创建了一个线程安全的session对象列表,以跟踪所有客户端。 - 添加了一个相对 URL 为
/tasks2的新端点。当客户端连接到此端点时,相应的session对象将被添加到列表中。然后服务器进入无限循环,等待接收新任务。收到新任务后,服务器将其存储在仓库中,并将副本发送给所有客户端(包括当前客户端)。
为了测试此功能,您将创建一个扩展 index.html 功能的新页面。
在 src/main/resources/static 内创建一个名为 wsClient.html 的新 HTML 文件。
打开 wsClient.html 并添加以下内容:
html这个新页面引入了一个 HTML 表单,用户可以在其中输入新任务的信息。提交表单后,将调用
sendTaskToServer()事件处理程序。这将构建一个包含表单数据的 JavaScript 对象,并使用 WebSocket 对象的.send()方法将其发送到服务器。在 IntelliJ IDEA 中,点击重新运行按钮 (
) 以重启应用程序。
要测试此功能,请并排打开两个浏览器,并按照以下步骤操作。
- 在浏览器 A 中,导航至 http://0.0.0.0:8080/static/wsClient.html。您应该会看到显示的默认任务。
- 在浏览器 A 中添加一个新任务。新任务应该出现在该页面的表格中。
- 在浏览器 B 中,导航至 http://0.0.0.0:8080/static/wsClient.html。您应该会看到默认任务,以及您在浏览器 A 中添加的任何新任务。
- 在任一浏览器中添加任务。您应该会看到新项目同时出现在两个页面上。

添加自动化测试
为了简化您的 QA 流程并使其快速、可复现且无需人工干预,您可以使用 Ktor 内置的
将以下依赖项添加到 build.gradle.kts 中,以便您在 Ktor Client 中配置对
内容协商的支持:ContentNegotiation 插件有两个主要用途:在客户端和服务器之间协商媒体类型,以及以特定格式序列化/反序列化内容。kotlin在 IntelliJ IDEA 中,点击编辑器右侧的 Gradle 通知图标 (
) 以加载 Gradle 更改。
导航至 src/test/kotlin 并打开 ServerTest.kt 文件。
将生成的测试类替换为以下实现:
kotlin通过此设置,您可以:
- 配置您的服务在测试环境中运行,并启用与生产环境相同的功能,包括 JSON 序列化和 WebSockets。
- 在 Ktor Client中配置内容协商和 WebSocket 支持。如果没有这些配置,客户端在通过 WebSocket 连接时将不知道如何将对象 (反) 序列化为 JSON。了解如何创建和配置 Ktor 客户端。
- 声明您期望服务返回的
Tasks列表。 - 使用
client对象的.webSocket函数向/tasks发送请求。 - 将传入的任务作为
Flow消费,并将其增量添加到列表中。 - 收到所有任务后,以常规方式将
expectedTasks与actualTasks进行比较。
后续步骤
做得好!通过将 WebSocket 通信和 Ktor Client 的自动化测试结合起来,您已经显著增强了任务管理器服务。
继续阅读
