Skip to content

Instantly share code, notes, and snippets.

@ErikHellman
Last active September 7, 2020 06:01
Show Gist options
  • Save ErikHellman/7f2b28a68fd01c6c079725083e8fc202 to your computer and use it in GitHub Desktop.
Save ErikHellman/7f2b28a68fd01c6c079725083e8fc202 to your computer and use it in GitHub Desktop.
A very simple example of an echo-server using the non-blocking Java I/O APIs
package se.hellsoft.nonblocking.echo
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
/**
* A simple non-blocking echo server.
*
* To test this, start the application and use something like netcat to connect to the port (9191)
*/
fun main() {
val selector = Selector.open()
val buffer = ByteBuffer.allocate(1024)
val serverChannel = ServerSocketChannel.open()
serverChannel.configureBlocking(false)
serverChannel.bind(InetSocketAddress(9191))
serverChannel.register(selector, SelectionKey.OP_ACCEPT)
selector.selectNow()
while (true) {
val count = selector.select()
println("Selected $count channels!")
val selectedKeys = selector.selectedKeys()
selectedKeys.forEach {
if (!it.isValid) {
println("Invalid key - cancel it!")
it.cancel()
return@forEach
}
try {
when {
it.isAcceptable -> { // New connection on our ServerSocketChannel
val sc = it.channel() as ServerSocketChannel
val newClient = sc.accept()
if (newClient != null) {
println("New client!")
newClient.configureBlocking(false)
newClient.register(selector, SelectionKey.OP_READ)
}
}
it.isReadable && it.isReadInteresting -> { // Incoming data on a SocketChannel
val socket = it.channel() as SocketChannel
if (socket.isConnected) {
socket.read(buffer)
buffer.flip()
val content = ByteArray(buffer.limit())
buffer.get(content)
val text = String(content, Charsets.UTF_8)
if (text.isNotEmpty()) {
println("Received: $text")
socket.register(selector, SelectionKey.OP_WRITE, content)
}
buffer.rewind()
}
}
it.isWritable && it.isWriteInteresting -> { // SocketChannel ready for writing
val socket = it.channel() as SocketChannel
if (socket.isConnected) {
val content = it.attachment() as ByteArray
buffer.put(content)
println("Wrote ${String(content, Charsets.UTF_8)} on channel")
buffer.flip()
socket.write(buffer)
socket.register(selector, SelectionKey.OP_READ, null)
buffer.rewind()
}
}
}
} catch (e: Exception) {
println("Closing channel!")
it.channel().close()
}
}
}
}
val SelectionKey.isReadInteresting
get() = this.interestOps() and SelectionKey.OP_READ == SelectionKey.OP_READ
val SelectionKey.isWriteInteresting
get() = this.interestOps() and SelectionKey.OP_WRITE == SelectionKey.OP_WRITE
@moldovean
Copy link

How is this non-blocking?

selector.selectNow() -> is non blocking: it will almost never have anything.

while (true) {
    val count = selector.select()

selector.select() -> is blocking, so it will wait until at least one selector is selected, while(true) is redundant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment