mina学习笔记

pache Mina Server 是一个网络通信应用框架,也就是说,它主要是对基于TCP/IP、UDP/IP协议栈的通信框架(当然,也可以提供JAVA 对象的序列化服务、虚拟机管道通信服务等),Mina 可以帮助我们快速开发高性能、高扩展性的网络通信应用,Mina 提供了事件驱动、异步(Mina 的异步IO 默认使用的是JAVA NIO 作为底层支持)操作的编程模型。

第一步:建立服务器连接

这里是最简单的方式

  1. 需要一个Nio的接受对象:
    nioSOcketAcceptor
  2. 需要一个处理器:
    该处理器要继承于IoHandlerAdapter(io处理适配器),其中有多个方法用于事件处理
  3. 添加拦截器:
    为什么需要连接器,我们知道传统的IO流传输的是二进制字节数组,我们就要像以前那样再去读流写流,用MINA就不用了呀,因为都封装在编码解码器里面,有几种默认的解码工厂,TextLineCodeCFactory用于字符串的解码,可以添加多个拦截器,也可以自己重写解码器,具体看文档
  4. bind绑定端口号

服务端主入口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) throws Exception {
try {
//第一步 创建一个NioSocketAcceptor 对象
NioSocketAcceptor acceptor = new NioSocketAcceptor();
//第二步设置handler
acceptor.setHandler(new MyServerHandler());
//第三步,获取拦截器,发来的消息都需要通过拦截器拦截之后才能接收到
//添加一个拦截器对数据进行加解码TextLineCodecFactory()
acceptor.getFilterChain().addLast("CodeFilter", new ProtocolCodecFilter(new TextLineCodecFactory()));
//第四步,绑定端口号
acceptor.bind(new InetSocketAddress("127.0.0.1", 9898));
} catch (Exception e) {
// TODO: handle exception
}
}

Io处理器类(MyServerHandler)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class MyServerHandler extends IoHandlerAdapter {

//异常捕获
@Override
public void exceptionCaught(IoSession session, Throwable cause)
throws Exception {
System.out.println("有异常了,异常处理启动!");
}

@Override
public void inputClosed(IoSession session) throws Exception {

}

//接收到客服端消息
@Override
public void messageReceived(IoSession session, Object message)
throws Exception {
System.out.println( "接受到客服端消息:"+message);
}

//发送给客服端消息
@Override
public void messageSent(IoSession session, Object message)
throws Exception {
}

//session关闭
@Override
public void sessionClosed(IoSession session) throws Exception {
System.out.println("session关闭!");
}

//session创建
@Override
public void sessionCreated(IoSession session) throws Exception {
System.out.println("sessionCreated!");
}

// session 空闲的时候调用
@Override
public void sessionIdle(IoSession session, IdleStatus status)
throws Exception {
System.out.println("session 空闲的时候调用 ");
}

// 创建了session 后会回调sessionOpened
@Override
public void sessionOpened(IoSession session) throws Exception {
System.out.println(" 创建了session 后会回调sessionOpened ");
new Thread(new SendMessageThread(session)).start();
}

}

最重要的是messageReceived用于接受请求,主要方法中在正常流程中要做其他处理,应该用线程来做,比如在↓sessionOPENED 表示通讯建立后的回调,这时候我就可以在session中写东西到客服端了,我开启一个新的线程SendMessageThread并启动,具体的业务逻辑就可以写在该线程中,用于服务端给客服端发消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class SendMessageThread implements Runnable{  

private IoSession session = null;
private String msg = null;

public SendMessageThread(IoSession session) {
this.session = session;
}

public SendMessageThread(IoSession session, String msg) {
this.msg = msg;
}

public String getMsg() {
return msg;
}

public void setMsg(String msg) {
this.msg = msg;
}

@Override
public void run() {
Scanner s = new Scanner(System.in);
if(null == msg) {
msg = "test";
}
while(true) {
msg = s.nextLine();
session.write(new Date()+":"+msg);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

第二步:建立客服端连接

步骤基本和服务端差不多

  1. 需要一个连接器;
    (NioSocketConnector)
  2. 需要一个处理器;
  3. 需要一个拦截器;
  4. 建立连接Socket(ip,端口):
    返回值为ConnectFuture(连接结果),因为不一定这个连接能成功,也可以对连接进行处理,关闭啊之类的,也可以在连接中获取到该Session 进行读写

客服端主入口(handler-MinaClientHanlder和服务端大同小异,就不贴出了)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static void main(String[] args) throws Exception {

// 创建Socket
NioSocketConnector connector = new NioSocketConnector();
//设置传输方式
/* DefaultIoFilterChainBuilder chain = connector.getFilterChain();
ProtocolCodecFilter filter = new ProtocolCodecFilter(new ObjectSerializationCodecFactory());
chain.addLast("objectFilter", filter); */

connector.getFilterChain().addLast("CodeFilter", new ProtocolCodecFilter(new TextLineCodecFactory()));

//设置消息处理
connector.setHandler(new MinaClientHanlder());
//超时设置
connector.setConnectTimeoutCheckInterval(30);
//连接
ConnectFuture cf = connector.connect(new InetSocketAddress("localhost", 9898));
cf.awaitUninterruptibly();

Scanner scanner = new Scanner(System.in);

while (true) {
String a = scanner .nextLine();
cf.getSession().write(new Date().toString()+"-"+a);
}
}

其他的思考

这里都是最简单的处理,客服端和服务端一对一的.  
当多个连接时,需要一个类用来装载,所有连接,给连接赋名,用来准确获取该连接.  
基本和tcp,udp差不多,客户端间通讯即是服务端转发嘛,也很好理解。

下载链接

  1. mina 相关jar包 https://github.com/MroZ11/mina/
  2. 试例源码 https://github.com/MroZ11/mina/