Long Polling vs WebSocket in Kotlin: What to Choose for Real-Time
\n\nImagine: you are writing a chat in Kotlin, a stock exchange terminal, or a server monitoring system. The client needs to see changes instantly. The traditional HTTP "request-response" approach won't work here — data must arrive on its own, without constant pings from the user.
\n\nIn the world of Kotlin, there are two popular approaches: Long Polling and WebSocket. Both allow you to organize real-time interaction, but they do it in fundamentally different ways. Which one to choose? Let's figure it out with live examples, without fluff or academic lectures.
\n\nWhat is Long Polling (and why it hasn't died)
\n\nLong Polling is a technique where the client sends an HTTP request, and the server does not respond immediately but keeps the connection open until new data appears. As soon as data appears, the server sends a response, the client processes it, and immediately opens a new request. The cycle repeats.
\n\nIt looks like regular HTTP, but with a long timeout. No additional protocols, no handshakes — everything works over standard HTTP/1.1 or HTTP/2.
\n\nLong Polling Example in Kotlin (Ktor Server + OkHttp Client)
\n\nServer-side on Ktor (the server will wait until a message appears):
\n\nimport io.ktor.server.engine.*\nimport io.ktor.server.netty.*\nimport io.ktor.server.routing.*\nimport io.ktor.server.response.*\nimport io.ktor.http.*\nimport kotlinx.coroutines.*\n\nfun main() {\n embeddedServer(Netty, port = 8080) {\n routing {\n get("/long-poll") {\n // Simulating waiting for a new message\n val message = withContext(Dispatchers.Default) {\n delay(5000) // wait 5 seconds (or for a new event)\n "New message: ${System.currentTimeMillis()}"\n }\n call.respondText(message, ContentType.Text.Plain)\n }\n }\n }.start(wait = true)\n}\n\n\nClient on OkHttp:
\n\nimport okhttp3.*\n\nval client = OkHttpClient.Builder()\n .readTimeout(30, TimeUnit.SECONDS) // important!\n .build()\n\nfun startLongPolling() {\n val request = Request.Builder()\n .url("http://localhost:8080/long-poll")\n .build()\n\n client.newCall(request).enqueue(object : Callback {\n override fun onFailure(call: Call, e: IOException) {\n e.printStackTrace()\n // Be sure to restart polling on error\n startLongPolling()\n }\n\n override fun onResponse(call: Call, response: Response) {\n response.use {\n if (response.isSuccessful) {\n println("Received: ${response.body?.string()}")\n // Immediately start the next request\n startLongPolling()\n }\n }\n }\n })\n}\n\n\nPros of Long Polling:
\n- \n
- Works everywhere, even behind corporate proxies and in old browsers \n
- Easy debugging — you see regular HTTP requests in the logs \n
- Easily integrates into existing infrastructure (load balancers, caches, CDNs) \n
Cons:
\n- \n
- High latency — up to RTT (Round Trip Time) may pass between sending a request and receiving a response \n
- The server holds many open connections (each request is a separate thread/coroutine) \n
- Message loss is possible during reconnection (if idempotency is not implemented) \n
WebSocket: Full Duplex
\n\nWebSocket is a separate protocol (ws://) that establishes a permanent bidirectional connection after an HTTP handshake. The server and client can send messages at any time, without header overhead.
\n\n