Skip to content

使用 Kotlin 與 Ktor 建立 WebSocket 應用程式

程式碼範例: tutorial-server-websockets

使用的插件:

Routing
路由是伺服器應用程式中處理傳入請求的核心插件。
Static Content
了解如何提供靜態內容,例如樣式表、腳本、圖片等。
Content Negotiation
ContentNegotiation 插件主要有兩個目的:協商用戶端和伺服器之間的媒體類型,以及以特定格式序列化/反序列化內容。
WebSockets in Ktor Server
WebSockets 插件允許您在伺服器和用戶端之間建立多向通訊會話。
kotlinx.serialization

本文將引導您完成使用 Kotlin 和 Ktor 建立 WebSocket 應用程式的過程。它建立在

建立 RESTful API
了解如何使用 Kotlin 和 Ktor 建置後端服務,其中包含產生 JSON 檔案的 RESTful API 範例。
教學中涵蓋的內容基礎上。

本文將教您如何執行以下操作:

  • 建立使用 JSON 序列化的服務。
  • 透過 WebSocket 連線傳送和接收內容。
  • 同時向多個用戶端廣播內容。

先決條件

您可以獨立完成本教學,但我們建議您完成

建立 RESTful API
了解如何使用 Kotlin 和 Ktor 建置後端服務,其中包含產生 JSON 檔案的 RESTful API 範例。
教學,以熟悉
Content Negotiation
ContentNegotiation 插件主要有兩個目的:協商用戶端和伺服器之間的媒體類型,以及以特定格式序列化/反序列化內容。
和 REST。

我們建議您安裝 IntelliJ IDEA,但您也可以使用您選擇的其他 IDE。

Hello WebSockets

在本教學中,您將在

建立 RESTful API
了解如何使用 Kotlin 和 Ktor 建置後端服務,其中包含產生 JSON 檔案的 RESTful API 範例。
教學中開發的任務管理器服務的基礎上,新增透過 WebSocket 連線與用戶端交換 Task 物件的能力。為此,您需要新增
WebSockets 插件
Websockets 插件允許您在伺服器和用戶端之間建立多向通訊會話。
。雖然您可以手動將其新增到現有專案中,但為了本教學的目的,我們將從頭開始建立一個新專案。

使用插件建立初始專案

  1. 導覽至 Ktor 專案產生器

  2. Project artifact 欄位中,輸入 com.example.ktor-websockets-task-app 作為專案成品的名稱。 在 Ktor 專案產生器中命名專案成品

  3. 在插件區塊中,搜尋並點擊 Add 按鈕新增以下插件:

    1. Routing
    2. Content Negotiation
    3. Kotlinx.serialization
    4. WebSockets
    5. Static Content

    在 Ktor 專案產生器中新增插件

  4. 新增插件後,點擊插件區塊右上角的 5 plugins 按鈕,顯示已新增的插件。

    您將看到所有將新增到專案中的插件列表: Ktor 專案產生器中的插件列表

  5. 點擊 Download 按鈕來產生並下載您的 Ktor 專案。

新增啟動程式碼

