使用 Kotlin 與 Ktor 建立 WebSocket 應用程式
程式碼範例: tutorial-server-websockets
使用的插件:
本文將引導您完成使用 Kotlin 和 Ktor 建立 WebSocket 應用程式的過程。它建立在
本文將教您如何執行以下操作:
- 建立使用 JSON 序列化的服務。
- 透過 WebSocket 連線傳送和接收內容。
- 同時向多個用戶端廣播內容。
先決條件
您可以獨立完成本教學,但我們建議您完成
我們建議您安裝 IntelliJ IDEA,但您也可以使用您選擇的其他 IDE。
Hello WebSockets
在本教學中,您將在
Task
物件的能力。為此,您需要新增 使用插件建立初始專案
導覽至 Ktor 專案產生器。
在 Project artifact 欄位中,輸入 com.example.ktor-websockets-task-app 作為專案成品的名稱。
在插件區塊中,搜尋並點擊 Add 按鈕新增以下插件:
- Routing
- Content Negotiation
- Kotlinx.serialization
- WebSockets
- Static Content
新增插件後,點擊插件區塊右上角的 5 plugins 按鈕,顯示已新增的插件。
您將看到所有將新增到專案中的插件列表:
點擊 Download 按鈕來產生並下載您的 Ktor 專案。
新增啟動程式碼
下載完成後,在 IntelliJ IDEA 中開啟您的專案並按照以下步驟操作:
- 導覽至 src/main/kotlin 並建立一個名為 model 的新子套件。
在 model 套件內建立一個新的 Task.kt 檔案。
開啟 Task.kt 檔案並新增一個
enum
來表示優先級,以及一個data class
來表示任務:kotlin請注意,
Task
類別使用kotlinx.serialization
函式庫中的Serializable
型別進行標註。這表示實例可以轉換為 JSON 格式,也可以從 JSON 轉換回來,從而允許其內容透過網路傳輸。因為您包含了 WebSockets 插件,一個 Sockets.kt 檔案已在 src/main/kotlin/com/example/plugins 內產生。
開啟 Sockets.kt 檔案並將現有的
Application.configureSockets()
函數替換為以下實作:kotlin在此程式碼中執行了以下步驟:
- 安裝並配置了 WebSockets 插件,並使用標準設定。
- 設定了
contentConverter
屬性,使插件能夠 透過 kotlinx.serialization 函式庫序列化傳送和接收的物件。 - 配置了路由,只有一個端點,其相對 URL 為
/tasks
。 - 收到請求後,將任務列表透過 WebSocket 連線序列化傳送。
- 所有項目傳送完畢後,伺服器關閉連線。
為了演示目的,在傳送任務之間引入了一秒的延遲。這 讓我們能夠觀察到任務在用戶端中遞增地出現。如果沒有此延遲, 該範例看起來會與在先前文章中開發的
RESTful 服務和了解如何使用 Kotlin 和 Ktor 建置後端服務,其中包含產生 JSON 檔案的 RESTful API 範例。網路應用程式相同。了解如何使用 Kotlin 和 Ktor 以及 Thymeleaf 模板建置網站。此迭代的最後一步是為此端點建立一個用戶端。因為您包含了
Static Content插件,一個 index.html 檔案已在 src/main/resources/static 內產生。了解如何提供靜態內容,例如樣式表、腳本、圖片等。開啟 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
物件列表,以追蹤所有用戶端。 - 新增了一個相對 URL 為
/task2
的新端點。當用戶端連接到 此端點時,對應的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 中新增的任何新任務。
- 在任一瀏覽器中新增一個任務。您應該會看到新項目同時出現在兩個頁面上。
新增自動化測試
為了簡化您的品保流程,使其快速、可重現且無需手動操作,您可以使用 Ktor 內建的
將以下依賴項新增到 build.gradle.kts 中,以允許您在 Ktor 用戶端中配置
內容協商支援:ContentNegotiation 插件主要有兩個目的:協商用戶端和伺服器之間的媒體類型,以及以特定格式序列化/反序列化內容。kotlin在 IntelliJ IDEA 中,點擊編輯器右側的 Gradle 通知圖示 (
) 以載入 Gradle 變更。
導覽至 src/test/kotlin/com/example 並開啟 ApplicationTest.kt 檔案。
將產生測試類別替換為以下實作:
kotlin透過此設定,您:
- 配置您的服務在測試環境中執行,並啟用與生產環境中相同的功能,包括 Routing、JSON 序列化和 WebSockets。
- 在 Ktor 用戶端中配置 Content Negotiation 和 WebSocket 支援。如果沒有這些,用戶端將不知道在使用 WebSocket 連線時如何 (反)序列化 JSON 物件。了解如何建立和配置 Ktor 用戶端。
- 宣告您期望服務傳回的
Tasks
列表。 - 使用用戶端物件的
websocket
方法向/tasks
傳送請求。 - 將傳入的任務作為
flow
消耗,並遞增地將它們新增到列表中。 - 一旦收到所有任務,以通常的方式比較
expectedTasks
與actualTasks
。
後續步驟
幹得好!透過整合 WebSocket 通訊和使用 Ktor 用戶端的自動化測試,您已大幅增強您的任務管理器服務。
繼續閱讀