Skip to content

Server Setup

Trevor Flynn edited this page Jan 15, 2021 · 1 revision

Server Intro

The Monkey-Netty server was build on Netty.IO. The server is designed as an jMonkeyEngine AppState, and is fully self contained. The server will create both a TCP and UDP server on their own threads to listen for connections from the clients. It has features built in for things like SSL, connection limits/blocking, and listeners. The server will manage all the client connections for you, while giving you the flexibility to use those clients as you wish in your code.

Getting Started

To get a simple NettyServer running, you will create the server, and attach it to your jMonkeyEngine server app.

public class JmeTestServer extends SimpleApplication {

    @Override
    public void simpleInitApp() {
        NettyServer server = new NettyServer("test", 10000);
        stateManager.attach(server);
    }

    public static void main(String[] args) {
        JmeTestServer server = new JmeTestServer();
        server.start(JmeContext.Type.Headless);
    }
}

In this example, we are creating a server with the service name test and listening on port 10,000. This server is now ready to accept connections, although we are not currently doing much with those connections.

Listeners

To utilize the functionality of the server and gain access to the clients, the server has listeners. Currently two types of listeners are supported: ConnectionListener and MessageListener You can have as many of each listener as you would like, and can register or unregister them at any time.

Connection Listener

The ConnectionListener will listener for clients to connect and disconnect. To add a connection listener, simply register one with the server:

public class JmeTestServer extends SimpleApplication {

    @Override
    public void simpleInitApp() {
        NettyServer server = new NettyServer("test", 10000);

        server.registerListener(new ConnectionListener() {
            @Override
            public void onConnect(NetworkClient client) {
                System.out.println("Client connected: " + client.getAddress());
            }

            @Override
            public void onDisconnect(NetworkClient client) {
                System.out.println("Client disconnected: " + client.getAddress());
            }
        });

        stateManager.attach(server);
    }

    public static void main(String[] args) {
        JmeTestServer server = new JmeTestServer();
        server.start(JmeContext.Type.Headless);
    }
}

You can now see that you have access to the NetworkClient that has connected to the server. The NetworkClient is your connection to each client session, and will allow you to access information about the client, send messages to the client, and add custom message logic to each client.

Message Listener

There are times when you want to perform a certain action when the server receives a message, in fact, this is perhaps the purpose of having a network server. The MessageListener will listen for messages from the client, and notify you what message was received, and by which client. This is where most of your network logic will live.

We can simply register a MessageListener with the server:

public class JmeTestServer extends SimpleApplication {

    @Override
    public void simpleInitApp() {
        NettyServer server = new NettyServer("test", 10000);
        server.registerListener(new MessageListener() {
            @Override
            public void onMessage(NetworkMessage msg, NetworkServer server, NetworkClient client) {
                System.out.println("Got message " + msg.getName() + " from client " + client.getAddress());
                System.out.println(msg.toString());
                client.send(msg);
            }

            @Override
            public Class<? extends NetworkMessage>[] getSupportedMessages() {
                return new Class[] {TestUDPMessage.class, TestTCPMessage.class};
            }
        });


        stateManager.attach(server);
    }

    public static void main(String[] args) {
        JmeTestServer server = new JmeTestServer();
        server.start(JmeContext.Type.Headless);
    }
}

In this example, we created a listener that will listen for a message, print the message name and the address of the client that sent it to us, then sends the message back to the client. You will notice that the listener has the function getSupportedMessages(), this acts as a filter for what type of messages the listener will receive.

More Features

The server also has some other built in features that are useful for simple apps.

Connection Blocking

You may want to prevent incoming connections until you app is ready. In which case, you can enable connection blocking at any time to prevent incoming connections.

public class JmeTestServer extends SimpleApplication {

    @Override
    public void simpleInitApp() {
        NettyServer server = new NettyServer("test", 10000);
        server.setBlocking(true);
        stateManager.attach(server);

        ... some app init logic

        server.setBlocking(false);
    }

    public static void main(String[] args) {
        JmeTestServer server = new JmeTestServer();
        server.start(JmeContext.Type.Headless);
    }
}

Connection Limits

Most servers will want to limit the number of clients that can connect to the server. In which case, we can set the connection limit.

public class JmeTestServer extends SimpleApplication {

    @Override
    public void simpleInitApp() {
        NettyServer server = new NettyServer("test", 10000);
        server.setMaxConnections(20);
        stateManager.attach(server);
    }

    public static void main(String[] args) {
        JmeTestServer server = new JmeTestServer();
        server.start(JmeContext.Type.Headless);
    }
}

Here we set the max number of clients to 20.

You can check the number of connections on the server and implement custom limiting/blocking logic.

public class JmeTestServer extends SimpleApplication {

    @Override
    public void simpleInitApp() {
        NettyServer server = new NettyServer("test", 10000);
        server.setMaxConnections(20);
        server.registerListener(new ConnectionListener() {
            @Override
            public void onConnect(NetworkClient client) {
                //Reserve 5 connections for internal network
                if (server.getConnections() >= (server.getMaxConnections() - 5) && !client.getAddress().startsWith("192.168")) {
                    client.disconnect();
                    return;
                }
                System.out.println("Client connected: " + client.getAddress());
            }

            @Override
            public void onDisconnect(NetworkClient client) {
                System.out.println("Client disconnected: " + client.getAddress());
            }
        });
        stateManager.attach(server);
    }

    public static void main(String[] args) {
        JmeTestServer server = new JmeTestServer();
        server.start(JmeContext.Type.Headless);
    }
}

Here we set the max number of connections to 20, but in our connection listener we check the number of connections, and if the connections are larger than five less than the max, we only allow connections from our local network.

Internal Logger

The server contains an internal logger for debugging. You can enable and disable it.

public class JmeTestServer extends SimpleApplication {

    @Override
    public void simpleInitApp() {
        NettyServer server = new NettyServer("test", 10000);
        server.setLogLevel(LogLevel.DEBUG);
        stateManager.attach(server);
    }

    public static void main(String[] args) {
        JmeTestServer server = new JmeTestServer();
        server.start(JmeContext.Type.Headless);
    }
}

SSL Support

The server also supports SSL on the TCP channel. This allows for sending sensitive information such as users' username and password. You can use self signed certificates that are auto generated by the server, or specify a specific certificate for the server to use.

Here is a simple self signed example:

public class JmeTestServer extends SimpleApplication {

    @Override
    public void simpleInitApp() {
        NettyServer server = new NettyServer("test", true, 10000);
        stateManager.attach(server);
    }

    public static void main(String[] args) {
        JmeTestServer server = new JmeTestServer();
        server.start(JmeContext.Type.Headless);
    }
}

Here you will notice an additional parameter set to true in the NettyServer constructor. This enables SSL, and if set to false SSL will be disabled. SSL can only be enabled or disabled during the creation of the server. You can check the SSL status by using server.isSsl(), as the server will fallback to non-ssl if it fails to load a certificate.

The server has two more ssl constructors:

public NettyServer(String service, boolean ssl, File cert, File key, int port)

private NettyServer(String service, boolean ssl, boolean selfGenCert, File cert, File key, int port)

You can use these to pass a certificate to the server, and specify if it should fallback to a self generated certificate.

If using SSL, you will need to tell the client using the client's constructor:

NettyClient client = new NettyClient("test", true, 10000, "localhost");

The client also contains a constructor to disable self signed certificates:

public NettyClient(String service, boolean ssl, boolean sslSelfSigned, int port, String server)