Last active
May 25, 2020 20:16
-
-
Save jbarop/d3f56ee038f448e6743cc911edfdeded to your computer and use it in GitHub Desktop.
A simple echo server using non blocking IO in Kotlin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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