mina2.X基于TCP/IP协议的开发示例
------服务端
一、环境:
(1)、java环境:jdk-1.6
(2)、jar包:mina-core-2.0.4.jar mina核心包
slf4j-api-1.6.1.jar 日志接口包(只是接口,并未实现功能)
log4j-1.2.15.jar Apache的开源日志包
slf4j-log4j12-1.6.1.jar slf4j调用log4j的日志接口实现包
(3)、开发工具:eclipse3.7
二、mina2.X框架:
特点:mina2.X区别于mina1.X的最大特点是Nio非阻塞技术。每个线程负责一组数量不确定的用户群,并采不断的轮询用户,操作可操作的用户。之后,jdk1.4对这种方法进行了升级,采用异步多路复用技术,
避免了轮询的开销。
服务端相关类: IoAcceptor(服务套接字),IoFilter(消息过滤器),IoHandler(业务处理类)
三、项目介绍:
本例模仿mina官方的TimeServer例子,当服务器收到客户端的消息时,返回当前的日期给客户端;当客户端发送字符串”quit”时,服务器断开连接(但是服务器并未关闭)。
mina服务器端的开发大致遵循了以下步骤:
(1)、创建服务器端监听 NioSocketAcceptor对象,相当于socket
(2)、添加过滤器。
I、添加日志过滤器。没什么好说的,一般的服务器都要布置日志系统。
Ii、添加编码过滤器。TCP/IP协议簇传送的数据通过该过滤器解析成消息对象。
Iii、其他需要的过滤器。本例中不涉及其他过滤器。
(3)、创建服务器的逻辑处理中心------一个继承了IoHandlerAdapter的Handler类。该类可以根据情况覆盖父类中的方法。本例将所有方法都覆盖,以观察这些方法何时触发。
以下是Handler类的代码:
TimeServerHandler.java
package com.handaer.mina.server;
import java.util.Date;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
/**
* 服务器逻辑处理类
*/
public classTimeServerHandler extends IoHandlerAdapter {
// 应用中未被捕获的所有异常将被该方法捕获
@Override
public voidexceptionCaught(IoSession session, Throwable cause)
throws Exception {
cause.printStackTrace();
}
// 服务器接收到客户端返回消息时触发的方法,Object message就是客户端发送的消息
@Override
public voidmessageReceived(IoSession session, Object message)
throws Exception {
Stringtrim = message.toString().trim();
System.out.println("messageReceived : "+ trim);
// 定义:如果发送"quit"则连接断开
if ("quit".equals(trim)) {
session.close(true);
return;
}
Datedate = newDate();
session.write(date.toString());
System.out.println("Message writen... ... : " + date.toString());
}
// 服务器向客户端发送消息时触发的方法.在本例中,messageSent的message将会是messageReceived中的date
@Override
public void messageSent(IoSessionsession, Object message) throws Exception {
System.out.println("messageSent : "+ message);
}
// 与客户端连接中断时触发的方法
@Override
public void sessionClosed(IoSessionsession) throwsException {
System.out.println("sessionClosed");
}
// 创建与客户端连接成功时触发的方法
@Override
public voidsessionCreated(IoSession session) throws Exception {
System.out.println("sessionCreated");
}
// 连接处于空闲状态时触发的方法。空闲状态由在创建服务时SocketSessionConfig的setIdleTime方法设置,包括读取空闲,写入空闲,或者两者都空闲这三个状态
@Override
public void sessionIdle(IoSessionsession, IdleStatus status)
throws Exception {
System.out.println("sessionIdle : " +session.getIdleCount(status));
}
// 开启连接触发
@Override
public void sessionOpened(IoSessionsession) throwsException {
System.out.println("sessionOpened");
}
}
服务还没有创建完成,继续创建服务。
(4)、将刚才创建的任务(IoHandler)交给服务套接字(IoAcceptor)。(绑定handler)
(5)、最后就是一些配置,具体参见代码。
package com.handaer.mina.server;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.logging.Logger;
import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.SocketAcceptor;
import org.apache.mina.transport.socket.SocketSessionConfig;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
public classMinaTimeServer {
private static SocketAcceptor acceptor;
private staticDefaultIoFilterChainBuilder filter;
private static SocketSessionConfig config;
public static Logger logger = Logger.getLogger("TimeServer");
/**
* @Description: TODO(这里用一句话描述这个方法的作用)
* @param args
* void 返回类型
* @throws
*/
public static void main(String[] args) {
// 1、创建服务器端监听
acceptor = new NioSocketAcceptor();
// 2、添加日志过滤和编码过滤
filter = acceptor.getFilterChain();
filter.addLast("logger",newLoggingFilter());
// ----编码过滤:将二进制或者协议相关数据转换成一个对象。TextLine工厂类可以处理基于文字的信息
filter.addLast("codec",newProtocolCodecFilter(
newTextLineCodecFactory(Charset.forName("GBK"))));
// 3、绑定handler到acceptor
acceptor.setHandler(new TimeServerHandler());
// 4、设置socket属性
// 获取socket的连接参数
config = acceptor.getSessionConfig();
// 设置socket的缓冲区大小为2M
config.setReadBufferSize(2048);
/**
* @params IdleStatus arg0 :在未成为idle状态前应该关心的状态(READ_IDLE或者WRITE_IDLE)
* @params IdleStatus arg1 : 变成IDLE状态所需要的时间(超时时间)
*
* 如果session持续idle的时间等于arg1时,将会触发handler中的sessionIdle方法
*/
// 设置空闲状态持续时间:1、这里的状态可以自己设置成只为读取设置空闲状态持续时间,只为写入设置空闲状态等待时间,或者为两者都设置空闲状态等待时间。后面的时间是两次触发handler中的sessionIdel方法的间隔时间。
config.setIdleTime(IdleStatus.BOTH_IDLE, 10);
try {
// 为服务器socket绑定端口
acceptor.bind(new InetSocketAddress(7000));
logger.info("服务已经启动... ...");
}catch(IOException e) {
logger.info("服务启动异常:");
e.printStackTrace();
}
}
}
至此,服务端已经完毕。跑起来
测试:
1、使用telnet工具测试服务器。(之后还有客户端开发)
打开cmd,输入telnet 127.0.0.17000 (确保telnet服务为本禁用且当前已经开启。)成功后,Eclipse控制台信息如:
开启服务:
2013-9-4 11:21:42 com.handaer.mina.server.MinaTimeServermain
信息: 服务已经启动... ...
sessionCreated
sessionOpened
输入字符:
messageReceived : 你好
Message writen... ... : Wed Sep 04 11:21:50 CST 2013
messageSent : Wed Sep 04 11:21:50 CST 2013
messageReceived : mina
Message writen... ... : Wed Sep 04 11:21:55 CST 2013
messageSent : Wed Sep 04 11:21:55 CST 2013
关闭连接:
messageReceived : quit
sessionClosed
关闭服务:点击控制台上面的红色按钮Teminate(如果服务未启动则该按钮是灰色)
2、使用浏览器测试(Http)。地址栏输入 : 127.0.0.1:7000
2013-9-4 11:24:55 com.handaer.mina.server.MinaTimeServer main
信息: 服务已经启动... ...
sessionCreated
sessionOpened
sessionCreated
sessionOpened
messageReceived : GET / HTTP/1.1
Message writen... ... : Wed Sep 04 11:25:16 CST 2013
messageReceived : Host: 127.0.0.1:9123
Message writen... ... : Wed Sep 04 11:25:16 CST 2013
messageReceived : Connection: keep-alive
Message writen... ... : Wed Sep 04 11:25:16 CST 2013
messageReceived : Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Message writen... ... : Wed Sep 04 11:25:16 CST 2013
messageReceived : User-Agent: Mozilla/5.0 (Windows NT 6.1)AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31
Message writen... ... : Wed Sep 04 11:25:16 CST 2013
messageReceived : Accept-Encoding: gzip,deflate,sdch
Message writen... ... : Wed Sep 04 11:25:16 CST 2013
messageReceived : Accept-Language: zh-CN,zh;q=0.8
Message writen... ... : Wed Sep 04 11:25:16 CST 2013
messageReceived : Accept-Charset: GBK,utf-8;q=0.7,*;q=0.3
Message writen... ... : Wed Sep 04 11:25:16 CST 2013
messageReceived :
Message writen... ... : Wed Sep 04 11:25:16 CST 2013
messageSent : Wed Sep 04 11:25:16 CST 2013
messageSent : Wed Sep 04 11:25:16 CST 2013
messageSent : Wed Sep 04 11:25:16 CST 2013
messageSent : Wed Sep 04 11:25:16 CST 2013
messageSent : Wed Sep 04 11:25:16 CST 2013
messageSent : Wed Sep 04 11:25:16 CST 2013
messageSent : Wed Sep 04 11:25:16 CST 2013
messageSent : Wed Sep 04 11:25:16 CST 2013
messageSent : Wed Sep 04 11:25:16 CST 2013
sessionClosed
sessionIdle :1
------客户端
与服务端基本一致,只是使用客户端的接口IoConnector。需要绑定ip和服务器指定的端口号
TimeClientHandler.java
package com.handaer.mina.client;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
public classTimeClientHandler implements IoHandler {
@Override
public voidexceptionCaught(IoSession arg0, Throwable arg1)
throws Exception {
arg1.printStackTrace();
}
@Override
public void messageReceived(IoSessionarg0, Object arg1) throws Exception {
System.out.println("messageReceived : "+ arg1);
}
@Override
public void messageSent(IoSessionarg0, Object arg1) throws Exception {
System.out.println("messageSent : "+ arg1);
}
@Override
public void sessionClosed(IoSessionarg0) throwsException {
System.out.println("sessionClosed");
}
@Override
public voidsessionCreated(IoSession arg0) throws Exception {
System.out.println("sessionCreated");
}
@Override
public void sessionIdle(IoSessionarg0, IdleStatus arg1) throws Exception {
System.out.println("sessionIdle: "+ arg0.getIdleCount(arg1));
}
@Override
public void sessionOpened(IoSessionarg0) throwsException {
System.out.println("sessionOpened ");
}
}
MinaTimeClient.java
package com.handaer.mina.client;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoConnector;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
public classMinaTimeClient {
private static IoConnector connector;
private staticDefaultIoFilterChainBuilder filterChain;
private static ConnectFuture future;
private static IoSession session;
public static void main(String[] args) {
// 1、创建客户端通讯串口
connector = new NioSocketConnector();
// 2、添加日志过滤和编码过滤
filterChain = connector.getFilterChain();
filterChain.addLast("loggor",newLoggingFilter());
filterChain.addLast("codec",newProtocolCodecFilter(
newTextLineCodecFactory(Charset.forName("GBK"))));
// 3、绑定handler
connector.setHandler(new TimeClientHandler());
// 4、创建连接
try {
future = connector
.connect(new InetSocketAddress("127.0.0.1",7000));
future.awaitUninterruptibly();//
session = future.getSession();
// 向服务器发送消息
session.write("你好mina");
session.write("quit");
}catch(Exception e) {
// TODO: handle exception
}finally{
Logger.getLogger("logger").info("finally");
// 从客户端关闭连接方法。(推荐从服务器关闭连接)
session.getCloseFuture().awaitUninterruptibly();// 等待连接断开
connector.dispose();
// session.write("quit"); //服务器定义如果收到"quit"则关闭连接,推荐的关闭方式
}
}
}
------log4j.properties日志配置文件
如果控制台出现如下警告,原因是log4j.propertis文件错误或者不存在:
log4j:WARN No appenders could be found for logger(org.apache.mina.filter.logging.LoggingFilter).
log4j:WARNPlease initialize the log4j system properly.
在服务器和客户端都有配置log4j.propertis:
在项目的src目录下创建log4j.propertis,其内容可以根据需要修改。
本项目内内容如下:
#Loggers
#log4j.rootLogger=info,file,console
log4j.rootLogger=info,file
#Appenders
#console
#log4j.appender.console=org.apache.log4j.ConsoleAppender;
#log4j.appender.console.layout=org.log4j.SimpleLayout
#file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=f:/NetBeans/logtest.log
log4j.appender.file.MaxFileSize=20KB
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d {yyyy-MM-ddHH:mm:ss,SSSS}%c,%M,%F,%L%p - %m%n