CoralReactor (Straight to the Point)

Below the basics of CoralReactor:

You can implement your own UDP and TCP clients and servers through the classes AbstractUdpClient, AbstractTcpClient and AbstractTcpServer. See further below this article for an example of an UDP client implementation.

It also comes with some clients and servers you can use out of the box. They are: LineTcpClient/LineTcpServer (messages delimited by the newline character), BytePayloadTcpClient/BytePayloadTcpServer (payload protocol with a byte size) and ShortPayloadTcpClient/ShortPayloadTcpServer (payload protocol with a short size). Below an example of a client that extends LineTcpClient to receive messages delimited by the newline character and print them to the console.

package com.coralblocks.coralreactor.client.print;

import java.nio.ByteBuffer;

import com.coralblocks.coralbits.util.ByteBufferUtils;
import com.coralblocks.coralreactor.client.Client;
import com.coralblocks.coralreactor.client.line.LineTcpClient;
import com.coralblocks.coralreactor.nio.NioReactor;
import com.coralblocks.coralreactor.util.Configuration;
import com.coralblocks.coralreactor.util.MapConfiguration;

public class PrintLineClient extends LineTcpClient {

	public PrintLineClient(NioReactor nio, String host, int port, Configuration config) {
	    super(nio, host, port, config);
    }
	
	@Override
	protected void handleOpened() {
		// the client was open and will now start trying to connect to its destination...
		
		// do whatever you want here...
	}
	
	@Override
	protected void handleClosed() {
		// the client was closed and will now do nothing (won't connect to anywhere and just sit idle)
		
		// do whatever you want here...
	}
	
	@Override
	protected void handleConnectionEstablished() {
		// a network connection was established
		// for TCP this is a socket connection
		// for UDP that means a packet was received so a "connection" can be assumed
		
		// do whatever you want here...
	}
	
	@Override
	protected void handleConnectionOpened() {
		// any special protocol login, handshake, authentication, etc. was successfully done
		// client is now ready to start exchanging messages
		// by default a connection is opened as soon as it is established but some special clients require
		// some initial login, handshake, authentication, etc. before they can start sending/receiving messages
		
		// do whatever you want here...
		
		send("Hello there!");
	}
	
	@Override
	protected void handleConnectionTerminated() {
		// the connection was broken or terminated for any reason...
		// for TCP this is a socket termination or the disconnect() method was explicitly called on the client
		// for UDP this is a read timeout (lack of heartbeat and data for some time) or the disconnect() method was explicitly called
		// after the connection is terminated the client will automatically try to reconnect unless the close() method is called
		
		// do whatever you want here...
	}
	
	@Override
	protected void handleMessage(ByteBuffer msg) {
		
		// here is an application message received by this client...
		// it does NOT include the newline character
		
		ByteBufferUtils.println(msg);
	}
	
	public static void main(String[] args) {
		
		NioReactor nio = NioReactor.create();
		
		MapConfiguration config = new MapConfiguration();
		
		Client client = new PrintLineClient(nio, "localhost", 45151, config);
		client.open();
		
		nio.start();
	}
}

You can easily simulate a server with netcat:
Screen Shot 2015-04-25 at 9.20.58 PM

The log output from the client is:

21:20:39.023705-INFO PrintClient-localhost:45151 Client opened! sequence=1 session=null
21:20:39.083586-INFO NioReactor Reactor started! type=OptimumNioReactor impl=KQueueSelectorImpl
21:20:39.085061-INFO PrintClient-localhost:45151 Connection established!
21:20:39.085096-INFO PrintClient-localhost:45151 Connection opened!
[hi]
[this is a test]
[how are you?]

The code for a server is very similar, the difference being that all methods have a client argument since the server can handle many clients simultaneously. To make it more interesting we add a timer on each client to send a message every N seconds.

package com.coralblocks.coralreactor.server.print;

import java.nio.ByteBuffer;

import com.coralblocks.coralbits.util.ByteBufferUtils;
import com.coralblocks.coralreactor.client.Client;
import com.coralblocks.coralreactor.nio.NioReactor;
import com.coralblocks.coralreactor.server.Server;
import com.coralblocks.coralreactor.server.line.LineTcpServer;
import com.coralblocks.coralreactor.util.Configuration;
import com.coralblocks.coralreactor.util.MapConfiguration;