下載完成後,在 IntelliJ IDEA 中開啟您的專案並按照以下步驟操作:

  1. 導覽至 src/main/kotlin 並建立一個名為 model 的新子套件。
  2. model 套件內建立一個新的 Task.kt 檔案。

  3. 開啟 Task.kt 檔案並新增一個 enum 來表示優先級,以及一個 data class 來表示任務:

    kotlin

    請注意,Task 類別使用 kotlinx.serialization 函式庫中的 Serializable 型別進行標註。這表示實例可以轉換為 JSON 格式,也可以從 JSON 轉換回來,從而允許其內容透過網路傳輸。

    因為您包含了 WebSockets 插件,一個 Sockets.kt 檔案已在 src/main/kotlin/com/example/plugins 內產生。

  4. 開啟 Sockets.kt 檔案並將現有的 Application.configureSockets() 函數替換為以下實作:

    kotlin

    在此程式碼中執行了以下步驟:

    1. 安裝並配置了 WebSockets 插件,並使用標準設定。
    2. 設定了 contentConverter 屬性,使插件能夠 透過 kotlinx.serialization 函式庫序列化傳送和接收的物件。
    3. 配置了路由,只有一個端點,其相對 URL 為 /tasks
    4. 收到請求後,將任務列表透過 WebSocket 連線序列化傳送。
    5. 所有項目傳送完畢後,伺服器關閉連線。

    為了演示目的,在傳送任務之間引入了一秒的延遲。這 讓我們能夠觀察到任務在用戶端中遞增地出現。如果沒有此延遲, 該範例看起來會與在先前文章中開發的

    RESTful 服務
    了解如何使用 Kotlin 和 Ktor 建置後端服務,其中包含產生 JSON 檔案的 RESTful API 範例。
    網路應用程式
    了解如何使用 Kotlin 和 Ktor 以及 Thymeleaf 模板建置網站。
    相同。

    此迭代的最後一步是為此端點建立一個用戶端。因為您包含了

    Static Content
    了解如何提供靜態內容,例如樣式表、腳本、圖片等。
    插件,一個 index.html 檔案已在 src/main/resources/static 內產生。

  5. 開啟 index.html 檔案並將現有內容替換為以下內容:

    html

    此頁面使用 WebSocket 型別, 所有現代瀏覽器都支援。我們在 JavaScript 中建立此物件,將 端點的 URL 傳入建構式。隨後,我們為 onopenoncloseonmessage 事件附加事件處理器。觸發 onmessage 事件後,我們使用 document 物件的方法將一行附加到表格中。

  6. 在 IntelliJ IDEA 中,點擊執行按鈕 (IntelliJ IDEA 執行圖示) 來啟動應用程式。

  7. 導覽至 http://0.0.0.0:8080/static/index.html。 您應該會看到一個帶有按鈕和空表格的表單:

    顯示帶有一個按鈕的 HTML 表單的網頁

    當您點擊表單時,任務應以每秒一個的速度從伺服器載入, 並遞增填充表格。您還可以透過在瀏覽器的 開發者工具中開啟 JavaScript 控制台 來查看記錄的訊息。

    顯示在按鈕點擊時列表項目的網頁

    至此,您可以看到服務正如預期般運作。WebSocket 連線已開啟,項目已傳送至用戶端,然後連線關閉。底層網路存在許多複雜性,但 Ktor 預設處理所有這些。

理解 WebSockets

在進入下一個迭代之前,回顧一些 WebSockets 的基礎知識可能會有所幫助。 如果您已經熟悉 WebSockets,您可以繼續改進服務的設計

在先前的教學中,您的用戶端傳送 HTTP 請求並接收 HTTP 回應。這運作良好,並使網際網路具有可擴展性和彈性。

然而,它不適用於以下情境:

  • 內容隨時間遞增產生。
  • 內容根據事件頻繁變化。
  • 用戶端需要在內容產生時與伺服器互動。
  • 一個用戶端傳送的資料需要快速傳播給其他用戶端。

這些情境的範例包括股票交易、購買電影和音樂會門票、在線上拍賣中競標以及社交媒體中的聊天功能。WebSockets 的開發就是為了處理這些情況。

WebSocket 連線透過 TCP 建立,並且可以持續較長時間。此連線提供「全雙工通訊」,意味著用戶端可以同時向伺服器傳送訊息並從伺服器接收訊息。

WebSocket API 定義了四個事件(open、message、close 和 error)和兩種動作(send 和 close)。 此功能的存取方式可能因不同的語言和函式庫而異。 例如,在 Kotlin 中,您可以將傳入訊息序列作為 Flow 消耗。

改進設計

接下來,您將重構現有程式碼,為更進階的範例騰出空間。

  1. model 套件中,建立一個新的 TaskRepository.kt 檔案。

  2. 開啟 TaskRepository.kt 並新增一個 TaskRepository 型別:

    kotlin

    您可能還記得此程式碼來自先前的教學。

  3. 導覽至 plugins 套件並開啟 Sockets.kt 檔案。
  4. 您現在可以透過利用 TaskRepository 來簡化 Application.configureSockets() 中的路由:

    kotlin

透過 WebSockets 傳送訊息

