Ktor 3.0.1 Help

Sockets

In addition to HTTP/WebSocket handling for the server and client, Ktor supports TCP and UDP raw sockets. It exposes a suspending API that uses java.nio under the hood.

Add dependencies

To use Sockets, you need to include the ktor-network artifact in the build script:

implementation("io.ktor:ktor-network:$ktor_version")
implementation "io.ktor:ktor-network:$ktor_version"
<dependency> <groupId>io.ktor</groupId> <artifactId>ktor-network-jvm</artifactId> <version>${ktor_version}</version> </dependency>

To use secure sockets in the client, you also need to add io.ktor:ktor-network-tls.

Server

Create a server socket

To build a server socket, create the SelectorManager instance, call the SocketBuilder.tcp() function on it, and then use bind to bind a server socket to specific port:

val selectorManager = SelectorManager(Dispatchers.IO) val serverSocket = aSocket(selectorManager).tcp().bind("127.0.0.1", 9002)

The snippet above creates a TCP socket, which is the ServerSocket instance. To create a UDP socket, use SocketBuilder.udp().

Accept incoming connections

After creating a server socket, you need to call the ServerSocket.accept function that accepts a socket connection and returns a connected socket (a Socket instance):

val socket = serverSocket.accept()

Once you have a connected socket, you can receive/send data by reading from or writing to the socket.

Receive data

To receive data from the client, you need to call the Socket.openReadChannel function, which returns ByteReadChannel:

val receiveChannel = socket.openReadChannel()

ByteReadChannel provides API for asynchronous reading of data. For example, you can read a line of UTF-8 characters using ByteReadChannel.readUTF8Line:

val name = receiveChannel.readUTF8Line()

Send data

To send data to the client, call the Socket.openWriteChannel function, which returns ByteWriteChannel:

val sendChannel = socket.openWriteChannel(autoFlush = true)

ByteWriteChannel provides API for asynchronous writing of sequences of bytes. For example, you can write a line of UTF-8 characters using ByteWriteChannel.writeStringUtf8:

val name = receiveChannel.readUTF8Line() sendChannel.writeStringUtf8("Hello, $name!\n")

Close a socket

To release resources associated with the connected socket, call Socket.close:

socket.close()

Example

A code sample below demonstrates how to use sockets on the server side:

package com.example import io.ktor.network.selector.* import io.ktor.network.sockets.* import io.ktor.utils.io.* import kotlinx.coroutines.* fun main(args: Array<String>) { runBlocking { val selectorManager = SelectorManager(Dispatchers.IO) val serverSocket = aSocket(selectorManager).tcp().bind("127.0.0.1", 9002) println("Server is listening at ${serverSocket.localAddress}") while (true) { val socket = serverSocket.accept() println("Accepted $socket") launch { val receiveChannel = socket.openReadChannel() val sendChannel = socket.openWriteChannel(autoFlush = true) sendChannel.writeStringUtf8("Please enter your name\n") try { while (true) { val name = receiveChannel.readUTF8Line() sendChannel.writeStringUtf8("Hello, $name!\n") } } catch (e: Throwable) { socket.close() } } } } }

You can find the full example here: sockets-server.

Client

Create a socket

To build a client socket, create the SelectorManager instance, call the SocketBuilder.tcp() function on it, and then use connect to establish a connection and get a connected socket (a Socket instance):

val selectorManager = SelectorManager(Dispatchers.IO) val socket = aSocket(selectorManager).tcp().connect("127.0.0.1", 9002)

Once you have a connected socket, you can receive/send data by reading from or writing to the socket.

Create a secure socket (SSL/TLS)

Secure sockets allow you to establish TLS connections. To use secure sockets, you need to add the ktor-network-tls dependency. Then, call the Socket.tls function on a connected socket:

val selectorManager = SelectorManager(Dispatchers.IO) val socket = aSocket(selectorManager).tcp().connect("127.0.0.1", 8443).tls()

The tls function allows you to adjust TLS parameters provided by TLSConfigBuilder:

val selectorManager = SelectorManager(Dispatchers.IO) val socket = aSocket(selectorManager).tcp().connect("youtrack.jetbrains.com", port = 443).tls(coroutineContext = coroutineContext) { trustManager = object : X509TrustManager { override fun getAcceptedIssuers(): Array<X509Certificate?> = arrayOf() override fun checkClientTrusted(certs: Array<X509Certificate?>?, authType: String?) {} override fun checkServerTrusted(certs: Array<X509Certificate?>?, authType: String?) {} } }

You can find the full example here: sockets-client-tls.

Receive data

To receive data from the server, you need to call the Socket.openReadChannel function, which returns ByteReadChannel:

val receiveChannel = socket.openReadChannel()

ByteReadChannel provides API for asynchronous reading of data. For example, you can read a line of UTF-8 characters using ByteReadChannel.readUTF8Line:

val greeting = receiveChannel.readUTF8Line()

Send data

To send data to the server, call the Socket.openWriteChannel function, which returns ByteWriteChannel:

val sendChannel = socket.openWriteChannel(autoFlush = true)

ByteWriteChannel provides API for asynchronous writing of sequences of bytes. For example, you can write a line of UTF-8 characters using ByteWriteChannel.writeStringUtf8:

val myMessage = readln() sendChannel.writeStringUtf8("$myMessage\n")

Close connection

To release resources associated with the connected socket, call Socket.close and SelectorManager.close:

socket.close() selectorManager.close()

Example

A code sample below demonstrates how to use sockets on the client side:

package com.example import io.ktor.network.selector.* import io.ktor.network.sockets.* import io.ktor.utils.io.* import kotlinx.coroutines.* import kotlin.system.* fun main(args: Array<String>) { runBlocking { val selectorManager = SelectorManager(Dispatchers.IO) val socket = aSocket(selectorManager).tcp().connect("127.0.0.1", 9002) val receiveChannel = socket.openReadChannel() val sendChannel = socket.openWriteChannel(autoFlush = true) launch(Dispatchers.IO) { while (true) { val greeting = receiveChannel.readUTF8Line() if (greeting != null) { println(greeting) } else { println("Server closed a connection") socket.close() selectorManager.close() exitProcess(0) } } } while (true) { val myMessage = readln() sendChannel.writeStringUtf8("$myMessage\n") } } }

You can find the full example here: sockets-client.

Last modified: 02 April 2024