WebSocket на Kotlin: создание real-time приложений с нуля

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: почему WebSocket и Kotlin — идеальная пара для real-time

Современные веб-приложения всё чаще требуют мгновенной передачи данных: от чатов и онлайн-игр до биржевых котировок и систем мониторинга. Традиционный HTTP с его запрос-ответной моделью для таких задач неэффективен — он создаёт задержки и избыточный трафик. Здесь на помощь приходит WebSocket — протокол, обеспечивающий постоянное двустороннее соединение между клиентом и сервером.

Kotlin с его лаконичным синтаксисом, полной совместимостью с Java и мощной поддержкой корутин (coroutines) идеально подходит для создания real-time приложений. Фреймворк Ktor от JetBrains предоставляет встроенную поддержку WebSocket, позволяя писать производительный и безопасный код буквально в несколько строк.

В этой статье мы разберём, как создать полноценное real-time приложение на Kotlin с использованием WebSocket. Вы узнаете:

  • Как настроить WebSocket сервер на Ktor
  • Как подключиться к нему из браузера или мобильного приложения
  • Как организовать обмен сообщениями в реальном времени
  • Какие паттерны и подходы использовать для масштабирования

1. Основы WebSocket и его преимущества в Kotlin

Что такое WebSocket?

WebSocket — это протокол, работающий поверх TCP. После установки соединения через HTTP-рукопожатие (upgrade), клиент и сервер могут обмениваться данными в обоих направлениях без повторных запросов. Это кардинально снижает задержки (latency) и уменьшает нагрузку на сеть.

Почему Kotlin?

  • Корутины — позволяют обрабатывать тысячи одновременных WebSocket-соединений без блокировки потоков.
  • Ktor — лёгкий асинхронный фреймворк с нативной поддержкой WebSocket.
  • Типобезопасность — ошибки на этапе компиляции, а не в рантайме.
  • Мультиплатформенность — один и тот же код может работать на JVM, JS и Native.

2. Создание WebSocket сервера на Ktor

Шаг 1: Настройка проекта

Добавьте зависимости в build.gradle.kts:

dependencies {
    implementation("io.ktor:ktor-server-core:2.3.0")
    implementation("io.ktor:ktor-server-netty:2.3.0")
    implementation("io.ktor:ktor-server-websockets:2.3.0")
    implementation("ch.qos.logback:logback-classic:1.4.11")
}

Шаг 2: Базовый сервер с WebSocket

import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.websocket.*
import io.ktor.websocket.*
import kotlinx.coroutines.delay

fun main() {
    embeddedServer(Netty, port = 8080) {
        install(WebSockets)
        webSocket("/chat") {
            send(Frame.Text("Добро пожаловать в чат!"))
            for (frame in incoming) {
                if (frame is Frame.Text) {
                    val text = frame.readText()
                    println("Получено: $text")
                    send(Frame.Text("Эхо: $text"))
                }
            }
        }
    }.start(wait = true)
}

Этот код создаёт простой эхо-сервер. При подключении клиент получает приветствие, а каждое его сообщение возвращается обратно с префиксом "Эхо:".

Шаг 3: Обработка нескольких клиентов и рассылка

Для real-time чата нужно хранить список активных сессий:

import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.websocket.*
import io.ktor.websocket.*
import kotlinx.coroutines.channels.ConcurrentHashMap

val clients = ConcurrentHashMap<String, WebSocketSession>()

fun main() {
    embeddedServer(Netty, port = 8080) {
        install(WebSockets)
        webSocket("/chat") {
            val userName = "User_${System.currentTimeMillis()}"
            clients[userName] = this
            try {
                send(Frame.Text("Вы вошли как $userName"))
                for (frame in incoming) {
                    if (frame is Frame.Text) {
                        val message = frame.readText()
                        val broadcast = "$userName: $message"
                        clients.values.forEach { it.send(Frame.Text(broadcast)) }
                    }
                }
            } finally {
                clients.remove(userName)
                clients.values.forEach { it.send(Frame.Text("$userName покинул чат")) }
            }
        }
    }.start(wait = true)
}

Теперь каждое сообщение уходит всем подключённым пользователям. При отключении клиента остальные узнают об этом.

3. WebSocket клиент на Kotlin (Ktor Client)

Для тестирования или создания мобильного клиента используйте Ktor Client:

Зависимости для клиента

dependencies {
    implementation("io.ktor:ktor-client-core:2.3.0")
    implementation("io.ktor:ktor-client-cio:2.3.0")
    implementation("io.ktor:ktor-client-websockets:2.3.0")
}

Код клиента

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 {
            while (true) {
                val input = readlnOrNull() ?: break
                send(Frame.Text(input))
            }
        }
        // Поток для получения сообщений
        for (frame in incoming) {
            if (frame is Frame.Text) {
                println(frame.readText())
            }
        }
    }
    client.close()
}

Этот консольный клиент подключается к серверу и позволяет общаться в реальном времени.

4. Продвинутые техники: масштабирование и безопасность

Масштабирование с помощью Redis Pub/Sub

Если ваше приложение должно работать на нескольких серверах, используйте Redis для обмена сообщениями между инстансами:

// Публикация сообщения в Redis
fun publishToRedis(channel: String, message: String) {
    jedis.publish(channel, message)
}

// Подписка на канал
fun subscribeToRedis(channel: String) {
    jedis.subscribe(object : JedisPubSub                

Блоги

Рекомендаций по книгам