public class PrintLineServer extends LineTcpServer {
	
	private final int interval;

	public PrintLineServer(NioReactor nio, int port, Configuration config) {
	    super(nio, port, config);
	    this.interval = config.getInt("interval", 1000); // 1 second by default
    }
	
	@Override
	protected void handleOpened() {
		// the server was open and will now start accepting connections (i.e. clients)
		
		// do whatever you want here...
	}
	
	@Override
	protected void handleClosed() {
		// the server was closed, all clients was closed and the server will not accept any more connections
		
		// do whatever you want here...
	}
	
	@Override
	protected void handleConnectionEstablished(Client client) {
		// a network connection was established with the given client
		
		// do whatever you want here...
	}
	
	@Override
	protected void handleConnectionOpened(Client client) {
		// any special protocol login, handshake, authentication, etc. was successfully done
		// client is now ready to start exchanging messages
		// by default a connection is opened as soon as it is established but special clients require
		// some initial login, handshake, authentication, etc. before they can start sending/receiving messages
		
		// do whatever you want here...
		
		client.setEventTimeout(interval);
	}
	
	@Override
	public void handleEventTimeout(Client client, long now, int timeout) {
		client.send("Hello");
		client.setEventTimeout(timeout); // timers trigger just once so re-register for another trigger
	}
	
	@Override
	protected void handleConnectionTerminated(Client client) {
		// the connection with the given client was closed
		
		// do whatever you want here...
	}
	
	@Override
	protected void handleMessage(Client client, ByteBuffer msg) {
		
		// here is an application message received by the given client...
		// it does NOT include the newline character
		
		if (ByteBufferUtils.equals(msg, "bye")) {
			client.close();
			return;
		}
		
		System.out.print("Message from ");
		System.out.print(client);
		System.out.print(" ");
		
		ByteBufferUtils.println(msg);
	}
	
	public static void main(String[] args) {
		
		NioReactor nio = NioReactor.create();
		
		MapConfiguration config = new MapConfiguration();
		config.add("interval", 2000); // 2 seconds
		
		Server server = new PrintLineServer(nio, 45151, config);
		server.open();
		
		nio.start();
	}
}

To connect to the server we can use the PrintLineClient we just wrote. For simplicity we just use netcat:

Screen Shot 2015-04-25 at 9.55.05 PM

The log output from the server is:

21:50:51.014509-INFO PrintLineServer-45151 Server opened! host=0.0.0.0 port=45151
21:50:51.017752-INFO NioReactor Reactor started! type=OptimumNioReactor impl=KQueueSelectorImpl
21:51:13.404784-INFO TcpServerClient-127.0.0.1:54391 Client connection established!
21:51:13.404828-INFO TcpServerClient-127.0.0.1:54391 Client connection opened!
Message from TcpServerClient-127.0.0.1:54391 [Hi]
21:51:30.109062-INFO PrintLineServer-45151 Client disconnected: TcpServerClient-127.0.0.1:54391


You can also code your own clients and servers through the AbstractUdpClient, AbstractTcpClient and AbstractTcpServer. Below an example of an UDP client that receives and prints packets.

package com.coralblocks.coralreactor.client.print;

import static com.coralblocks.corallog.Log.*;

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;

import com.coralblocks.coralreactor.client.AbstractUdpClient;
import com.coralblocks.coralreactor.client.Client;
import com.coralblocks.coralreactor.nio.NioReactor;
import com.coralblocks.coralreactor.util.Configuration;
import com.coralblocks.coralreactor.util.MapConfiguration;

public class PrintPacketClient extends AbstractUdpClient {

	public PrintPacketClient(NioReactor nio, String host, int port, Configuration config) {
	    super(nio, host, port, config);
    }
	
	@Override
	protected void handleOpened() {
		// optional (here for clarity)
	}
	
	@Override
	protected void handleClosed() {
		// optional (here for clarity)
	}
	
	@Override
	protected void handleConnectionEstablished() {
		// optional (here for clarity)
	}
	
	@Override
	protected void handleConnectionOpened() {
		// optional (here for clarity)
	}
	
