Ktor client WebSocket plugin - provides WebSocket support for the Ktor HTTP client on multiple platforms including iOS x64
—
Ktor Client WebSockets provides automatic serialization and deserialization of objects through WebSocket frames using Ktor's content conversion system.
Send objects as WebSocket frames with automatic serialization:
suspend fun DefaultClientWebSocketSession.sendSerialized(
data: Any?,
typeInfo: TypeInfo
)
suspend inline fun <reified T> DefaultClientWebSocketSession.sendSerialized(
data: T
)Parameters:
Usage:
import io.ktor.client.plugins.websocket.*
import kotlinx.serialization.*
@Serializable
data class Message(val text: String, val timestamp: Long)
client.webSocket("ws://example.com/api") {
val message = Message("Hello World", System.currentTimeMillis())
// Send with reified type (recommended)
sendSerialized(message)
// Send with explicit TypeInfo
sendSerialized(message, typeInfo<Message>())
}Receive WebSocket frames and automatically deserialize to objects:
suspend fun <T> DefaultClientWebSocketSession.receiveDeserialized(
typeInfo: TypeInfo
): T
suspend inline fun <reified T> DefaultClientWebSocketSession.receiveDeserialized(): TParameters:
Returns:
Usage:
client.webSocket("ws://example.com/api") {
// Receive with reified type (recommended)
val message = receiveDeserialized<Message>()
println("Received: ${message.text} at ${message.timestamp}")
// Receive with explicit TypeInfo
val message2 = receiveDeserialized<Message>(typeInfo<Message>())
println("Received: ${message2.text}")
}Content serialization requires a WebSocket content converter to be configured in the WebSockets plugin:
var WebSockets.Config.contentConverter: WebsocketContentConverter?Using kotlinx.serialization with JSON:
import io.ktor.client.*
import io.ktor.client.plugins.websocket.*
import io.ktor.serialization.kotlinx.*
import kotlinx.serialization.json.*
val client = HttpClient {
install(WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
})
}
}Implement custom serialization logic:
import io.ktor.websocket.*
import io.ktor.util.*
import io.ktor.util.reflect.*
class MyWebSocketConverter : WebsocketContentConverter {
override suspend fun serialize(
charset: Charset,
typeInfo: TypeInfo,
value: Any?
): Frame {
// Custom serialization logic
val serialized = mySerialize(value)
return Frame.Text(serialized)
}
override suspend fun deserialize(
charset: Charset,
typeInfo: TypeInfo,
content: Frame
): Any? {
// Custom deserialization logic
return when (content) {
is Frame.Text -> myDeserialize(content.readText(), typeInfo)
is Frame.Binary -> myDeserializeBinary(content.data, typeInfo)
else -> throw WebSocketException("Unsupported frame type for deserialization")
}
}
override fun isApplicable(frame: Frame): Boolean {
return frame is Frame.Text || frame is Frame.Binary
}
}
// Install custom converter
val client = HttpClient {
install(WebSockets) {
contentConverter = MyWebSocketConverter()
}
}import kotlinx.serialization.*
@Serializable
data class ChatMessage(
val user: String,
val message: String,
val timestamp: Long = System.currentTimeMillis()
)
client.webSocket("ws://chat.example.com/room/general") {
// Send chat message
val chatMsg = ChatMessage("alice", "Hello everyone!")
sendSerialized(chatMsg)
// Receive chat messages
for (frame in incoming) {
when (frame) {
is Frame.Text -> {
try {
val received = receiveDeserialized<ChatMessage>()
println("${received.user}: ${received.message}")
} catch (e: Exception) {
println("Failed to deserialize: ${frame.readText()}")
}
}
is Frame.Close -> break
else -> { /* Handle other frame types */ }
}
}
}@Serializable
data class GameState(
val players: List<Player>,
val currentTurn: String,
val board: Map<String, String>,
val settings: GameSettings
)
@Serializable
data class Player(val id: String, val name: String, val score: Int)
@Serializable
data class GameSettings(val maxPlayers: Int, val timeLimit: Long)
client.webSocket("ws://game.example.com/session/123") {
// Send complex game state
val gameState = GameState(
players = listOf(
Player("p1", "Alice", 100),
Player("p2", "Bob", 85)
),
currentTurn = "p1",
board = mapOf("a1" to "X", "b2" to "O"),
settings = GameSettings(maxPlayers = 4, timeLimit = 300_000)
)
sendSerialized(gameState)
// Receive updated game state
val updatedState = receiveDeserialized<GameState>()
println("Game updated: ${updatedState.players.size} players")
}Combine serialized objects with raw frames:
client.webSocket("ws://api.example.com/mixed") {
// Send serialized object
sendSerialized(ChatMessage("system", "User connected"))
// Send raw text frame
send("Raw status message")
// Receive mixed content
for (frame in incoming) {
when (frame) {
is Frame.Text -> {
val text = frame.readText()
// Try to deserialize as known type
try {
val message = receiveDeserialized<ChatMessage>()
println("Structured: ${message.user} - ${message.message}")
} catch (e: Exception) {
// Handle as raw text
println("Raw: $text")
}
}
is Frame.Close -> break
else -> { /* Handle other frames */ }
}
}
}Handle serialization failures gracefully:
client.webSocket("ws://example.com/api") {
try {
val message = Message("test", System.currentTimeMillis())
sendSerialized(message)
} catch (e: SerializationException) {
println("Failed to serialize message: ${e.message}")
// Fallback to raw frame
send("Serialization failed - sending raw text")
} catch (e: Exception) {
println("Unexpected error during serialization: ${e.message}")
}
}Handle deserialization failures:
client.webSocket("ws://example.com/api") {
for (frame in incoming) {
when (frame) {
is Frame.Text -> {
try {
val message = receiveDeserialized<Message>()
processMessage(message)
} catch (e: SerializationException) {
println("Failed to deserialize frame: ${e.message}")
// Handle as raw text
val rawText = frame.readText()
processRawMessage(rawText)
} catch (e: Exception) {
println("Unexpected deserialization error: ${e.message}")
}
}
is Frame.Close -> break
else -> { /* Handle other frame types */ }
}
}
}Handle cases where no content converter is configured:
client.webSocket("ws://example.com/api") {
if (converter != null) {
// Use serialization
sendSerialized(MyData("test"))
val response = receiveDeserialized<MyResponse>()
} else {
// Fall back to raw frames
send("Raw message without serialization")
val frame = incoming.receive()
if (frame is Frame.Text) {
val response = frame.readText()
println("Raw response: $response")
}
}
}Handle binary data serialization:
@Serializable
data class BinaryMessage(
val header: String,
val payload: ByteArray
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is BinaryMessage) return false
if (header != other.header) return false
if (!payload.contentEquals(other.payload)) return false
return true
}
override fun hashCode(): Int {
var result = header.hashCode()
result = 31 * result + payload.contentHashCode()
return result
}
}
// Configure for binary serialization
val client = HttpClient {
install(WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(
ProtoBuf // or other binary format
)
}
}
client.webSocket("ws://example.com/binary") {
// Send binary message
val binaryMsg = BinaryMessage("header", byteArrayOf(1, 2, 3, 4))
sendSerialized(binaryMsg)
// Receive binary message
val received = receiveDeserialized<BinaryMessage>()
println("Header: ${received.header}, Payload size: ${received.payload.size}")
}For large objects, consider chunking:
@Serializable
data class LargeDataSet(val items: List<DataItem>)
client.webSocket("ws://example.com/large-data") {
val largeData = generateLargeDataSet()
// Check frame size limits
if (maxFrameSize < estimateSerializedSize(largeData)) {
// Chunk the data
largeData.items.chunked(100).forEach { chunk ->
sendSerialized(DataChunk(chunk))
}
sendSerialized(EndOfData())
} else {
// Send as single frame
sendSerialized(largeData)
}
}For continuous data streams:
client.webSocket("ws://example.com/stream") {
// Stream data as individual serialized frames
generateDataStream().collect { dataPoint ->
sendSerialized(dataPoint)
// Optional: Add throttling
delay(10) // 100 FPS max
}
}Different serialization formats can be configured:
// JSON serialization
install(WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(Json)
}
// Protocol Buffers serialization
install(WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(ProtoBuf)
}
// CBOR serialization
install(WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(Cbor)
}
// XML serialization
install(WebSockets) {
contentConverter = KotlinxWebsocketSerializationConverter(XML)
}Install with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor-client-websockets