Skip to content

Instantly share code, notes, and snippets.

@latant
Last active April 22, 2020 10:24
Show Gist options
  • Select an option

  • Save latant/0940499253a1c4c9f5193f7f049671fa to your computer and use it in GitHub Desktop.

Select an option

Save latant/0940499253a1c4c9f5193f7f049671fa to your computer and use it in GitHub Desktop.
A demo of creating a Java NIO one threaded, non-blocking server coroutine API.
interface NioSocketServer {
fun stop()
suspend fun SelectableChannel.awaitSelection(ops: Int): SelectionKey
}
fun nioSocketServer(port: Int, acceptor: suspend NioSocketServer.(SocketChannel) -> Unit) {
var isStopped = false
val serverChannel = ServerSocketChannel.open().apply {
configureBlocking(false)
bind(InetSocketAddress(port))
}
val selector = Selector.open()
val dispatcher = object : CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) { block.run() }
}
val server = object : NioSocketServer {
override suspend fun SelectableChannel.awaitSelection(ops: Int) =
suspendCoroutine<SelectionKey> { cont -> register(selector, ops, cont) }
override fun stop() { isStopped = false }
}
runBlocking(dispatcher) {
while (!isStopped) {
if (selector.selectNow() > 0) {
selector.selectedKeys().onEach {
it.cancel()
(it.attachment() as Continuation<SelectionKey>).resume(it)
}.clear()
}
serverChannel.accept()?.let {
it.configureBlocking(false)
async { server.acceptor(it) }
}
}
}
}
fun main() {
nioSocketServer(80) { channel ->
val buffer = ByteBuffer.allocate(128)
val selectionKey = channel.awaitSelection(SelectionKey.OP_READ)
channel.read(buffer)
buffer.flip()
val byteArray = ByteArray(128)
buffer.get(byteArray)
println(byteArray.toString(Charsets.UTF_8))
channel.socket().close()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment