WebSockets
WebSocket is a protocol which provides a full-duplex communication session between the user's browser and a server over a single TCP connection. It is particularly useful for creating applications that require real-time data transfer from and to the server.
Ktor supports the WebSocket protocol both on the server-, and the client-side.
Ktor allows you to:
Configure basic WebSocket settings, such as frame size, a ping period, and so on.
Handle a WebSocket session for exchanging messages between the server and client.
Add WebSocket extensions. For example, you can use the Deflate extension or implement a custom extension.
Add dependencies
To use WebSockets
, you need to include the ktor-server-websockets
artifact in the build script:
Install WebSockets
To install the WebSockets
plugin to the application, pass it to the install
function in the specified module. The code snippets below show how to install WebSockets
...
... inside the
embeddedServer
function call.... inside the explicitly defined
module
, which is an extension function of theApplication
class.
Configure WebSockets
Optionally, you can configure the plugin inside the install
block by passing WebSocketOptions:
Use the
pingPeriod
property to specify the duration between pings.Use the
timeout
property to set a timeout after which the connection to be closed.Use the
maxFrameSize
property to set a maximum Frame that could be received or sent.Use the
masking
property to specify whether masking is enabled.Use the
contentConverter
property to set a converter for serialization/deserialization.
Handle WebSockets sessions
API overview
Once you have installed and configured the WebSockets
plugin, you can define an endpoint to handle a Websocket session. To define a WebSocket endpoint on a server, call the webSocket
function inside the routing block:
In this example, the server accepts WebSocket requests to ws://localhost:8080/echo
when a default configuration is used.
Inside the webSocket
block, you define the handler for the WebSocket session, which is represented by the DefaultWebSocketServerSession class. The following functions and properties are available within the block:
Use the
send
function to send text content to the client.Use the
incoming
andoutgoing
properties to access the channels for receiving and sending WebSocket frames. A frame is represented by theFrame
class.Use the
close
function to send a close frame with the specified reason.
When handling a session, you can check a frame type, for example:
Frame.Text
is a text frame. For this frame type, you can read its content usingFrame.Text.readText()
.Frame.Binary
is a binary frame. For this type, you can read its content usingFrame.Binary.readBytes()
.
Below, we'll take a look at the examples of using this API.
Example: Handle a single session
The example below shows how to create the echo
WebSocket endpoint to handle a session with one client:
For the full example, see server-websockets.
Example: Handle multiple sessions
To efficiently manage multiple WebSocket sessions and handle broadcasting, you can utilize Kotlin's SharedFlow
. This approach provides a scalable and concurrency-friendly method for managing WebSocket communications. Here's how to implement this pattern:
Define a
SharedFlow
for broadcasting messages:
In your WebSocket route, implement the broadcasting and message handling logic:
The runCatching
block processes incoming messages and emits them to the SharedFlow
, which then broadcasts to all collectors.
By using this pattern, you can efficiently manage multiple WebSocket sessions without manually tracking individual connections. This approach scales well for applications with many concurrent WebSocket connections and provides a clean, reactive way to handle message broadcasting.
For the full example, see server-websockets-sharedflow.
The WebSocket API and Ktor
The standard events from the WebSocket API map to Ktor in the following way:
onConnect
happens at the start of the block.onMessage
happens after successfully reading a message (for example, withincoming.receive()
) or using suspended iteration withfor(frame in incoming)
.onClose
happens when theincoming
channel is closed. That would complete the suspended iteration, or throw aClosedReceiveChannelException
when trying to receive a message.onError
is equivalent to other exceptions.
In both onClose
and onError
, the closeReason
property is set.
In the following example, the infinite loop will only be exited when an exception has risen (either a ClosedReceiveChannelException
or another exception):