使用 Ktor 在 Kotlin 中创建 WebSocket 应用程序
代码示例: tutorial-server-websockets
已使用的插件:
本文将引导您完成使用 Ktor 在 Kotlin 中创建 WebSocket 应用程序的过程。它建立在
本文将教您如何执行以下操作:
- 创建使用 JSON 序列化的服务。
- 通过 WebSocket 连接发送和接收内容。
- 同时向多个客户端广播内容。
先决条件
您可以独立完成本教程,但是,我们建议您完成
我们建议您安装 IntelliJ IDEA,但您也可以选择其他 IDE。
Hello WebSockets
在本教程中,您将基于
Task
对象的能力。为此,您需要添加 使用插件创建初始项目
导航到 Ktor 项目生成器 。
在 项目 artifact 字段中,输入 com.example.ktor-websockets-task-app 作为您的项目 artifact 名称。
在插件部分中搜索并点击 添加 按钮添加以下插件:
- Routing
- Content Negotiation
- Kotlinx.serialization
- WebSockets
- Static Content
添加插件后,点击插件部分右上角的 5 plugins 按钮,以显示已添加的插件。
您将看到一个列表,其中包含将添加到您项目的所有插件:
点击 下载 按钮以生成并下载您的 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/com/example/plugins 中已生成了一个 Sockets.kt 文件。
打开 Sockets.kt 文件,并用以下实现替换现有的
Application.configureSockets()
函数:kotlin此代码执行以下步骤:
- 安装 WebSockets 插件并使用标准设置进行配置。
- 设置
contentConverter
属性,使插件能够通过 kotlinx.serialization 库序列化发送和接收的对象。 - 路由配置了一个单端点,其相对 URL 为
/tasks
。 - 收到请求后,任务列表通过 WebSocket 连接进行序列化传输。
- 所有项发送完毕后,服务器关闭连接。
为了演示目的,在发送任务之间引入了一秒的延迟。这 使我们能够观察任务在客户端中逐渐出现。如果没有此延迟, 该示例将与先前文章中开发的
RESTful 服务和了解如何使用 Kotlin 和 Ktor 构建后端服务,其中包含一个生成 JSON 文件的 RESTful API 示例。Web 应用程序看起来相同。了解如何使用 Ktor 和 Thymeleaf 模板在 Kotlin 中构建网站。此迭代的最后一步是为此端点创建一个客户端。由于您包含了
静态内容插件,因此在 src/main/resources/static 中已生成了一个 index.html 文件。了解如何提供静态内容,例如样式表、脚本、图像等。打开 index.html 文件,并用以下内容替换现有内容:
html此页面使用所有现代浏览器中可用的 WebSocket 类型。我们在 JavaScript 中创建此对象,将端点的 URL 传递给构造函数。随后,我们为
onopen
、onclose
和onmessage
事件附加事件处理程序。当触发onmessage
事件时,我们使用 document 对象的方法将一行附加到表格中。在 IntelliJ IDEA 中,点击运行按钮 (
) 以启动应用程序。
导航到 http://0.0.0.0:8080/static/index.html。 您应该会看到一个带有按钮的表单和一个空表格:
当您点击表单时,任务将从服务器加载,每秒出现一个。因此,表格将逐渐填充。您还可以通过在浏览器的 开发者工具 中打开 JavaScript 控制台 来查看已记录的消息。
通过此操作,您可以看到服务按预期运行。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您可能还记得之前教程中的这段代码。
- 导航到 plugins 包并打开 Sockets.kt 文件。
您现在可以通过利用
TaskRepository
来简化Application.configureSockets()
中的路由:kotlin
通过 WebSockets 发送消息
为了说明 WebSockets 的强大功能,您将创建一个新端点,其中:
- 当客户端启动时,它会收到所有现有任务。
- 客户端可以创建和发送任务。
- 当一个客户端发送任务时,其他客户端会收到通知。
在 Sockets.kt 文件中,用以下实现替换当前的
configureSockets()
方法:kotlin通过此代码,您完成了以下操作:
- 将发送所有现有任务的功能重构为一个辅助方法。
- 在
routing
部分,您创建了一个线程安全的session
对象 list,用于跟踪所有客户端。 - 添加了一个相对 URL 为
/task2
的新端点。当客户端连接到 此端点时,相应的session
对象会添加到 list 中。服务器 然后进入无限循环,等待接收新任务。收到新任务后,服务器将其存储在 repository 中,并将副本发送给所有客户端,包括当前客户端。
为了测试此功能,您将创建一个新页面,扩展 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 中配置
Content Negotiation的支持:ContentNegotiation 插件有两个主要目的:在客户端和服务器之间协商媒体类型,以及以特定格式序列化/反序列化内容。kotlin在 IntelliJ IDEA 中,点击通知 Gradle 图标 (
) 在编辑器右侧,以加载 Gradle 更改。
导航到 src/test/kotlin/com/example 并打开 ApplicationTest.kt 文件。
用以下实现替换生成的测试类:
kotlin通过此设置,您:
- 将您的服务配置为在测试环境中运行,并启用与生产环境相同的功能,包括路由、JSON 序列化和 WebSockets。
- 在 Ktor Client中配置 Content Negotiation 和 WebSocket 支持。否则,客户端在使用 WebSocket 连接时将不知道如何(反)序列化 JSON 对象。了解如何创建和配置 Ktor 客户端。
- 声明您期望服务返回的
Tasks
list。 - 使用客户端对象的
websocket
方法向/tasks
发送请求。 - 将传入任务作为
flow
消费,并将其增量添加到 list 中。 - 收到所有任务后,以常规方式比较
expectedTasks
和actualTasks
。
后续步骤
干得好!通过集成 WebSocket 通信和使用 Ktor Client 进行自动化测试,您显著增强了任务管理器服务。
继续