WebSockets in Ktor Server
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
embeddedServerfunction call.... inside the explicitly defined
module, which is an extension function of theApplicationclass.
Configure WebSockets
Optionally, you can configure the plugin inside the install block by passing WebSocketOptions:
Use the
pingPeriodproperty to specify the duration between pings.Use the
timeoutproperty to set a timeout after which the connection to be closed.Use the
maxFrameSizeproperty to set a maximum Frame that could be received or sent.Use the
maskingproperty to specify whether masking is enabled.Use the
contentConverterproperty 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
sendfunction to send text content to the client.Use the
incomingandoutgoingproperties to access the channels for receiving and sending WebSocket frames. A frame is represented by theFrameclass.Use the
closefunction to send a close frame with the specified reason.
When handling a session, you can check a frame type, for example:
Frame.Textis a text frame. For this frame type, you can read its content usingFrame.Text.readText().Frame.Binaryis 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
SharedFlowfor 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:
onConnecthappens at the start of the block.onMessagehappens after successfully reading a message (for example, withincoming.receive()) or using suspended iteration withfor(frame in incoming).onClosehappens when theincomingchannel is closed. That would complete the suspended iteration, or throw aClosedReceiveChannelExceptionwhen trying to receive a message.onErroris 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):