為了說明 WebSockets 的強大功能,您將建立一個新端點,其中:

  • 當用戶端啟動時,它會接收所有現有任務。
  • 用戶端可以建立和傳送任務。
  • 當一個用戶端傳送任務時,其他用戶端會收到通知。
  1. Sockets.kt 檔案中,將目前的 configureSockets() 方法替換為以下實作:

    kotlin

    透過此程式碼,您已執行以下操作:

    • 將傳送所有現有任務的功能重構為輔助方法。
    • routing 區塊中,您建立了一個執行緒安全的 session 物件列表,以追蹤所有用戶端。
    • 新增了一個相對 URL 為 /task2 的新端點。當用戶端連接到 此端點時,對應的 session 物件會被新增到列表中。伺服器 隨後進入無限迴圈,等待接收新任務。收到新任務後,伺服器 將其儲存到儲存庫中,並將副本傳送給所有用戶端,包括當前的用戶端。

    為了測試此功能,您將建立一個擴展 index.html 功能的頁面。

  2. src/main/resources/static 內建立一個名為 wsClient.html 的新 HTML 檔案。

  3. 開啟 wsClient.html 並新增以下內容:

    html

    這個新頁面引入了一個 HTML 表單,使用者可以在其中輸入新任務的資訊。 提交表單後,會呼叫 sendTaskToServer 事件處理器。這會用 表單資料建置一個 JavaScript 物件,並使用 WebSocket 物件的 send 方法將其傳送給伺服器。

  4. 在 IntelliJ IDEA 中,點擊重新執行按鈕(IntelliJ IDEA 重新執行圖示)來重新啟動應用程式。

  5. 為了測試此功能,並排開啟兩個瀏覽器並按照以下步驟操作。

    1. 在瀏覽器 A 中,導覽至 http://0.0.0.0:8080/static/wsClient.html。 您應該會看到預設任務顯示。
    2. 在瀏覽器 A 中新增一個任務。新任務應該會出現在該頁面的表格中。
    3. 在瀏覽器 B 中,導覽至 http://0.0.0.0:8080/static/wsClient.html。 您應該會看到預設任務,以及您在瀏覽器 A 中新增的任何新任務。
    4. 在任一瀏覽器中新增一個任務。您應該會看到新項目同時出現在兩個頁面上。
    兩個網頁並排顯示透過 HTML 表單建立新任務的過程

新增自動化測試

為了簡化您的品保流程,使其快速、可重現且無需手動操作,您可以使用 Ktor 內建的

自動化測試支援
了解如何使用特殊的測試引擎測試您的伺服器應用程式。
。請按照以下步驟操作:

  1. 將以下依賴項新增到 build.gradle.kts 中,以允許您在 Ktor 用戶端中配置

    內容協商
    ContentNegotiation 插件主要有兩個目的:協商用戶端和伺服器之間的媒體類型,以及以特定格式序列化/反序列化內容。
    支援:

    kotlin
  2. 在 IntelliJ IDEA 中,點擊編輯器右側的 Gradle 通知圖示 (IntelliJ IDEA Gradle 圖示) 以載入 Gradle 變更。

  3. 導覽至 src/test/kotlin/com/example 並開啟 ApplicationTest.kt 檔案。

  4. 將產生測試類別替換為以下實作:

    kotlin

    透過此設定,您:

    • 配置您的服務在測試環境中執行,並啟用與生產環境中相同的功能,包括 Routing、JSON 序列化和 WebSockets。
    • Ktor 用戶端
      了解如何建立和配置 Ktor 用戶端。
      中配置 Content Negotiation 和 WebSocket 支援。如果沒有這些,用戶端將不知道在使用 WebSocket 連線時如何 (反)序列化 JSON 物件。
    • 宣告您期望服務傳回的 Tasks 列表。
    • 使用用戶端物件的 websocket 方法向 /tasks 傳送請求。
    • 將傳入的任務作為 flow 消耗,並遞增地將它們新增到列表中。
    • 一旦收到所有任務,以通常的方式比較 expectedTasksactualTasks

後續步驟

幹得好!透過整合 WebSocket 通訊和使用 Ktor 用戶端的自動化測試,您已大幅增強您的任務管理器服務。

繼續閱讀

下一個教學
了解如何使用 Exposed SQL 函式庫將 Ktor 服務連接到資料庫儲存庫。
以探索您的服務如何使用 Exposed 函式庫與關聯式資料庫無縫互動。