Mina.NET Basics

In this article we shall have a look at Client/Server Architecture and details on working out a Mina.NET based Server and Client. We will also expose some very simple Servers and Clients, based on TCP and UDP.

Application Architecture

The following image shows a bit more the internal of Mina.NET, and what are each of the Mina.NET components doing:

(The image is from Apache MINA)

Broadly, Mina.NET based applications are divided into 3 layers:
  • I/O Service - Performs actual I/O
  • I/O Filter Chain - Filters/Transforms bytes into desired Data Structures and vice-versa
  • I/O Handler - Here resides the actual business logic

So, in order to create a Mina.NET based Application, you need:
  • Create an I/O service - Choose from already available Services (*Acceptor) or create your own
  • Create a Filter Chain - Choose from already existing Filters or create a custom Filter for transforming request/response
  • Create an I/O Handler - Write business logic, on handling different messages

Sample TCP Server

We shall use TimeServer as a reference implementation.
To construct a Server, we need to do following:
  1. Create a Acceptor
  2. Create a Filter Chain
  3. Create a IOHandler and add to Acceptor
  4. Bind to an end point

Create a Acceptor

IoAcceptor acceptor = new AsyncSocketAcceptor();

Here we have created a async socket acceptor.

Create a Filter Chain

acceptor.FilterChain.AddLast("logger", new LoggingFilter());
acceptor.FilterChain.AddLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Encoding.UTF8)));

