Skip to content

Connections

Thorben Kuck edited this page Sep 20, 2018 · 8 revisions

If you want, you can create multiple connections per Client. A Connection is a communication-chanel.

Any Connection has to be established AFTER the ClientStart/ServerStart is launched! This is important, because the DefaultConnection should exist at the Point where you want to create the new Connection.

Default

By default, NetCom2 uses the DefaultConnection. The DefaultConnection is a Class, that identifies the Connection, as an Connection, that is established at the point the ClientStart and the ServerStart initially connect. Please note, that using multiple Sockets per client is expensive.


A Connection

A connection is an interface, which represents a Socket-connection. Connections by default can be either TCP, UDP and NIO.

Before diving into the different existing Connection-Types, let me state, that the UDP-Connection is currently in an experimental state. This should not be used by now, especially at the Server side!

TCP

The TCP Connection uses the Java Socket implementations, to establish TCP-Connections between the ServerStart and the ClientStart. You can establish a ServerStart and a ClientStart, which uses this implementations of TCP like this:

int port = ...
String hostname = ....
ServerStart serverStart = ServerStart.tcp(port);
ClientStart clientStart = ClientStart.tcp(hostname, port);

A TCPConnection uses a Thread, to listen for incoming messages.

NIO

Technically speaking, the NIO-Connection is also a TCP-Connection. The difference lies within the used Blocking-Mode. The NIO-Connection is a non-blocking way and utilizes SocketChannels, combined with Selectors. As of Version 2.0, it is the main-connection. Therefore, you can just state the following to establish a NIO connection:

int port = ...
String hostname = ....
ServerStart serverStart = ServerStart.at(port);
ClientStart clientStart = ClientStart.at(hostname, port);

Both NetworkInterfaces also allow you to specifically request a NIO-Type, by calling the method nio(/* parameters */).

Connections-Mechanisms

The mechanism, introduced with version 2 is the following:

ServerStart

The ServerStart uses a ConnectorCore, to establish a ServerSocket, to listen for the new Clients. Once connected, a new Connection is created and passed to a EventLoop. Non-Blocking communications EventLoop uses a Selector to find Connections, that aggregate SocketChannels, that has pending data to be read. Within the blocking Communication, this EventLoop passes a Callback to the Connection, which is notified once the extracted Thread reads new Data.

The Blocking-/NonBlocking-EventLoop is defined by which way you created the respecting NetworkInterface.


Communication Mechanism

Upon calling launch or establishing a new Connection, between the ServerStart and ClientStart, the two will exchange a (at least) 5-way Handshake Protocol. It is defined in the following way:

Connection establishment communication

First of, the optional part in which the NewConnectionRequests are send over the Network, is used to signal a new Connection establishment. You can send a NewConnectionRequest from either side and the Response will be the same; A new Connection will be established.

After his, the NewConnectionInitializer ist send from the Client to the Server, which tells the Server what the type of the Connection is. The Connection will then be stored within the Client. Once done, the NewConnectionInitializer will be send back to the Client.

Afterwards, the NewConnectionResponse will be send from the Client to the Server.

The Client responses with a NewConnectionResponse. It contains the current ClientId within it. The Server will afterwards check the ClientId and the connected Client, checking multiple things and lastly answer with a Ping, which contains the ClientId this connected Client should use.

The Ping, which is exchanged between the two is the signal, that the Connection is correctly set and can be used.

If you want to construct a custom Network-Communication, this is what you should implement.


Writing to the Connection

The Connection has two methods to write to it. Both are named write. One takes bytes, another one takes a String.

If you want to write a custom message to the Connection (aka call connection.write(/* args */)), make sure, that whatever you pass in there end on "\r\n". This signals to the ConnectionHandler, that the Object is complete. This is done, to compensate the fact, that the message might be split into multiple small sub-packages and therefor also allow to send objects of any size.

For Example:

If you want to write the message "Hello" to the Connection, you should do it the following way:

Connection connection = ...
connection.write("Hello\r\n");

If you want to use bytes, make sure that the byte-array ends with [13, 10]. So, the same example as above would look like this:

Connection connection = ...
connection.write(new byte[]{72, 101, 108, 108, 111, 13, 10});

The byte-series of "72, 101, 108, 108, 111" is "Hello" by the way.


Initializing a new connection from the clientside

Initializing a new Connection from the clientside is easy. The ClientStart wrapps the newConnection method to encapsulate the Client. So just do the following:

ClientStart clientStart = ClientStart.at(4444)
clientStart.launch();
//Returns a synchronization mechanism, to await the creation of the Connection
Awaitin awaitin = clientStart.newConnection(TestObject.class);
// with this call, the current thread waits, until the connection is completed.
//It throws a InterruptedException, if an error happens.
awaitin.synchronize();
//sending over the new connection
//Note that the Object alone has no effect, over which connection ist is going to be send!
ClientStart.send().objectToServer(new TestObject(), TestObject.class);

Initializing a new Connection from the serverside:

This is a bit more tricky, than you might think! A new Connection has to be initialized using the Client-interface like this:

// This snipped is related to the ServerStart!
// It is inside of an Communication or whatever
Client client = ...
Class<?> key = ...

Awaiting awaiting = client.createNewConnection(key);
awaiting.synchronize();

To state best practices, use one of the following types:

With custom User-Objects

This is my recommandation, since you now also have a custom representation of the user.

class User {
    // Some important attributes and methods
}

public class Example {
    private Mapping<User, Client> userClientMapping = new ClientMapping<>();

    public void newConnection(User u, Class<?> key) {
        userClientMapping.get(u).ifPresent(client -> client.createNewConnection(key).synchronize());
    }
}

With the internal ClientList

Alternatively you could access the clients directly through the ServerStart.clientList();

public class Example {

    private ServerStart serverStart;
    private Class<?> key;

    // ...

    public void register() {
        serverStart.getCommunicationRegistration()
                   .register(TestObject.class)
                   .addFirst((session, testObject) -> serverStart.clientList()
                              .getClient(session)
                              .ifPresent(client -> client.createNewConnection(key)));
    }
}

Synchronization

Just like at the ClientSide, those creations are asynchronous. The method client.creatNewConnection(Class<?>); will return immediately and returns an Awaiting instance, to synchronize until the new Connection is established. You can call synchronize on it. This method call will block until the Connection is established.

Clone this wiki locally