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:

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:
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!