Here we have added a LoggingFilter and a ProtocolCodecFilter. This LoggingFilter will log all information such as newly created sessions, messages received, messages sent, session closed. The next ProtocolCodecFilter will translate binary or protocol specific data into message object and vice versa. We use an existing TextLine factory because it will handle text base message for you (you don't have to write the codec part).

Create a IOHandler and add to Acceptor

acceptor.MessageReceived += (s, e) =>
{
	String str = e.Message.ToString();

	// "Quit" ? let's get out ...
	if (str.Trim().Equals("quit", StringComparison.OrdinalIgnoreCase))
	{
		e.Session.Close(true);
		return;
	}

	// Send the current date back to the client
	e.Session.Write(DateTime.Now.ToString());
	Console.WriteLine("Message written...");
};

Bind to an end point

Int32 port = 9123;
acceptor.Bind(new IPEndPoint(IPAddress.Any, port));

Sample TCP Client

We shall use Sumup Client as a reference implementation.
To construct a Client, we need to do following:
  • Create a Connector
  • Create a Filter Chain
  • Create a IOHandler and add to Connector
  • Connect to Server

Create a Connector

IoConnector connector = new AsyncSocketConnector();

Here we have created a async socket connector.

Create a Filter Chain

if (USE_CUSTOM_CODEC)
{
	connector.FilterChain.AddLast("codec",
			new ProtocolCodecFilter(new SumUpProtocolCodecFactory(false)));
}
else
{
	connector.FilterChain.AddLast("codec",
			new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
}

Here we have added a ProtocolCodec, to the filter Chain.

Create a IOHandler and add to Connector

connector.SessionOpened += (s, e) =>
{
	for (int i = 0; i < values.Length; i++)
	{
		AddMessage m = new AddMessage();
		m.Sequence = i;
		m.Value = values[i];
		e.Session.Write(m);
	}
};

connector.MessageReceived += (s, e) =>
{
	ResultMessage rm = (ResultMessage)e.Message;
	if (rm.OK)
	{
		if (rm.Sequence == values.Length - 1)
		{
			Console.WriteLine("The sum: " + rm.Value);
			e.Session.Close(true);
		}
	}
	else
	{
		Console.WriteLine("Server error, disconnecting...");
		e.Session.Close(true);
	}
};

Connect to Server

IoSession session;
while (true)
{
	try
	{
		IConnectFuture future = connector.Connect(new IPEndPoint(IPAddress.Loopback, PORT));
		future.Await();
		session = future.Session;
		break;
	}
	catch (Exception ex)
	{
		Console.WriteLine(ex);
		Thread.Sleep(3000);
	}
}

Here is the most important stuff. We connect to remote Server. Since, connect is an async task, we use the IConnectFuture interface to know the when the connection is complete. Once the connection is complete, we get the associated IoSession. To send any message to the Server, we shall have to write to the session. All responses/messages from server shall traverse the Filter chain and finally be handled in IoHandler.

Sample UDP Server

We will begin by looking at the code found in the Udp example. To keep life simple, we shall concentrate on Mina.NET related constructs only.

To construct the server, we shall have to do the following: (see MemoryMonitor.cs)
  1. Create a Datagram Socket to listen for incoming Client requests
  2. Create an IoHandler to handle the Mina.NET framework generated events

Here is the first snippet that addresses Point #1, we create a AsyncDatagramAcceptor to listen for incoming Client requests:

AsyncDatagramAcceptor acceptor = new AsyncDatagramAcceptor();

The next step is to add a logging filter to the filter chain that this AsyncDatagramAcceptor will use. LoggingFilter is a very nice way to see Mina.NET in Action. It generate log statements at various stages, providing an insight into how Mina.NET works.

acceptor.FilterChain.AddLast("logger", new LoggingFilter());

Next we get into some more specific code for the UDP traffic. We will set the acceptor to reuse the address.

acceptor.SessionConfig.ReuseAddress = true;

Of course the last thing that is required here is to call Bind().

int port = 18567;
acceptor.Bind(new IPEndPoint(IPAddress.Any, port));

IoHandler implementation

There are three major events of interest for our Server Implementation:
  • Session Created
  • Message Received
  • Session Closed

Lets look at each of them in detail.

Session Created Event

acceptor.SessionCreated += (s, e) =>
{
	Console.WriteLine("Session created...");
};

Message Receivedd Event

acceptor.MessageReceived += (s, e) =>
{
	IoBuffer buf = e.Message as IoBuffer;
	if (buf != null)
	{
		Console.WriteLine("New value for {0}: {1}", e.Session.RemoteEndPoint, buf.GetInt64());
	}
};

In the message received event, we just dump the data received in the message. Applications that need to send responses, can process message and write the responses onto session in this function.

Session Closed Event

acceptor.SessionClosed += (s, e) =>
{
	Console.WriteLine("Session closed...");
};

Sample UDP Client

Lets look at the client code for the UDP Server from previous section.

To implement the Client we need to do following:
  • Create Socket and Connect to Server
  • Set the IoHandler
  • Get the total memory
  • Send the Data to the Server

We will begin by looking at the file MemMonClient.cs. The first few lines of the code are simple and straightforward.

IoConnector connector = new AsyncDatagramConnector();
IConnectFuture connFuture = connector.Connect(new IPEndPoint(IPAddress.Loopback, MemoryMonitor.port));

Here we create a AsyncDatagramConnector. Next we will wait for acknowledgment that the client has connected to the server. Once we know we are connected, we can start writing data to the server. Here is that code:

connFuture.Complete += (s, e) =>
{
	IConnectFuture f = (IConnectFuture)e.Future;
	if (f.Connected)
	{
		Console.WriteLine("...connected");
		IoSession session = f.Session;

		for (int i = 0; i < 30; i++)
		{
			Int64 memory = GC.GetTotalMemory(false);
			IoBuffer buffer = IoBuffer.Allocate(8);
			buffer.PutInt64(memory);
			buffer.Flip();
			session.Write(buffer);

			try
			{
				Thread.Sleep(1000);
			}
			catch (ThreadInterruptedException)
			{
				break;
			}
		}
	}
	else
	{
		Console.WriteLine("Not connected...exiting");
	}
};

Here we add an event handler to the Complete event of the IConnectFuture object and when we receive a notification that the client has connected, we will start to write data.

We will write the amount of total memory to the server once a second for 30 seconds. Here you can see that we allocate a IoBuffer large enough to hold a long variable and then place the amount of free memory in the buffer. This buffer is then flipped and written to the server.

Congratulation! Our UDP Client implementation is complete.

Last edited Apr 10, 2014 at 5:34 AM by longshine, version 1