Mina框架官网为:http://mina.apache.org/,关于介绍,这里只简单说两句,更多的请自行Google
首先,开发一个Mina应用,整体来讲就是:创建连结、设定过滤规则、编写消息处理器这三步
其次,Mina执行流程大致为:IoService-->IoProcessor-->IoFilter-->IoHandler-->IoFilter-->IoProcessor-->IoService
最后,说下消息处理器
无论使用Mina编写服务端还是客户端,都要自定义消息处理器,简而言之就是帮你处理通信消息的东西
比如下面演示代码中的ServerHandler.java
和ClientHandler.java
自定义消息处理器,通常会继承IoHandlerAdapter.java
,通信过程中,会根据情况自动调用里面定义的方法
它一般用于处理程序接收到的消息,以及通信中的连结、断开、消息到达等事件,类似于Swing事件中的调用机制
ok,let`s drink code …
公共的实体类
这是客户端和服务端通信时,传输信息的实体类UserInfo.java
package com.xuanyuv.demo.mina.model;
import java.io.Serializable;
/**
* Mina传输的实体类,要求其实现Serializable接口
* Created by 玄玉<https://www.xuanyuv.com/> on 2012/06/14 14:16.
*/
public class UserInfo implements Serializable {
private static final long serialVersionUID = 5964298293666394269L;
private String name;
private int age;
/*-- 两个属性的setter和getter略 --*/
public UserInfo(){}
public UserInfo(String name, int age) {
this.name = name;
this.age = age;
}
}
服务端
下面是服务端启动类MyServer.java
package com.xuanyuv.demo.mina.server;
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.serialization.ObjectSerializationCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import java.io.IOException;
import java.net.InetSocketAddress;
/**
* 服务端示例
* Created by 玄玉<https://www.xuanyuv.com/> on 2012/06/14 14:06.
*/
public class MyServer {
public static void main(String[] args) throws IOException {
//服务器端绑定的端口
int bindPort = 9876;
//初始化服务端TCP/IP的基于NIO的套接字,即创建非阻塞服务器端,类似于java.net.ServerSocket
IoAcceptor acceptor = new NioSocketAcceptor();
//调用IoSessionConfig设置读取数据的缓冲区大小、读写通道均在10秒内无任何操作就进入空闲状态
acceptor.getSessionConfig().setReadBufferSize(2048);
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
//启用Mina的日志跟踪
acceptor.getFilterChain().addLast("logger", new LoggingFilter());
//这段代码要在acceptor.bind()方法前执行,因为绑定套接字之后,就不能再做这些准备工作了
//设定服务器解析消息的规则是以Object对象为单位进行传输,注意此时该对象需实现Serializable接口
acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
//若传输的是以换行符为标识的数据,则可适当考虑Mina自带的TextLineCodecFactory换行符编解码器工厂
//若不清楚操作系统或Telnet软件的换行符是什么,可以删掉TextLineCodecFactory()后面两个参数
//即TextLineCodecFactory(Charset.forName("UTF-8")),此时用的就是TextLineCodec内部自动识别机制
//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());
//绑定端口,启动服务器
//这里与java.net.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.xuanyuv.demo.mina.server;
import com.xuanyuv.demo.mina.model.UserInfo;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
/**
* 服务端的自定义消息处理器
* Created by 玄玉<https://www.xuanyuv.com/> on 2012/06/14 14:06.
*/
class ServerHandler extends IoHandlerAdapter {
//这是IoHandlerAdapter类中最重要的一个方法
//IoSession代表与对方机器的TCP/IP连接,Object代表接收到的数据
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
//如果服务端使用的是TextLineCodecFactory(),也就是说设定了解析消息的规则为一行一行读取
//那么这里就可以写为String str = message.toString();
//不过,我们已经设定了服务器解析消息的规则:以Java实体对象为单位进行传输
UserInfo userInfo = (UserInfo)message;
System.out.println("收到客户机的信息-->" + ReflectionToStringBuilder.toString(userInfo));
session.write(new UserInfo(userInfo.getName()+"==>>是个神秘的人", 22));
}
@Override
public void sessionOpened(IoSession session) throws Exception{
System.out.println("InComing Client:" + session.getRemoteAddress());
}
}
客户端
下面是客户端启动类MyClient.java
package com.xuanyuv.demo.mina.client;
import com.xuanyuv.demo.mina.model.UserInfo;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
/**
* 客户端示例
* Created by 玄玉<https://www.xuanyuv.com/> on 2012/06/14 14:06.
*/
public class MyClient {
public static void main(String[] args) {
//NioSocketConnector功能类似于java.net.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()
// )
//));
connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
//注册IoHandler,即指定客户器端的消息处理器
//connector.setHandler(new ClientHandler("岂曰无衣..\r\n月照沟渠...."));
connector.setHandler(new ClientHandler(new UserInfo("查文斌", 99)));
//连接服务器
//该方法是异步执行的,且可以同时连接多个服务端
SocketAddress remoteAddress = new InetSocketAddress("127.0.0.1", 9876);
SocketAddress localAddress = new InetSocketAddress("127.0.0.1", 5432);
//第二个参数若不传递则会使用本地的随机端口访问Server
//connector.connect(remoteAddress, localAddress);
connector.connect(remoteAddress);
System.out.println("Mina Client is startup");
}
}
下面是客户端的自定义消息处理器ClientHandler.java
package com.xuanyuv.demo.mina.client;
import com.xuanyuv.demo.mina.model.UserInfo;
import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
/**
* 客户端的自定义消息处理器
* Created by 玄玉<https://www.xuanyuv.com/> on 2012/06/14 14:06.
*/
class ClientHandler extends IoHandlerAdapter {
//双方采用TextLineCodecFactory(即换行符为标识的数据通信)时可以直接定义字符串接收
//private final String values;
//ClientHandler(String _values){
// this.values = _values;
//}
private final UserInfo userInfo;
ClientHandler(UserInfo _userInfo){
this.userInfo = _userInfo;
}
@Override
public void sessionCreated(IoSession session) throws Exception {
System.out.println("sessionCreated is invoked....");
}
/**
* 发送消息
* -----------------------------------------------------------------------------------
* 客户端连接有两个事件:sessionCreated()和sessionOpened()
* sessionCreated()是由IoProcessor线程触发的,sessionOpened()跟在其后,是由业务线程触发的
* 由于Mina中的IoProcessor线程非常少,因此sessionCreated()通常用于处理耗时短的操作
* 而将业务初始化等功能放在sessionOpened()事件中,比如发送消息
* -----------------------------------------------------------------------------------
* 我们可以在sessionOpened()、messageReceived()中通过IoSession.write()方法发送消息
* 因为在这两个方法中,TCP连接都是打开的状态,只不过发送的时机不同
* sessionOpened()是在TCP连接建立之后,接收到数据之前发送
* messageReceived()是在接收到数据之后发送
* -----------------------------------------------------------------------------------
*/
@Override
public void sessionOpened(IoSession session) throws Exception {
//写数据,该操作是异步的
//session.write(this.values);
session.write(this.userInfo);
}
@Override
public void messageReceived(IoSession session, Object message) throws Exception {
UserInfo userInfo = (UserInfo)message;
System.out.println("收到服务机的信息-->" + ReflectionToStringBuilder.toString(userInfo));
}
/**
* 关于TCP连接的关闭
* -----------------------------------------------------------------------------------
* 无论在客户端还是服务端,IoSession都用于表示底层的一个TCP连接
* 不过,无论Server端还是Client端的IoSession调用close()之后,TCP连接虽然显示关闭,但主线程仍在运行,即JVM并未退出
* 这是因为IoSession的close()仅仅是关闭了TCP的连接通道,并没有关闭Server端和Client端的程序
* 此时需要调用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后再关闭)
//Mina-2.0.16已经不推荐直接调用clost(),推荐使用closeNow()和flushAndClose()替代
session.close(false);
//IoSession.IoService getService()用于返回与当前会话对象关联的IoService实例
session.getService().dispose();
}
}
控制台输出
这是客户端的控制台输出
Mina Client is startup
sessionCreated is invoked....
13:34:22.922 [NioProcessor-2] DEBUG org.apache.mina.filter.codec.ProtocolCodecFilter - Processing a MESSAGE_RECEIVED for session 1
收到服务机的信息-->com.xuanyuv.demo.mina.model.UserInfo@26e2473b[name=查文斌==>>是个神秘的人,age=22]
这是服务端的控制台输出
MinaServer is startup, and it`s listing on := 9876
13:34:22.851 [NioProcessor-2] INFO org.apache.mina.filter.logging.LoggingFilter - CREATED
13:34:22.857 [NioProcessor-2] INFO org.apache.mina.filter.logging.LoggingFilter - OPENED
InComing Client:/127.0.0.1:52102
13:34:22.873 [NioProcessor-2] INFO org.apache.mina.filter.logging.LoggingFilter - RECEIVED: HeapBuffer[pos=0 lim=66 cap=2048: 00 00 00 3E AC ED 00 05 73 72 01 00 23 63 6F 6D...]
13:34:22.876 [NioProcessor-2] DEBUG org.apache.mina.filter.codec.ProtocolCodecFilter - Processing a MESSAGE_RECEIVED for session 1
收到客户机的信息-->com.xuanyuv.demo.mina.model.UserInfo@369a4df3[name=查文斌,age=99]
13:34:22.918 [NioProcessor-2] INFO org.apache.mina.filter.logging.LoggingFilter - SENT: com.xuanyuv.demo.mina.model.UserInfo@56ee1273
13:34:32.918 [NioProcessor-2] INFO org.apache.mina.filter.logging.LoggingFilter - IDLE
13:34:42.919 [NioProcessor-2] INFO org.apache.mina.filter.logging.LoggingFilter - IDLE
13:34:52.919 [NioProcessor-2] INFO org.apache.mina.filter.logging.LoggingFilter - IDLE
13:35:02.920 [NioProcessor-2] INFO org.apache.mina.filter.logging.LoggingFilter - IDLE
13:35:12.920 [NioProcessor-2] INFO org.apache.mina.filter.logging.LoggingFilter - IDLE