这是使用Mina2编写的服务端主类MyServer.java
package com.mina.server;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.LineDelimiter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
/**
* 简单Mina Server示例
* @see 非阻塞I/O是JDK5.0提供的API,意思是服务器不用像以前那样调用accept()方法,阻塞等待了
* @see 开发一个Mina应用,简单的说:就是创建连结、设定过滤规则、编写自己的消息处理器这三步
* @see Mina执行流程:进入IoService-->IoProcessor-->IoFilter-->IoHandler-->IoFilter-->IoProcessor-->IoService
*/
public class MyServer {
public static void main(String[] args) throws IOException {
//指定服务器端所绑定的端口
int bindPort = 9876;
//初始化服务端的TCP/IP的基于NIO的套接字
//即创建非阻塞服务器端,类似于Java中的ServerSocket
IoAcceptor acceptor = new NioSocketAcceptor();
//调用IoSessionConfig设置读取数据的缓冲区大小、读写通道均在10秒内无任何操作就进入空闲状态
acceptor.getSessionConfig().setReadBufferSize(2048);
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
/**
* 定义拦截器:可以包括日志输出、黑名单过滤、数据的编码(write方向)与解码(read方向)等功能
* 其中数据的encode与decode是最为重要的,也是在使用Mina时最主要关注的地方
*/
//启用Mina的日志跟踪
acceptor.getFilterChain().addLast("logger", new LoggingFilter());
//这段代码要在acceptor.bind()方法之前前执行,因为绑定套接字之后,就不能再做这些准备工作了
//这里所要传输的是以换行符为标识的数据,所以使用了Mina自带的换行符编解码器工厂
//若不清楚操作系统或Telnet软件的换行符是什么,可以删掉new TextLineCodecFactory(*,*,*)的后两个参数
//即new TextLineCodecFactory(Charset.forName("UTF-8")),此时使用的就是TextLineCodec内部的自动识别机制
//acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"))));
acceptor.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new TextLineCodecFactory(
Charset.forName("UTF-8"), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue())));
/**
* 指定服务器端的消息处理器。它负责编写业务逻辑,即接收、发送数据的地方
*/
//把编写好的IoHandler注册到IoService。它也要在acceptor.bind()方法之前前执行
acceptor.setHandler(new ServerHandler());
//绑定端口,启动服务器
//该接口中的void bind()方法用于监听端口、void unbind()方法用于解除对套接字的监听
//这里与传统的Java中的ServerSocket不同的是:IoAcceptor可以多次调用bind()方法同时监听多个端口
//或者在一个方法中传入多个SocketAddress参数,来监听多个端口
acceptor.bind(new InetSocketAddress(bindPort));
System.out.println("MinaServer is startup, and it`s listing on := " + bindPort);
}
}
这是我们编写的服务端消息处理器ServerHandler.java
package com.mina.server;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
/**
* 自定义的消息处理器,必须实现IoHandlerAdapter类
* @see IoHandlerAdapter:它定义的方法用于处理程序接收到的消息,并处理通信中的连结,断开,消息到达等事件
* @see 客户机和服务器端创建后,都有一个setHandler方法,就是要传入我们重写的该类的对象
* @see 其中各个方法在通信中会根据情况自动调用,类似于Swing事件中的调用机制
*/
public class ServerHandler extends IoHandlerAdapter {
//这是IoHandlerAdapter类中最重要的一个方法。IoSession代表与对方机器的TCP/IP连接,Object代表接收到的数据
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
String str = message.toString(); //我们已设定了服务器解析消息的规则是一行一行读取,这里就可转为String
System.out.println("The message received from Client is [" + str + "]");
}
@Override
public void sessionOpened(IoSession session) throws Exception{
System.out.println("InComing Client:" + session.getRemoteAddress());
}
}
package com.mina.client;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.LineDelimiter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
/**
* 简单的TCPClient
* @see Mina中的Server端和Client端的执行流程是一样的,唯一不同的是IoService的Client端实现是IoConnector
* @see 这里我们实现Mina中的TCPClient。运行MyClient时,会发现MyServer控制台输入如下语句
* @see The message received from Client is [岂曰无衣..]
* @see The message received from Client is [月照沟渠....]
* @see 说明服务器端收到的是两条消息,因为我们所用的编解码器是以换行符判断数据是否读取完毕的
*/
public class MyClient {
public static void main(String[] args) {
//Create TCP/IP connector
//NioSocketConnector功能类似于JDK中的Socket类,它也是非阻塞的读取数据
IoConnector connector = new NioSocketConnector();
connector.setConnectTimeoutMillis(3000);
connector.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new TextLineCodecFactory(
Charset.forName("UTF-8"), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue())));
//注册IoHandler,即指定客户器端的消息处理器
connector.setHandler(new ClientHandler("岂曰无衣..\r\n月照沟渠...."));
//连接到服务器
//ConnectFuture connect(SocketAddress arg0,SocketAddress arg1)
//该方法用于与Server端建立连接,第二个参数若不传递则使用本地的一个随机端口访问Server端
//该方法是异步执行的,且可以同时连接多个服务端
connector.connect(new InetSocketAddress("127.0.0.1", 9876));
System.out.println("Mina Client is startup");
}
}
package com.mina.client;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
public class ClientHandler extends IoHandlerAdapter {
private final String values;
public ClientHandler(String values){
this.values = values;
}
@Override
public void sessionCreated(IoSession session) throws Exception {
System.out.println("sessionCreated is invoked....");
}
/**
* 发送消息
* @see =================================================================================================
* @see 客户端连接有两个事件:sessionCreated和sessionOpened
* @see sessionCreated是由IoProcessor线程触发的,sessionOpened跟在其后,是由业务线程触发的
* @see 由于Mina中的IoProcessor线程非常少,因此sessionCreated通常用于处理耗时短的操作
* @see 而将业务初始化等功能放在sessionOpened事件中,比如发送消息
* @see =================================================================================================
* @see 我们可以在sessionOpened()、messageReceived()中使用IoSession.write()方法发送消息
* @see 因为在这两个方法中,TCP连接都是打开的状态,只不过发送的时机不同
* @see sessionOpened()是在TCP连接建立之后,接收到数据之前发送
* @see messageReceived()是在接收到数据之后发送
* @see =================================================================================================
*/
@Override
public void sessionOpened(IoSession session) throws Exception {
session.write(values); //写数据,该操作是异步的
}
/**
* 关于TCP连接的关闭
* @see 无论在客户端还是服务端,IoSession都用于表示底层的一个TCP连接
* @see 那么你会发现无论是Server端还是Client端的IoSession调用close()后,TCP连接虽然显示关闭,但主线程仍在运行,即JVM并未退出
* @see 这是因为IoSession的close()仅仅是关闭了TCP的连接通道,并没有关闭Server端和Client端的程序
* @see 此时需要调用IoService.dispose()停止Server端和Client端
*/
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
System.out.println("与" + session.getRemoteAddress() + "通信过程中出现错误:[" + cause.getMessage() + "]..连接即将关闭....");
//关闭IoSession,该操作也是异步的....true表示立即关闭,false表示所有写操作都flush后关闭
session.close(false);
//IoSession.IoService getService()用于返回与当前会话对象关联的IoService实例
session.getService().dispose();
}
}