在上一篇文档中我们已经了解了IoFilter的用法和其在Mina中的作用,作为Mina数据传输过程中比较重要的组件,IoFilter起到了承上启下的作用----接收数据,编/解码,将数据传递到逻辑层,当数据传递地到逻辑层时,IoFilter的使命就完成了,那么逻辑层的数据由谁来处理呢?如何处理的?这就是本文要讲述的内容----IoHandler。
在介绍IoFilter的时候,文中首先是从IoFilter的结构和其在Mina中的作用谈起的,最后添加了一个使用IoFilter的例子,之前我将其传给几个同学看时,感觉这种方式比较晦涩,应该将例子提到前面,由于时间的关系我不能在对IoFilter 的介绍做过多的修改,所以在本篇文档中我就先以一个例子开头,介绍IoHandler,希望这种讲述方式能对你理解Mina有更多的帮助。
好了,言归正传,我们的例子还是以上篇文档中的IoFilter的例子为基础,在此基础上着重突出IoHandler的作用。
ServerMain:
public class ServerMain {
public static void main(String[] args) throws IOException {
SocketAddress address = new InetSocketAddress
("localhost", 4321);
IoAcceptor acceptor = new SocketAcceptor();
IoServiceConfig config = acceptor.getDefaultConfig
();
// 配置数据的编解码器
config.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new
ObjectSerializationCodecFactory()));
config.getFilterChain().addLast("logger", new
LoggingFilter());
// 绑定服务器端口
acceptor.bind(address, new ServerHandler());
System.out.println(" 服务器开始在 8000 端口监听
.......");
}
}
ServerHandler:
public class ServerHandler extends IoHandlerAdapter {
// 创建会话
public void sessionOpened(IoSession session) throws
Exception {
System.out.println(" 服务器创建了会话 ");
session.write(" 服务器创建会话时发送的信息 。");
}
// 发送信息
public void messageSent(IoSession session, Object message)
throws Exception {
}
// 接收信息
public void messageReceived(IoSession session, Object
message)
throws Exception {
}
}
ClientMain:
public class ClientMain {
public static void main(String[] args) {
SocketAddress address = new InetSocketAddress
("localhost", 4321);
IoConnector connector = new SocketConnector();
IoServiceConfig config =
connector.getDefaultConfig();
// 配置数据的编解码器
config.getFilterChain().addLast("codec",
new ProtocolCodecFilter(new
ObjectSerializationCodecFactory()));
config.getFilterChain().addLast("logger", new
LoggingFilter());
// 连接到服务器
connector.connect(address, new ClientHandler());
System.out.println(" 已经连接到了服务器 " + address);
}
}
ClientHandler:
public class ClientHandler extends IoHandlerAdapter {
// 发送信息
public void messageSent(IoSession session, Object message)
throws Exception {
}
// 接收信息
public void messageReceived(IoSession session, Object
message)
throws Exception {
System.out.println(" 客户端接收到的服务器的信息是 "
+ message);
}
}
上面给出里这个例子中的主要的代码,当先后启动服务器和客户端后,服务器会在客户端连接到服务器后发送一个字符串的消息。这个消息的发送就是在IoHandler中发送的。IoHandler在Mina中属于业务层,这里的IoHandler更相是J2EE中的Servlet的作用,在IoHandler中你可以不用考虑底层数据的封装和转换,前提是你已经在IoFilter中已经完成了数据的转换。这里需要提到的一个是,所谓数据的转换,是指将二进制数据转换成Java中的可用对象或者是基本类型的数据。由于网络传输中传输的都是二进制数据,这就需要有一个专门的数据转换层,就Mina中的编解码器来实现这个功能。如果使用RMI,对于这个问题应该不陌生,二进制和对象之间的转化过程其实就是对象的序列化和反序列化的过程。关于Mina中实现对象的序列化和反序列化会在后续的文档中详细介绍,在此不在赘述。
既然IoHandler是逻辑层,我们就用IoHandler实现一个简单的逻辑实现。先听一个小故事:一个淘气的小孩要去KFC买汉堡,由于KFC生意比较好,人比较多,服务员忙不过来,于是KFC专门设立了一个自动售汉堡的机器,这个机器只是一个简单的数据收发装置,(由于汉堡的价格时常变化,所以价格会实时更新,因此该机器需要和KFC的汉堡的价格服务器相连)小朋友买汉堡时只要向机器中投入硬币,机器就会查询服务器,看价格是否符合,若符合,则送给小朋友一个汉堡
,若不符合则提示小朋友钱不够,买不到这个汉堡。
上面是我自己虚构的一个小故事,我们先不管现实中的KFC是如何运作的,我们就当故事是真的了,那么现在这个小的项目分配给了我们,我们需要抽象出它的需求:
客户端要向服务器发送数据,查询价格,根据价格是否合理给出相应的显示服务器接收客户度的价格查询请求,根据服务器中存储的价格信息,返回响应的结果。
根据上面的需求,我们使用Mina来实现上面的服务器和客户端,程序的代码如下(完整代码在附件中):
KFCFoodPriceHandler(服务器句柄):
public class KFCFoodPriceHandler extends IoHandlerAdapter {
// 创建会话
public void sessionOpened(IoSession session) throws
Exception {
// System.out.println(" 服务器创建了会话 ");
}
// 接收信息
public void messageReceived(IoSession session, Object
message)
throws Exception {
HashMap<String, Object> map = (HashMap<String,
Object>) message;
String buythings = (String) map.get("购买");
// System.out.println(" 服务器接收到的信息 " +
buythings);
if (buythings.equals("汉堡")) {
HashMap<String, Object> map2 = new
HashMap<String, Object>();
map2.put("食品", "汉堡");
map2.put("价格", 4);
session.write(map2);
} else if (buythings.equals("鸡翅")) {
HashMap<String, Object> map2 = new
HashMap<String, Object>();
map2.put("食品", "鸡翅");
map2.put("价格", 5);
session.write(map2);
} else {
session.write(" 该种物品已经出售完毕,谢谢
惠顾!");
}
}
}
KFCSellerHandler(客户端句柄):
public class KFCSellerHandler extends IoHandlerAdapter {
private Integer childInputMoney_Ham = 4;
private Integer childInputMoney_Chick = 5;
// 创建会话
public void sessionOpened(IoSession session) throws
Exception {
HashMap<String, Object> map = new
HashMap<String, Object>();
map.put("购买", "汉堡");
session.write(map);
}
// 接收信息
public void messageReceived(IoSession session, Object
message)
throws Exception {
// System.out.println(" 客户端接收到的服务器的信息是 "
// + (HashMap<String, Object>)
message);
HashMap<String, Object> priceInfor =
(HashMap<String, Object>) message;
// System.out.println("============" +
priceInfor.get("食品"));
String foodName = (String) priceInfor.get("食品");
if (foodName.equals("汉堡")) {
Integer foodPrice = (Integer) priceInfor.get
("价格");
if (foodPrice.equals
(childInputMoney_Ham)) {
System.out.println(" 您好,请收好
你的汉堡,欢迎下次光临!");
} else {
System.out.println(" 对不起,你投
如的钱币数量不够,钱已经如数归还,请收好!");
}
} else if (foodName.equals("鸡翅")) {
Integer foodPrice = (Integer) priceInfor.get
("价格");
if (foodPrice.equals
(childInputMoney_Chick)) {
System.out.println(" 您好,请收好
你的汉堡,欢迎下次光临!");
} else {
System.out.println(" 对不起,你投
如的钱币数量不够,钱已经如数归还,请收好!");
}
}
}
}
通过上面的程序我们可以看出Mina的中业务逻辑处理都可以在IoHandler中,而不需要考虑对象的序列化和反序列化问题。关于IoHandler的简单用法就说这么多。下面再看看与IoHandler相关的几个中要的类。
按照惯例,还是先给出IoHandler及其相关类的类图:
从上面的类图我们可以清晰的看到IoHandler是一个接口,它有两个子类:
IoHandlerAdpater:它只是提供了IoHandler中定义的方法体,没有任何的逻辑处理,你可以根据你自己的需求重写该类中的相关方法。这个类在实际的开发中使用的是较多的。我们上面写的例子都是继承于这个类来实现的。
SingleSessionIoHandlerDelegate:这是一个服务器和客户端只有一个会话时使用的类,在该类的方法中没有提供session的参数,该类在实际的开发中使用的较少,如果需要对该类进行更深入的了解,请参考Mina 1.1.7的API文档。
在Mina提供的IoHandler的具体实现中,大部分的实现类都是继承与IoHandlerApater,IoHandlerAdpater在Mina 1.1.7中的子类有三个:
ChainedIoHandler:这个类主要是用于处理IoHandler的messageReceived事件,它和IoHandlerChain配合使用。当在业务逻辑中有多个IoHandler需要处理时,你可以将你的每个IoHandler添加到IoHandlerChain中,这个和过滤器
链比较相似,关于IoFilter和IoHandlerChain的具体用法和区别会在后续的文档中给出。
StreamIoHandler:该类也是用于处理IoHandler的messageReceived事件,它主要用于文件传输的系统中,比如FTP服务器中,如果需要对该类进行更深入的了解,请参考Mina 1.1.7的API文档。
DemuxingIoHandler:该类主要是用于处理多个IoHandler的messageReceived,由于在TCP/IP协议的数据传输中会出现数据的截断现象(由于socket传输的数据包的长度是固定的,当数据包大于该长度,数据包就会被截断),所以提供这个类主要是保证IoHandler所处理的数据包的完整性,这个和编解码器中的CumulativeProtocolDecoder类似,关于这两个类的具体介绍会在后续的文档中给出。
至此,关于IoHandler的作用就讲述完了,希望对你能有所帮助。:)