(1.) IoService:这个接口在一个线程上负责套接字的建立,拥有自己的Selector,监听是否有连接被建立。
(2.) IoProcessor:这个接口在另一个线程上,负责检查是否有数据在通道上读写,也就是说它也拥有自己的Selector,这是与我们使用JAVA NIO 编码时的一个不同之处,通常在JAVA NIO 编码中,我们都是使用一个Selector,也就是不区分IoService与IoProcessor 两个功能接口。另外,IoProcessor 负责调用注册在IoService 上的过滤器,并在过滤器链之后调用IoHandler。
(3.) IoFilter:这个接口定义一组拦截器,这些拦截器可以包括日志输出、黑名单过滤、数据的编码(write 方向)与解码(read 方向)等功能,其中数据的encode 与decode是最为重要的、也是你在使用Mina 时最主要关注的地方。
(4.) IoHandler:这个接口负责编写业务逻辑,也就是接收、发送数据的地方。
(2.)IoAcceptor:
这个接口是TCPServer 的接口,主要增加了void bind()监听端口、void unbind()解除对套接字的监听等方法。这里与传统的JAVA 中的ServerSocket 不同的是IoAcceptor 可以多次调用bind()方法(或者在一个方法中传入多个SocketAddress 参数)同时监听多个端口。
3.)IoConnector:
这个接口是TCPClient 的接口, 主要增加了ConnectFuture connect(SocketAddressremoteAddress,SocketAddress localAddress)方法,用于与Server 端建立连接,第二个参数如果不传递则使用本地的一个随机端口访问Server 端。这个方法是异步执行的,同样的,也可以同时连接多个服务端。
(6.)IoHandler:
这个接口是你编写业务逻辑的地方,从上面的示例代码可以看出,读取数据、发送数据基本都在这个接口总完成,这个实例是绑定到IoService 上的,有且只有一个实例(没有给一个IoService 注入一个IoHandler 实例会抛出异常)。它有如下几个方法:
A. void sessionCreated(IoSession session):
这个方法当一个Session 对象被创建的时候被调用。对于TCP 连接来说,连接被接受的时候调用,但要注意此时TCP 连接并未建立,此方法仅代表字面含义,也就是连接的对象IoSession 被创建完毕的时候,回调这个方法。对于UDP 来说,当有数据包收到的时候回调这个方法,因为UDP 是无连接的。
B. void sessionOpened(IoSession session):
这个方法在连接被打开时调用,它总是在sessionCreated()方法之后被调用。对于TCP 来说,它是在连接被建立之后调用,你可以在这里执行一些认证操作、发送数据等。对于UDP 来说,这个方法与sessionCreated()没什么区别,但是紧跟其后执行。如果你每隔一段时间,发送一些数据,那么sessionCreated()方法只会在第一次调用,但是sessionOpened()方法每次都会调用。
C. void sessionClosed(IoSession session) :
对于TCP 来说,连接被关闭时,调用这个方法。对于UDP 来说,IoSession 的close()方法被调用时才会毁掉这个方法。
D. void sessionIdle(IoSession session, IdleStatus status) :
这个方法在IoSession 的通道进入空闲状态时调用,对于UDP 协议来说,这个方法始终不会被调用。
E. void exceptionCaught(IoSession session, Throwable cause) :
这个方法在你的程序、Mina 自身出现异常时回调,一般这里是关闭IoSession。
F. void messageReceived(IoSession session, Object message) :
接收到消息时调用的方法,也就是用于接收消息的方法,一般情况下,message 是一个IoBuffer 类,如果你使用了协议编解码器,那么可以强制转换为你需要的类型。通常我们都是会使用协议编解码器的, 就像上面的例子, 因为协议编解码器是
TextLineCodecFactory,所以我们可以强制转message 为String 类型。
G. void messageSent(IoSession session, Object message) :
当发送消息成功时调用这个方法,注意这里的措辞,发送成功之后,也就是说发送消息是不能用这个方法的。
发送消息的时机:
发送消息应该在sessionOpened()、messageReceived()方法中调用IoSession.write()方法完成。因为在sessionOpened()方法中,TCP 连接已经真正打开,同样的在messageReceived()方法TCP 连接也是打开状态,只不过两者的时机不同。sessionOpened()方法是在TCP 连接建立之后,接收到数据之前发送;messageReceived()方法是在接收到数据之后发送,你可以完成依据收到的内容是什么样子,决定发送什么样的数据。因为这个接口中的方法太多,因此通常使用适配器模式IoHandlerAdapter,覆盖你所感兴趣的方法即可。
5.过滤器:
前面我们看到了LoggingFilter、ProtocolCodecFilter 两个过滤器,一个负责日志输出,一个负责数据的编解码,通过最前面的Mina 执行流程图,在IoProcessor 与IoHandler 之间可以有很多的过滤器,这种设计方式为你提供可插拔似的扩展功能提供了非常便利的方式,目前的Apache CXF、Apache Struts2 中的拦截器也都是一样的设计思路。Mina 中的IoFilter 是单例的,这与CXF、Apache Struts2 没什么区别。IoService 实例上会绑定一个DefaultIoFilterChainBuilder 实例,DefaultIoFilterChainBuilder 会把使用内部的EntryImpl 类把所有的过滤器按照顺序连在一起,组成一个过滤器链。
client.java
public class Client {
public static void main(String[] args) {
//创建链接
NioSocketConnector connector=new NioSocketConnector();
DefaultIoFilterChainBuilder chain=connector.getFilterChain();
//chain.addLast("myChain",new ProtocolCodecFilter(new TextLineCodecFactory()));
chain.addLast("objectFilter",new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
//使用默认的过滤器一行一行的输出
connector.setHandler((IoHandler) new MinaClientHandler());;
//链接服务器
ConnectFuture cf=connector.connect(new InetSocketAddress("localhost",9999));
cf.awaitUninterruptibly();//等待链接成功
Scanner input=new Scanner(System.in);
while(true){
/*System.out.println("请输入:");
String info=input.nextLine();
cf.getSession().write(info);//发送消息*/
//以对象的方式传输数据
Message msg=new Message();
System.out.println("from:");
msg.setFrom(input.nextLine());
System.out.println("to:");
msg.setTo(input.nextLine());
System.out.println("info:");
msg.setInfo(input.nextLine());
msg.setType("send");
cf.getSession().write(msg);
}
//等待链接服务器关闭,结束长链接
//cf.getSession().getCloseFuture().awaitUninterruptibly();
}
}
MinaClientHandler.java
public class MinaClientHandler extends IoHandlerAdapter {//消息处理器
public void sessionOpened(IoSession session)throws Exception{
super.sessionOpened(session);
System.out.println("sessionOpened");
}
public void sessionClosed(IoSession session)throws Exception{
super.sessionClosed(session);
System.out.println("sessionClosed");
}
public void messageReceived(IoSession session,Object message)throws Exception{
super.messageReceived(session, message);
//String msg=(String)message;
Message msg=(Message)message;
System.out.println(msg);
}
public static void main(String[] args) {
}
}
Server.java
public class Server {
public static void main(String[] args) {
//创建一个非阻塞的server端socket NIO
SocketAcceptor acceptor=new NioSocketAcceptor();
DefaultIoFilterChainBuilder chain=acceptor.getFilterChain();
//chain.addLast("myChain", new ProtocolCodecFilter(new TextLineCodecFactory()));
//设定过滤器以对想为单位读取数据
chain.addLast("objectFilter",new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
//设置一个过滤器一行一行的读取
//设置服务器端的消息处理器
acceptor.setHandler(new MinaServerHandler());
int port=9999;
try{
acceptor.bind(new InetSocketAddress(port));//绑定端口服务器启动立即返回
}catch(IOException e){
e.printStackTrace();
}
System.out.println("mina server running...,listening on"+port);
}
}
MinaServerHandler.java
public class MinaServerHandler extends IoHandlerAdapter {
//服务器端的消息处理器
public static void main(String[] args) {
}
public void sessionClosed(IoSession session) throws Exception {
super.sessionClosed(session);
System.out.println("client closed");
}
public void sessionOpened(IoSession session) throws Exception {
super.sessionOpened(session);
System.out.println("client"+session.getRemoteAddress());//获取远程的地址
}
public void messageReceived(IoSession session,Object message)throws Exception{
super.messageReceived(session, message);
//String msg=(String)message;//接收到的消息对象
Message msg=(Message)message;
System.out.println("收到客户端发来的消息"+msg);
msg.setInfo("吃好吃的");
session.write("echo:"+msg);//想客户端发送消息对象
}
}
Message.java
public class Message implements Serializable{
private String from;
@Override
public String toString() {
return "Message [from=" + from + ", to=" + to + ", type=" + type + ", info=" + info + "]";
}
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
private String to;
private String type;
private String info;
public static void main(String[] args) {
}
}