Ktor client WebSocket plugin - provides WebSocket support for the Ktor HTTP client on multiple platforms including iOS x64
—
WebSocket session operations provide access to client-specific session interfaces, HTTP call context, and core frame communication functionality.
The main interface for client WebSocket sessions that provides access to the associated HTTP call:
interface ClientWebSocketSession : WebSocketSession {
val call: HttpClientCall
}Usage:
client.webSocketSession("ws://example.com/") { session ->
// Access HTTP call context
println("Request URL: ${session.call.request.url}")
println("Response status: ${session.call.response.status}")
// Use WebSocket functionality
session.send("Hello!")
}The default implementation used by all WebSocket connection functions:
class DefaultClientWebSocketSession(
override val call: HttpClientCall,
delegate: DefaultWebSocketSession
) : ClientWebSocketSession, DefaultWebSocketSession by delegateProperties inherited from WebSocketSession:
var masking: Boolean // Frame masking (client-side typically true)
var maxFrameSize: Long // Maximum frame size limit
val incoming: ReceiveChannel<Frame> // Channel for incoming frames
val outgoing: SendChannel<Frame> // Channel for outgoing frames
val extensions: List<WebSocketExtension<*>> // Active WebSocket extensionsProperties inherited from DefaultWebSocketSession:
var pingIntervalMillis: Long // Ping interval in milliseconds
var timeoutMillis: Long // Session timeout in milliseconds
val closeReason: Deferred<CloseReason?> // Deferred close reasonSend different types of WebSocket frames:
client.webSocket("ws://example.com/") {
// Send text frame (convenience method)
send("Hello WebSocket!")
// Send explicit text frame
send(Frame.Text("Explicit text frame"))
// Send binary frame
val data = "Binary data".toByteArray()
send(Frame.Binary(true, data))
// Send ping frame
send(Frame.Ping("ping-data".toByteArray()))
// Send pong frame (usually automatic)
send(Frame.Pong("pong-data".toByteArray()))
// Send close frame
send(Frame.Close(CloseReason(CloseReason.Codes.NORMAL, "Goodbye")))
}Process incoming WebSocket frames:
client.webSocket("ws://example.com/") {
// Receive single frame
val frame = incoming.receive()
when (frame) {
is Frame.Text -> {
val text = frame.readText()
println("Text: $text")
}
is Frame.Binary -> {
val data = frame.data
println("Binary: ${data.size} bytes")
}
is Frame.Close -> {
val reason = frame.readReason()
println("Close: ${reason?.code} - ${reason?.message}")
}
is Frame.Ping -> {
println("Ping received")
// Pong response usually automatic
}
is Frame.Pong -> {
println("Pong received")
}
}
// Process all incoming frames
for (frame in incoming) {
when (frame) {
is Frame.Text -> handleTextFrame(frame.readText())
is Frame.Binary -> handleBinaryFrame(frame.data)
is Frame.Close -> {
println("Connection closed: ${frame.readReason()}")
break
}
else -> println("Other frame: $frame")
}
}
}Convenience methods for working with frames:
// Text frame utilities
fun Frame.Text.readText(): String // Read frame content as text
// Binary frame utilities
val Frame.Binary.data: ByteArray // Access binary data
// Close frame utilities
fun Frame.Close.readReason(): CloseReason? // Read close reasonCreate and manage sessions manually:
val session = client.webSocketSession("ws://example.com/")
try {
// Use session
session.send("Hello!")
val response = session.incoming.receive()
println("Response: $response")
} finally {
// Always close manually created sessions
session.close(CloseReason(CloseReason.Codes.NORMAL, "Done"))
}Use session blocks for automatic management:
client.webSocket("ws://example.com/") {
// Session automatically closed when block exits
send("Hello!")
val response = incoming.receive()
println("Response: $response")
// Explicit close optional
close(CloseReason(CloseReason.Codes.NORMAL, "Finished"))
}suspend fun WebSocketSession.send(frame: Frame)
suspend fun WebSocketSession.flush()
suspend fun WebSocketSession.close(reason: CloseReason? = null)var DefaultWebSocketSession.pingIntervalMillis: Long // Ping interval
var DefaultWebSocketSession.timeoutMillis: Long // Timeout for responses
val DefaultWebSocketSession.closeReason: Deferred<CloseReason?> // Final close reasonUsage:
client.webSocket("ws://example.com/") {
// Configure ping behavior
pingIntervalMillis = 30_000 // 30 seconds
timeoutMillis = 60_000 // 60 seconds timeout
send("Configured connection!")
// Wait for close reason
val reason = closeReason.await()
println("Session closed: $reason")
}Handle session-level errors and exceptions:
client.webSocket("ws://example.com/") {
try {
send("Test message")
for (frame in incoming) {
when (frame) {
is Frame.Text -> processMessage(frame.readText())
is Frame.Close -> {
val reason = frame.readReason()
if (reason?.code != CloseReason.Codes.NORMAL.code) {
println("Abnormal close: ${reason?.message}")
}
break
}
else -> { /* Handle other frames */ }
}
}
} catch (e: ClosedReceiveChannelException) {
println("Connection closed unexpectedly")
} catch (e: WebSocketException) {
println("WebSocket error: ${e.message}")
} catch (e: Exception) {
println("Session error: ${e.message}")
// Close gracefully on error
close(CloseReason(CloseReason.Codes.INTERNAL_ERROR, "Client error"))
}
}Access the configured content converter for serialization:
val DefaultClientWebSocketSession.converter: WebsocketContentConverter?Usage:
client.webSocket("ws://example.com/") {
val converter = this.converter
if (converter != null) {
println("Content converter available: ${converter::class.simpleName}")
// Use sendSerialized/receiveDeserialized
} else {
println("No content converter configured")
// Use raw frame operations
}
}Install with Tessl CLI
npx tessl i tessl/maven-io-ktor--ktor-client-websockets