	@Override
	protected void handleConnectionTerminated() {
		// optional (here for clarity)
	}
	
	@Override
	protected void handleMessage(InetSocketAddress from, ByteBuffer msg) {
		Info.log(name, "Got packet:", msg);
	}
	
	public static void main(String[] args) {
		
		NioReactor nio = NioReactor.create();
		
		MapConfiguration config = new MapConfiguration();
		
		Client client = new PrintPacketClient(nio, "0.0.0.0", 45151, config);
		client.open();
		
		nio.start();
	}	
}

To code an UDP client that only sends packets, we can extend SenderUdpClient as below:

package com.coralblocks.coralreactor.client.sender;

import static com.coralblocks.corallog.Log.*;

import com.coralblocks.coralreactor.client.Client;
import com.coralblocks.coralreactor.nio.NioReactor;
import com.coralblocks.coralreactor.util.Configuration;
import com.coralblocks.coralreactor.util.MapConfiguration;

public class SenderPacketClient extends SenderUdpClient {
	
	private final int interval;

	public SenderPacketClient(NioReactor nio, String host, int port, Configuration config) {
	    super(nio, host, port, config);
	    this.interval = config.getInt("interval", 1000); // 1 second by default
    }
	
	@Override
	protected void handleOpened() {
		// optional (here for clarity)
	}
	
	@Override
	protected void handleClosed() {
		// optional (here for clarity)
	}
	
	@Override
	protected void handleConnectionEstablished() {
		// optional (here for clarity)
	}
	
	@Override
	protected void handleConnectionOpened() {
		setEventTimeout(interval);
	}
	
	@Override
	protected void handleConnectionTerminated() {
		// optional (here for clarity)
	}
	
	@Override
	protected void handleEventTimeout(long now, int timeout) {
		Info.log(name, "Sending packet...");
		send("Hello there!");
		if (isConnectionOpen()) { // just in case there was an error sending the UDP packet and the client was closed
			setEventTimeout(timeout); // re-register so it happens again (timers trigger only once)
		}
	}
	
	public static void main(String[] args) {
		
		NioReactor nio = NioReactor.create();
		
		MapConfiguration config = new MapConfiguration();
		
		config.add("sendAddress", "localhost");
		config.add("sendPort", 45151);
		
		String bindAddress = "0.0.0.0";
		int bindPort = 0; // choose any free port to bind (ephemeral port)
		
		Client client = new SenderPacketClient(nio, bindAddress, bindPort, config);
		client.open();
		
		nio.start();
	}
}

Now if we run both clients, we get the following log output:

02:53:41.328129-INFO SenderPacketClient-localhost:45151 Bound datagram channel to /0.0.0.0:55365
02:53:41.329297-INFO SenderPacketClient-localhost:45151 Client opened! sequence=1 session=null
02:53:41.331682-INFO SenderPacketClient-localhost:45151 Connection established!
02:53:41.333870-INFO SenderPacketClient-localhost:45151 Connection opened!
02:53:41.334212-INFO NioReactor Reactor started! type=OptimumNioReactor impl=KQueueSelectorImpl
02:53:41.800535-INFO SenderPacketClient-localhost:45151 Sending packet...
02:53:42.801833-INFO SenderPacketClient-localhost:45151 Sending packet...
02:53:43.802194-INFO SenderPacketClient-localhost:45151 Sending packet...
03:00:08.926596-INFO PrintPacketClient-0.0.0.0:45151 Bound datagram channel to /0.0.0.0:45151
03:00:08.927742-INFO PrintPacketClient-0.0.0.0:45151 Client opened! sequence=1 session=null
03:00:08.937026-INFO NioReactor Reactor started! type=OptimumNioReactor impl=KQueueSelectorImpl
03:00:08.943330-INFO PrintPacketClient-0.0.0.0:45151 Connection established!
03:00:08.943557-INFO PrintPacketClient-0.0.0.0:45151 Connection opened!
03:00:08.943622-INFO PrintPacketClient-0.0.0.0:45151 Got packet: Hello there!
03:00:09.838106-INFO PrintPacketClient-0.0.0.0:45151 Got packet: Hello there!
03:00:10.838293-INFO PrintPacketClient-0.0.0.0:45151 Got packet: Hello there!