mina.net docs 保留
Mina.NET 基础
这篇文章将介绍Mina.NET的基础架构,以及如何使用Mina.NET构建服务端与客户端应用程序,并且给出几个TCP、UDP服务端和客户端的简单示例。应用程序架构
下图描述了Mina.NET的内部结构,以及各部分组件的任务:
(图片来自Apache MINA)
一般情况下,Mina.NET应用程序可以划分为三层:
- I/O 服务 - 真实 I/O 发生的地方
- I/O 过滤器链 - 在字节数据和指定数据结构之间进行处理或转换
- I/O Handler - 业务逻辑层
因此,创建Mina.NET应用程序通常需要:
- 创建 I/O 服务 - 选择一个合适的已有服务,或者自定义新的服务
- 创建过滤器链 - 选择已有的过滤器,或者新建自定义过滤器
- 创建 I/O Handler - 处理消息的业务逻辑
TCP服务端示例
以TimeServer为例。创建服务器需要以下几步:- 创建一个Acceptor
- 创建过滤器链
- 创建IOHandler,添加至Acceptor
- 侦听端口
创建Acceptor
IoAcceptor acceptor = new AsyncSocketAcceptor();
这里我们创建了一个异步套接字服务(AsyncSocketAcceptor)。
创建过滤器链
acceptor.FilterChain.AddLast("logger", new LoggingFilter());
acceptor.FilterChain.AddLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Encoding.UTF8)));
我们添加了一个LoggingFilter和一个ProtocolCodecFilter。LoggingFilter将记录所有的事件,包括会话建立、消息接收、消息发送和会话关闭等。第二个ProtocolCodecFilter能够在二进制数据和指定协议之间进行编解码。在这里我们使用一个现有的命令行协议(TextLineCodecFactory),它能够处理文本消息,我们不必重新写一个编解码器。
创建IOHandler,添加至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...");
};
侦听端口
Int32 port = 9123;
acceptor.Bind(new IPEndPoint(IPAddress.Any, port));
TCP客户端示例
以Sumup客户端为例。建立客户端需要以下几步:- 创建一个Connector
- 创建过滤器链
- 创建IOHandler,添加至Connector
- 连接服务器
创建Connector
IoConnector connector = new AsyncSocketConnector();
首先我们创建一个异步套接字连接(AsyncSocketConnector)。
创建过滤器链
if (USE_CUSTOM_CODEC)
{
connector.FilterChain.AddLast("codec",
new ProtocolCodecFilter(new SumUpProtocolCodecFactory(false)));
}
else
{
connector.FilterChain.AddLast("codec",
new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
}
创建IOHandler,添加至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);
}
};
连接服务器
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);
}
}
这是客户中最重要的部分。我们向远程服务端发起连接,由于连接是一个异步操作,我们需要使用IConnectFuture接口来了解连接操作什么时候完成。当连接完成后,我们获取关联的会话IoSession,通过这个会话,我们可以向服务端发送数据。
UDP服务端示例
以Udp示例为参考。建立服务端,我们需要完成以下两部分工作:(MemoryMonitor.cs)- 创建UDP套接字接收客户端请求
- 创建IoHandler处理Mina.NET事件
首先,我们创建一个AsyncDatagramAcceptor来侦听客户端请求:
AsyncDatagramAcceptor acceptor = new AsyncDatagramAcceptor();
接下来,向AsyncDatagramAcceptor的过滤器链添加一个LoggingFilter。LoggingFilter能够在Mina.NET的各个步骤生成日志信息,能够帮助我们很好地了解Mina.NET是如何工作的。
acceptor.FilterChain.AddLast("logger", new LoggingFilter());
接下来我们添加一些控制底层UDP套接字的代码,设置acceptor为重用地址。
acceptor.SessionConfig.ReuseAddress = true;
当然,最后我们需要调用Bind()。
int port = 18567;
acceptor.Bind(new IPEndPoint(IPAddress.Any, port));
实现IoHandler
在这个示例中,我们主要需要处理以下3个事件:- 会话创建(Session Created)
- 消息接收(Message Received)
- 会话关闭(Session Closed)
会话创建事件
acceptor.SessionCreated += (s, e) =>
{
Console.WriteLine("Session created...");
};
消息接收事件
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());
}
};
在消息接收事件中,我们只是打印出接收到的数据。如果需要发送响应消息,可以在这里编写处理代码。
会话关闭事件
acceptor.SessionClosed += (s, e) =>
{
Console.WriteLine("Session closed...");
};
UDP客户端示例
这一部分是对应于上一部分UDP服务端的客户端代码。实现客户端,我们需要以下几步:- 创建套接字连接到服务端
- 设置IoHandler
- 获取内存数
- 发送数据至服务端
代码文件请查看MemMonClient.cs.
IoConnector connector = new AsyncDatagramConnector();
IConnectFuture connFuture = connector.Connect(new IPEndPoint(IPAddress.Loopback, MemoryMonitor.port));
我们创建了一个AsyncDatagramConnector。接下来,等待客户端与服务端连接,连接成功后就可以开始发送数据了。
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");
}
};
这里,我们侦听了IConnectFuture对象的Complete事件,当连接完成,事件被触发时,程序将开始发送数据。
程序将在30秒内,每隔一秒向服务端发送一次内存数据。我们每次分配一个8字节的IoBuffer,正好放下一个长整型变量。在调用Write方法之前,我们需要调用buffer.Flip()。
Last edited Apr 10, 2014 at 2:16 PM by longshine, version 1

浙公网安备 33010602011771号