Buffers

Buffers are the core of Niol. A buffer is a data container that inherits from both NiolOutput and NiolInput.

Read and write operations are separated from each other, that is, two different position indexes are used.

NiolBuffer vs RandomAccessBuffer

NiolBuffer is the superclass of all buffers. It exposes basic reading and writing methods, but hides the read and write positions. The benefit is that "special" buffers can be created, that doesn't have a linear storage, like the CircularBufer.

On the contrary, a RandomAccessBuffer is a simpler, linear data container, very similar to a java ByteBuffer but with two positions instead of one.

NioBaseBuffer - Like ByteBuffer, but better

As the name suggests, the NioBaseBuffer is based on the Java NIO ByteBuffer and serves as a base for the other buffers. It is a simple wrapper that adds no special functionality.

Memory Management

To make the provider system work and to release the direct buffers more efficiently, you have to call the discard() method when you don't need a buffer anymore.

A discarded buffer must never be used. Calling a method on a discarded buffer may or may not work, Niol provides no guarantee at all.

Some operations like duplicate() and sub() create buffers that are linked to the original buffer. The original buffer won't be collected until all its sub buffers plus itself are discarded.

valbuff=DirectNioAllocator.getBuffer(4096)
valdup= buff.duplicate
// work
buff.discard() // discard the original - this can be done before or after discarding the duplicate, it doesn't matter!
dup.discard() // discard the duplicate -> triggers memory cleanup (see below)// Both the original and the duplicate have been discarded, therefore the buffer's memory is released as soon as possible.

Non-blocking TCP Server on multiple ports

The ScalableSelector allows you to quickly create a TCP server that handles multiple ports and clients simultaneously.

Usage

The class ScalableSelector uses the Java NIO Selector and ServerSocketChannels. First, create a selector with its basic handlers:

valerrorHandler= (e: Exception) => {
// Reacts to an exception. You can decide to throw an exception to stop the server, or to continue.
}
valstartHandler= () => {
// Called when the selector is started
}
valstopHandler= () => {
// Called when the selector is stopped
}
valselector=newScalableSelector(errorHandler, startHandler, stopHandler)

Get a BufferProvider that will create one message buffer per client. Here we'll use a StageBufferPool to avoid creating a new buffer each time we receive a packet.

Then, create a subclass of ClientAttach to handle the server's clients. Each client has its instance of ClientAttach, which contains the client's informations and handles the incoming and outgoing messages.

Sending messages to clients

To send a message to the client, put it (the header + the data) in a buffer and call the ClientAttach.write method. You can add a completionHandler (in the form of a Runnable) that will be executed once the write operation is completed.