快速开始

English

这篇教程将帮助你一步一步使用Mina.NET搭建一个时间服务器。开始之前,你需要准备以下条件:

编写Mina.NET时间服务器

首先,我们创建一个名为MinaTimeServer.cs的源文件,初始代码如下所示:

class MinaTimeServer
{
  static void Main(string[] args)
  {
    // 代码实现
  }
}

我们只是简单地定义了一个Main函数作为程序的入口,接下来要逐步添加构建服务的代码。我们需要一个对象来监听所有的连接,这个例程将使用TCP/IP,因此我们添加一个SocketAcceptor到程序里。

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

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

接下来,我们要向程序中添加过滤器。第一个过滤器是日志记录器,它将记录所有的事件,包括会话建立、消息接收、消息发送和会话关闭等。第二个过滤器是ProtocolCodecFilter,它能够在二进制数据和指定协议之间进行编解码。在这里我们使用一个现有的命令行协议(TextLineCodecFactory),它能够处理文本消息,我们不必重新写一个编解码器。

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)));
  }
}

下一步,我们将要编写处理连接和请求的代码。对于大多数使用Mina.NET的程序,这部分代码是程序的核心部分,因为它要处理所有的连接和数据收发过程。处理程序有两种编写方式,我们先使用事件,之后再介绍另一种方式。

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...");
    };
  }
}

我们定义了3个事件处理函数,分别处理ExceptionCaughtSessionIdleMessageReceived事件。在与远程连接的交互过程中发生异常时,ExceptionCaught事件将被触发,通常需要处理这个事件,否则将不能及时对异常进行处理。

在这个例子中,我们只是简单地在控制台打印出异常信息。对于大多数程序,通常的做法是结束会话,除非发生的异常是可以恢复的。

MessageReceived事件中将接收客户端发来的数据,并将当前时间发送给客户端,接收到“quit”消息时将结束当前会话。根据使用的编解码过滤器,事件数据e.Message将会不同,发送数据时调用session.Write(Object)传入的数据也将不同。这个例子使用的是命令行协议,因此接收和发送的数据都是字符串。如果不指定编解码过滤器,那么接收和发送的数据将会是IoBuffer对象。

SessionIdle事件将会在会话空闲一段特定的时候时触发。

接下来我们添加一些AsyncSocketAcceptor配置,以对底层socket进行设置。

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);
  }
}

例程中添加了两行代码,分别设置了接收缓冲区大小和会话空闲时间。接收缓冲区大小用于告诉底层系统如何分配缓冲区;会话空闲时间则指定的是检查空闲会话的时间间隔。在SetIdleTime方法中,第一个参数定义了如何判定会话是否空闲,第二个参数指定了经过多长时间(秒)后会话将被判定为空闲。

最后一步,我们将调用Bind使服务程序绑定并监听一个端口,调用这个方法的同时也将启动服务程序,代码如下:

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();
  }
}

配置日志

为了查看LoggingFilter记录的日志,我们需要在app.config中对Common.Logging进行一下配置:

<?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>

体验时间服务器

祝贺你!现在我们可以编译并运行程序了。最简单的测试方式是使用telnet连接到服务程序:

客户端输出

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


服务端输出

[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

IoAcceptor事件 vs IoHandler类

处理程序的另一种方式是使用实现IoHandler接口的处理类。在这个例子中,我们将定义一个TimeServerHandler类,并继承自IoHandlerAdapterIoHandlerAdapter类可以用于简化开发,只需要重写需要处理的事件即可,而不用实现IoHandler接口中定义的全部方法。

需要注意的是,IoAcceptor事件与IoHandler类不能同时使用,当指定了IoHandler时,IoAcceptor事件将不会被触发。

在主程序中,我们将事件处理函数替换成以下这行代码:

acceptor.Handler = new TimeServerHandler();

TimeServerHandler的定义如下:

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));
  }
}

接下来?

请访问用户手册查看更多资源。

Last edited Apr 7, 2014 at 9:22 AM by longshine, version 3