Raw Sockets

    Table of contents:

    This functionality is exposed through the artifact.

    In order to create either server or client sockets, you have to use the aSocket builder,with a mandatory ActorSelectorManager: aSocket(selector). For example: aSocket(ActorSelectorManager(Dispatchers.IO)).

    Then use:

    • val socketBuilder = aSocket(selector).tcp() for a builder using TCP sockets
    • val socketBuilder = aSocket(selector).udp() for a builder using UDP sockets

    This returns a SocketBuilder that can be used to:

    • val serverSocket = aSocket(selector).tcp().bind(address) to listen to an address (for servers)
    • val clientSocket = aSocket(selector).tcp().connect(address) to connect to an address (for clients)

    Once you have a socket open by either binding or the builder,you can read from or write to the socket, by opening read/write channels:

    1. val input : ByteReadChannel = socket.openReadChannel()
    2. val output: ByteWriteChannel = socket.openWriteChannel(autoFlush = true)

    You can read the KDoc for ByteReadChanneland for further information on the available methods.

    When creating a server socket, you have to bind to a specific SocketAddress to geta ServerSocket:

    1. val server = aSocket(selector).tcp().bind(InetSocketAddress("127.0.0.1", 2323))

    The server socket has an accept method that returns, one at a time, a connected socket for each incoming connection pending in the backlog:

    If you want to support multiple clients at once, remember to call launch { } to preventthe function that is accepting the sockets from suspending.

    1. fun main(args: Array<String>) {
    2. runBlocking {
    3. println("Started echo telnet server at ${server.localAddress}")
    4. val socket = server.accept()
    5. launch {
    6. println("Socket accepted: ${socket.remoteAddress}")
    7. val input = socket.openReadChannel()
    8. val output = socket.openWriteChannel(autoFlush = true)
    9. try {
    10. while (true) {
    11. val line = input.readUTF8Line()
    12. println("${socket.remoteAddress}: $line")
    13. output.writeBytes("$line\r\n")
    14. }
    15. } catch (e: Throwable) {
    16. e.printStackTrace()
    17. socket.close()
    18. }
    19. }
    20. }
    21. }

    Then you can connect to it using telnet and start typing:

    For each line that you type (you have to press the return key), the server will replywith the same line:

    When creating a socket client, you have to connect to a specific SocketAddress to geta Socket:

    1. val socket = aSocket(selector).tcp().connect(InetSocketAddress("127.0.0.1", 2323))

    Simple Client Connecting to an Echo Server:

    echo-client.kt

    1. fun main(args: Array<String>) {
    2. runBlocking {
    3. val socket = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().connect(InetSocketAddress("127.0.0.1", 2323))
    4. val input = socket.openReadChannel()
    5. val output = socket.openWriteChannel(autoFlush = true)
    6. output.writeBytes("hello\r\n")
    7. val response = input.readUTF8Line()
    8. println("Server said: '$response'")
    9. }
    10. }

    Ktor supports secure sockets. To enable them you will need to include theio.ktor:ktor-network-tls:$ktor_version artifact, and call the .tls() to a connected socket.

    You can adjust a few optional parameters for the TLS connection:

    1. suspend fun Socket.tls(
    2. trustManager: X509TrustManager? = null,
    3. randomAlgorithm: String = "NativePRNGNonBlocking",
    4. serverName: String? = null,
    5. coroutineContext: CoroutineContext = Dispatchers.IO