Skip to content

Instantly share code, notes, and snippets.

@jbarop
Last active May 25, 2020 20:16
Show Gist options
  • Select an option

  • Save jbarop/d3f56ee038f448e6743cc911edfdeded to your computer and use it in GitHub Desktop.

Select an option

Save jbarop/d3f56ee038f448e6743cc911edfdeded to your computer and use it in GitHub Desktop.
A simple echo server using non blocking IO in Kotlin
import java.net.InetSocketAddress
import java.nio.ByteBuffer
import java.nio.channels.SelectionKey
import java.nio.channels.Selector
import java.nio.channels.ServerSocketChannel
import java.nio.channels.SocketChannel
import java.util.*
/**
* Represents a server.
*/
data class Server(val id: String, val channel: ServerSocketChannel)
/**
* Represents a client.
*/
data class Client(val server: Server, val channel: SocketChannel) {
private val buffer = ByteBuffer.allocate(512)!!
val id = UUID.randomUUID().toString()
var active = true
fun send(data: String) {
buffer.clear()
buffer.put(data.toByteArray())
buffer.flip()
channel.write(buffer)
}
fun handleMessage() {
buffer.clear()
val bytesRead = channel.read(buffer)
if (bytesRead == -1) {
close()
} else {
handleMessage(String(buffer.array(), 0, bytesRead))
}
}
private fun handleMessage(data: String) {
if (data.trim() == "bye") {
send("\n -- Bye Bye!\n\n")
close()
} else {
send("${data.toUpperCase()}\n")
}
}
private fun close() {
channel.close()
active = false
}
}
/**
* Creates an simple TCP listening server.
*/
fun createServer(port: Int): ServerSocketChannel {
val serverChannel = ServerSocketChannel.open()
serverChannel.socket().bind(InetSocketAddress(port))
serverChannel.configureBlocking(false)
println("Listing on ${serverChannel.socket().inetAddress}:${serverChannel.socket().localPort}")
return serverChannel
}
fun main(args: Array<String>) {
val selector = Selector.open()
arrayOf(8080, 8585, 9090, 9595)
.map { createServer(it) }
.mapIndexed { i: Int, channel: ServerSocketChannel -> Server("server-${i + 1}", channel) }
.forEach { it.channel.register(selector, SelectionKey.OP_ACCEPT, it) }
while (true) {
selector.select()
val iterator = selector.selectedKeys().iterator()
while (iterator.hasNext()) {
val selection = iterator.next()
iterator.remove()
if (selection.isAcceptable) {
val server: Server = selection.attachment() as Server
val client = Client(server, server.channel.accept())
client.channel.configureBlocking(false)
client.channel.register(selector, SelectionKey.OP_READ, client)
println("Server `${server.id}` got a new client with id `${client.id}`.")
client.send("Welcome to the upper-case-service.\nYour client id is ${client.id}\n\n")
} else if (selection.isReadable) {
val client: Client = selection.attachment() as Client
client.handleMessage()
if (!client.active) {
println("Server `${client.server.id}` lost client `${client.id}`.")
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment