WebSockets в Kotlin: от теории к real-time магии
Представьте, что вы открыли мессенджер, отправили сообщение — и оно мгновенно появилось у собеседника. Без перезагрузки страницы, без постоянных запросов к серверу. Это не магия, это WebSockets в Kotlin. Если вы когда-нибудь хотели создать свой чат, систему уведомлений в реальном времени или многопользовательскую игру — вы пришли по адресу.
В этой статье мы разберем, как работают WebSockets, напишем полноценное real-time приложение на Kotlin с использованием Ktor, и я покажу вам код, который можно сразу запустить. Поехали!
Что такое WebSockets и почему они нужны в Kotlin?
WebSocket — это протокол двунаправленной связи между клиентом и сервером поверх одного TCP-соединения. В отличие от классического HTTP, где клиент всегда инициирует запрос, WebSocket позволяет серверу самому отправлять данные клиенту в любой момент.
Чем WebSocket лучше HTTP?
- Мгновенность: данные доставляются за миллисекунды
- Экономия трафика: нет лишних заголовков, как в HTTP
- Двунаправленность: сервер может инициировать отправку
- Постоянное соединение: не нужно переустанавливать каждый раз
Kotlin с его корутинами и фреймворком Ktor идеально подходит для работы с WebSockets. Асинхронность, легковесность и выразительный синтаксис — вот что делает Kotlin WebSocket разработку такой приятной.
Когда использовать WebSockets?
Вот несколько сценариев, где real-time приложения на Kotlin с WebSockets будут незаменимы:
- Чаты и мессенджеры — передача сообщений в реальном времени
- Системы уведомлений — алерты, всплывающие окна, обновления статусов
- Онлайн-игры — синхронизация игроков
- Биржевые котировки — обновление цен без перезагрузки
- Совместное редактирование — Google Docs-like функционал
Настройка проекта: Ktor + WebSockets
Для создания WebSocket сервера на Kotlin мы будем использовать Ktor — современный асинхронный фреймворк от JetBrains. Давайте настроим проект.
Шаг 1: Добавляем зависимости
Если вы используете Gradle (а я уверен, что используете), добавьте в build.gradle.kts:
dependencies {
implementation("io.ktor:ktor-server-core:2.3.7")
implementation("io.ktor:ktor-server-netty:2.3.7")
implementation("io.ktor:ktor-server-websockets:2.3.7")
implementation("ch.qos.logback:logback-classic:1.4.14")
}
Шаг 2: Создаем WebSocket сервер
Теперь напишем сам сервер. Он будет принимать подключения и транслировать сообщения всем подключенным клиентам — базовая логика чата.
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.websocket.*
import io.ktor.websocket.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
fun main() {
val clients = mutableSetOf<WebSocketSession>()
embeddedServer(Netty, port = 8080) {
install(WebSockets) {
pingPeriod = 15_000 // 15 секунд
timeout = 15_000
maxFrameSize = Long.MAX_VALUE
}
routing {
webSocket("/chat") {
// Добавляем клиента
clients.add(this)
println("Новое подключение: $this")
try {
for (frame in incoming) {
if (frame is Frame.Text) {
val message = frame.readText()
println("Получено: $message")
// Отправляем всем клиентам
clients.forEach { client ->
client.send(Frame.Text("$message"))
}
}
}
} catch (e: Exception) {
println("Ошибка: ${e.message}")
} finally {
// Удаляем клиента при отключении
clients.remove(this)
println("Подключение закрыто: $this")
}
}
}
}.start(wait = true)
}
Этот код создает простой WebSocket чат на Kotlin. Все сообщения, отправленные одним клиентом, видят все остальные. Обратите внимание на использование корутин — они делают код неблокирующим и эффективным.
Написание WebSocket клиента на Kotlin
Сервер готов, но без клиента он бесполезен. Давайте напишем WebSocket клиент на Kotlin с помощью Ktor Client.
Шаг 1: Добавляем клиентскую зависимость
implementation("io.ktor:ktor-client-websockets:2.3.7")
implementation("io.ktor:ktor-client-cio:2.3.7")
Шаг 2: Код клиента
import io.ktor.client.*
import io.ktor.client.plugins.websocket.*
import io.ktor.websocket.*
import kotlinx.coroutines.*
suspend fun main() {
val client = HttpClient {
install(WebSockets)
}
client.webSocket("ws://localhost:8080/chat") {
// Запускаем корутину для чтения сообщений
launch {
for (frame in incoming) {
if (frame is Frame.Text) {
println("Сервер: ${frame.readText()}")
}
}
}
// Отправляем сообщения
send(Frame.Text("Привет, мир!"))
delay(1000)
send(Frame.Text("Как дела?"))
delay(1000)
send(Frame.Text("Пока!"))
// Ждем немного перед закрытием
delay(2000)
}
client.close()
}
Этот клиент подключается к нашему серверу, отправляет несколько сообщений и выводит ответы. Просто, правда?
Продвинутые техники: аутентификация и комнаты
В реальных проектах простой чат — это только начало. Давайте посмотрим, как добавить аутентификацию и комнаты (channels) для групповых чатов.
Аутентификация через WebSocket
При подключении к WebSocket можно передать токен в query-параметре:
webSocket("/chat?token={token}") {
val token = call.parameters["token"]
if (token == null || !isValidToken(token)) {
close(CloseReason(CloseReason.Codes.VIOLATED_POLICY, "Неверный токен"))
return@webSocket
}
// ... остальная логика
}
Создание комнат
Чтобы разделить пользователей на комнаты, можно использовать Map:
val rooms = mutableMapOf<String, MutableSet<WebSocketSession>>()
webSocket("/chat/{room}") {
val roomName = call.parameters["room"] ?: "general"
val room = rooms.getOrPut(roomName) { mutableSetOf()