Quick Start Guide

中文

This tutorial will walk you through the process of building a Mina.NET based program by building a time server. The following prerequisites are required for this tutorial:

Writing the Mina.NET time server

We will begin by creating a file called MinaTimeServer.cs. The initial code can be found below:

class MinaTimeServer
{
  static void Main(string[] args)
  {
    // code will go here next
  }
}

This code should be straightforward to all. We are simply defining a main method that will be used to kick off the program. At this point, we will begin to add the code that will make up our server. First off, we need an object that will be used to listen for incoming connections. Since this program will be TCP/IP based, we will add a SocketAcceptor to our program.

using Mina.Core.Service;
using Mina.Transport.Socket;

class MinaTimeServer
{
  static void Main(string[] args)
  {
    IoAcceptor acceptor = new AsyncSocketAcceptor();
  }
}

Next we add a filter to the configuration. This filter will log all information such as newly created sessions, messages received, messages sent, session closed. The next filter is a ProtocolCodecFilter. This filter 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)

using System.Text;
using Mina.Core.Service;
using Mina.Filter.Codec;
using Mina.Filter.Codec.TextLine;
using Mina.Filter.Logging;
using Mina.Transport.Socket;

class MinaTimeServer
{
  static void Main(string[] args)
  {
    IoAcceptor acceptor = new AsyncSocketAcceptor();
    acceptor.FilterChain.AddLast("logger", new LoggingFilter());
    acceptor.FilterChain.AddLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Encoding.UTF8)));
  }
}

At this point, we will define the handler that will be used to service client connections and the requests for the current time. The handler can be defined in two different ways. For almost all programs that use Mina.NET, this becomes the workhorse of the program, as it services all incoming requests from the clients. We will cover the other way later.

using System;
using System.Text;
using Mina.Core.Service;
using Mina.Filter.Codec;
using Mina.Filter.Codec.TextLine;
using Mina.Filter.Logging;
using Mina.Transport.Socket;

class MinaTimeServer
{
  static void Main(string[] args)
  {
    IoAcceptor acceptor = new AsyncSocketAcceptor();
    acceptor.FilterChain.AddLast("logger", new LoggingFilter());
    acceptor.FilterChain.AddLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Encoding.UTF8)));

    acceptor.ExceptionCaught += (s, e) => Console.WriteLine(e.Exception);
    acceptor.SessionIdle += (s, e) => Console.WriteLine("IDLE " + e.Session.GetIdleCount(e.IdleStatus));
    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...");
    };
  }
}

We define 3 event handlers for event ExceptionCaught, SessionIdle and MessageReceived. ExceptionCaught should always be defined in a handler to process and exceptions that are raised in the normal course of handling remote connections. If this method is not defined, exceptions may not get properly reported.

The ExceptionCaught method will simply print the detail of the error and close the session. For most programs, this will be standard practice unless the handler can recover from the exception condition.

The MessageReceived method will receive the data from the client and write back to the client the current time. If the message received from the client is the word "quit", then the session will be closed. This method will also print out the current time to the client. Depending on the protocol codec that you use, the object (contained in event parameter) that gets passed in to this method will be different, as well as the object that you pass in to the session.Write(Object) method. If you do not specify a protocol codec, you will most likely receive a IoBuffer object, and be required to write out a IoBuffer object.

The SessionIdle method will be called once a session has remained idle for the amount of time.

We will now add in the AsyncSocketAcceptor configuration. This will allow us to make socket-specific settings for the socket that will be used to accept connections from clients.

using System;
using System.Text;
using Mina.Core.Service;
using Mina.Filter.Codec;
using Mina.Filter.Codec.TextLine;
using Mina.Filter.Logging;
using Mina.Transport.Socket;

class MinaTimeServer
{
  static void Main(string[] args)
  {
    IoAcceptor acceptor = new AsyncSocketAcceptor();
    acceptor.FilterChain.AddLast("logger", new LoggingFilter());
    acceptor.FilterChain.AddLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Encoding.UTF8)));

    acceptor.ExceptionCaught += (s, e) => Console.WriteLine(e.Exception);
    acceptor.SessionIdle += (s, e) => Console.WriteLine("IDLE " + e.Session.GetIdleCount(e.IdleStatus));
    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...");
    };

    acceptor.SessionConfig.ReadBufferSize = 2048;
    acceptor.SessionConfig.SetIdleTime(IdleStatus.BothIdle, 10);
  }
}

There are 2 new lines in the MinaTimeServer class. These methods set the input buffer size and the idle property for the sessions. The buffer size will be specified in order to tell the underlying operating system how much room to allocate for incoming data. The second line will specify when to check for idle sessions. In the call to SetIdleTime, the first parameter defines what actions to check for when determining if a session is idle, the second parameter defines the length of time in seconds that must occur before a session is deemed to be idle.

All that is left to do is define the end point that the server will listen on, and actually make the call that will start the server. That code is shown below:

using System;
using System.Net;
using System.Text;
using Mina.Core.Service;
using Mina.Core.Session;
using Mina.Filter.Codec;
using Mina.Filter.Codec.TextLine;
using Mina.Filter.Logging;
using Mina.Transport.Socket;

class MinaTimeServer
{
  private static readonly Int32 port = 9123;

  static void Main(string[] args)
  {
    IoAcceptor acceptor = new AsyncSocketAcceptor();
    acceptor.FilterChain.AddLast("logger", new LoggingFilter());
    acceptor.FilterChain.AddLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Encoding.UTF8)));

    acceptor.ExceptionCaught += (s, e) => Console.WriteLine(e.Exception);
    acceptor.SessionIdle += (s, e) => Console.WriteLine("IDLE " + e.Session.GetIdleCount(e.IdleStatus));
    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...");
    };

    acceptor.SessionConfig.ReadBufferSize = 2048;
    acceptor.SessionConfig.SetIdleTime(IdleStatus.BothIdle, 10);

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

    Console.ReadLine();
  }
}

Configure logging

In order to view logs from LoggingFilter, we need to configure Common.Logging in app.config like this:

<?xml version="1.0"?>
<configuration>
  <configSections>
    <sectionGroup name="common">
      <section name="logging" type="Common.Logging.ConfigurationSectionHandler, Common.Logging"/>
    </sectionGroup>
  </configSections>
  <common>
    <logging>
      <factoryAdapter type="Common.Logging.Simple.ConsoleOutLoggerFactoryAdapter, Common.Logging">
        <arg key="level" value="DEBUG"/>
        <arg key="showLogName" value="false"/>
        <arg key="showDateTime" value="false"/>
        <arg key="dateTimeFormat" value="yyyy/MM/dd HH:mm:ss:fff"/>
      </factoryAdapter>
    </logging>
  </common>
</configuration>

Try out the Time server

At this point, we can go ahead and compile the program. Once you have compiled the program you can run the program in order to test out what happens. The easiest way to test the program is to start the program, and then telnet in to the program:

Client Output

> telnet 127.0.0.1 9123
hello
4/9/2014 23:42:55
quit
Connection to host lost.
>


Server Output

[INFO]  CREATED
[INFO]  OPENED
[INFO]  RECEIVED: Mina.Core.Buffer.ByteBuffer
Message written...
[INFO]  SENT: Mina.Core.Buffer.ByteBuffer
[INFO]  RECEIVED: Mina.Core.Buffer.ByteBuffer
[INFO]  CLOSED

Handler events vs IoHandler class

The other way other than event handlers of IoAcceptor is handler class that implement the interface IoHandler. For this tutorial, we will extend the class IoHandlerAdapter. This is a class that simplifies the amount of code that needs to be written in order to satisfy the requirement of passing in a class that implements the IoHandler interface.

NOTE that the handler class and handler events can NOT be used at the same time. Once a handler class is specified, handler events will NOT be fired.

In the main class, we replace the event handlers with the following line:

acceptor.Handler = new TimeServerHandler();

The code for the handler is shown below:

using System;
using Mina.Core.Service;
using Mina.Core.Session;

class TimeServerHandler : IoHandlerAdapter
{
  public override void ExceptionCaught(IoSession session, Exception cause)
  {
    Console.WriteLine(cause);
  }

  public override void MessageReceived(IoSession session, Object message)
  {
    String str = message.ToString();

    if (str.Trim().Equals("quit", StringComparison.OrdinalIgnoreCase))
    {
        session.Close(true);
        return;
    }

    session.Write(DateTime.Now.ToString());
    Console.WriteLine("Message written...");
  }

  public override void SessionIdle(IoSession session, IdleStatus status)
  {
    Console.WriteLine("IDLE " + session.GetIdleCount(status));
  }
}

What's Next?

Please visit User Guide page to find out more resources.

Last edited Apr 7, 2014 at 8:21 AM by longshine, version 4