多路分离的解码器

(6-3.)多路分离的解码器:
假设一段数据发送过来之后,需要根据某种条件决定使用哪个解码器,而不是像上面的例子,固定使用一个解码器,那么该如何做呢?幸好Mina 提供了org.apache.mina.filter.codec.demux 包来完成这种多路分离(Demultiplexes)的解码工作,也就是同时注册多个解码器,然后运行时依据传入的数据决定到底使用哪个解码器来工作。所谓多路分离就是依据条件分发到指定的解码器,譬如:上面的短信协议进行扩展,可以依据状态行来判断使用1.0 版本的短信协议解码器还是2.0版本的短信协议解码器。
下面我们使用一个简单的例子,说明这个多路分离的解码器是如何使用的,需求如下所示:
(1.) 客户端传入两个int 类型的数字,还有一个char 类型的符号。
(2.) 如果符号是+,服务端就是用1 号解码器,对两个数字相加,然后把结果返回给客户端。
(3.) 如果符号是-,服务端就使用2 号解码器,将两个数字变为相反数,然后相加,把结果返回给客户端。
Demux 开发编解码器主要有如下几个步骤:
A. 定义Client 端、Server 端发送、接收的数据对象。
B. 使用Demux 编写编码器是实现MessageEncoder接口,T 是你要编码的数据对象,这个MessageEncoder 会在DemuxingProtocolEncoder 中调用。
C. 使用Demux 编写编码器是实现MessageDecoder 接口,这个MessageDecoder 会在DemuxingProtocolDecoder 中调用。

D. 在 DemuxingProtocolCodecFactory 中调用addMessageEncoder()、addMessageDecoder()方法组装编解码器。
MessageEncoder的接口如下所示:

[java]  view plain copy
  1. public interface MessageEncoder {    
  2. void encode(IoSession session, T message, ProtocolEncoderOutput out)    
  3. throws Exception;    
  4. }    

你注意到消息编码器接口与在ProtocolEncoder 中没什么不同,区别就是Object message被泛型具体化了类型,你不需要手动的类型转换了。
MessageDecoder的接口如下所示:

[java]  view plain copy
  1. public interface MessageDecoder {    
  2. static MessageDecoderResult OK = MessageDecoderResult.OK;    
  3. static MessageDecoderResult NEED_DATA =    
  4. MessageDecoderResult.NEED_DATA;    
  5. static MessageDecoderResult NOT_OK = MessageDecoderResult.NOT_OK;    
  6. MessageDecoderResult decodable(IoSession session, IoBuffer in);    
  7. MessageDecoderResult decode(IoSession session, IoBuffer in,    
  8. ProtocolDecoderOutput out) throws Exception;    
  9. void finishDecode(IoSession session, ProtocolDecoderOutput out)    
  10. throws Exception;    
  11. }    

(1.)decodable()方法有三个返回值,分别表示如下的含义:
A. MessageDecoderResult.NOT_OK:表示这个解码器不适合解码数据,然后检查其它解码器,如果都不满足会抛异常;
B. MessageDecoderResult.NEED_DATA:表示当前的读入的数据不够判断是否能够使用这个解码器解码,然后再次调用decodable()方法检查其它解码器,如果都是NEED_DATA,则等待下次输入;
C. MessageDecoderResult.OK: 表示这个解码器可以解码读入的数据, 然后则调用MessageDecoder 的decode()方法。这里注意decodable()方法对参数IoBuffer in 的任何操作在方法结束之后,都会复原,也就是你不必担心在调用decode()方法时,position 已经不在缓冲区的起始位置。这个方法相当于是预读取,用于判断是否是可用的解码器。
(2.)decode()方法有三个返回值,分别表示如下的含义:
A. MessageDecoderResult.NOT_OK:表示解码失败,会抛异常;

B. MessageDecoderResult.NEED_DATA:表示数据不够,需要读到新的数据后,再次调用decode()方法。
C. MessageDecoderResult.OK:表示解码成功。
代码演示:
(1.)客户端发送的数据对象:

[java]  view plain copy
  1. public class SendMessage {    
  2. private int i = 0;    
  3. private int j = 0;    
  4. private char symbol = '+';    
  5. public char getSymbol() {    
  6. return symbol;    
  7. }    
  8. public void setSymbol(char symbol) {    
  9. this.symbol = symbol;    
  10. }    
  11. public int getI() {    
  12. return i;    
  13. }    
  14. public void setI(int i) {    
  15. this.i = i;    
  16. }    
  17. public int getJ() {    
  18. return j;    
  19. }    
  20. public void setJ(int j) {    
  21. this.j = j;    
  22. }    
  23. }    

(2.)服务端发送的返回结果对象:

[java]  view plain copy
  1. public class ResultMessage {    
  2. private int result = 0;    
  3. public int getResult() {    
  4. return result;    
  5. }    
  6. public void setResult(int result) {    
  7. this.result = result;    
  8. }    
  9. }    

(3.)客户端使用的SendMessage的编码器:

[java]  view plain copy
  1. public class SendMessageEncoder implements MessageEncoder    
  2. {    
  3. @Override    
  4. public void encode(IoSession session, SendMessage message,    
  5. ProtocolEncoderOutput out) throws Exception {    
  6. IoBuffer buffer = IoBuffer.allocate(10);    
  7. buffer.putChar(message.getSymbol());    
  8. buffer.putInt(message.getI());    
  9. buffer.putInt(message.getJ());    
  10. buffer.flip();    
  11. out.write(buffer);    
  12. }    
  13. }    

这里我们的SendMessage、ResultMessage 中的字段都是用长度固定的基本数据类型,这样IoBuffer 就不需要自动扩展了,提高性能。按照一个char、两个int 计算,这里的IoBuffer只需要10 个字节的长度就可以了。
(4.)服务端使用的SendMessage的1号解码器:

[java]  view plain copy
  1. public class SendMessageDecoderPositive implements MessageDecoder {    
  2. @Override    
  3. public MessageDecoderResult decodable(IoSession session, IoBuffer in)    
  4. {    
  5. if (in.remaining() < 2)    
  6. return MessageDecoderResult.NEED_DATA;    
  7. else {    
  8. char symbol = in.getChar();    
  9. if (symbol == '+') {    
  10. return MessageDecoderResult.OK;    
  11. else {    
  12. return MessageDecoderResult.NOT_OK;    
  13. }    
  14. }    
  15. }    
  16. @Override    
  17. public MessageDecoderResult decode(IoSession session, IoBuffer in,    
  18. ProtocolDecoderOutput out) throws Exception {    
  19. SendMessage sm = new SendMessage();    
  20. sm.setSymbol(in.getChar());    
  21. sm.setI(in.getInt());    
  22. sm.setJ(in.getInt());    
  23. out.write(sm);    
  24. return MessageDecoderResult.OK;    
  25. }    
  26. @Override    
  27. public void finishDecode(IoSession session, ProtocolDecoderOutput    
  28. out)    
  29. throws Exception {    
  30. // undo    
  31. }    
  32. }    

因为客户端发送的SendMessage 的前两个字节(char)就是符号位,所以我们在decodable()方法中对此条件进行了判断,之后读到两个字节,并且这两个字节表示的字符是+时,才认为这个解码器可用。
(5.)服务端使用的SendMessage的2号解码器:

[java]  view plain copy
  1. public class SendMessageDecoderNegative implements MessageDecoder {    
  2. @Override    
  3. public MessageDecoderResult decodable(IoSession session, IoBuffer in)    
  4. {    
  5. if (in.remaining() < 2)    
  6. return MessageDecoderResult.NEED_DATA;    
  7. else {    
  8. char symbol = in.getChar();    
  9. if (symbol == '-') {    
  10. return MessageDecoderResult.OK;    
  11. else {    
  12. return MessageDecoderResult.NOT_OK;    
  13. }    
  14. }    
  15. }    
  16. @Override    
  17. public MessageDecoderResult decode(IoSession session, IoBuffer in,    
  18. ProtocolDecoderOutput out) throws Exception {    
  19. SendMessage sm = new SendMessage();    
  20. sm.setSymbol(in.getChar());    
  21. sm.setI(-in.getInt());    
  22. sm.setJ(-in.getInt());    
  23. out.write(sm);    
  24. return MessageDecoderResult.OK;    
  25. }    
  26. @Override    
  27. public void finishDecode(IoSession session, ProtocolDecoderOutput    
  28. out)    
  29. throws Exception {    
  30. // undo    
  31. }    
  32. }    
(6.)服务端使用的ResultMessage的编码器:

[java]  view plain copy
  1. public class ResultMessageEncoder implements    
  2. MessageEncoder {    
  3. @Override    
  4. public void encode(IoSession session, ResultMessage message,    
  5. ProtocolEncoderOutput out) throws Exception {    
  6. IoBuffer buffer = IoBuffer.allocate(4);    
  7. buffer.putInt(message.getResult());    
  8. buffer.flip();    
  9. out.write(buffer);    
  10. }    
  11. }    

(7.)客户端使用的ResultMessage的解码器:

[java]  view plain copy
  1. public class ResultMessageDecoder implements MessageDecoder {    
  2. @Override    
  3. public MessageDecoderResult decodable(IoSession session, IoBuffer in)    
  4. {    
  5. if (in.remaining() < 4)    
  6. return MessageDecoderResult.NEED_DATA;    
  7. else if (in.remaining() == 4)    
  8. return MessageDecoderResult.OK;    
  9. else    
  10. return MessageDecoderResult.NOT_OK;    
  11. }    
  12. @Override    
  13. public MessageDecoderResult decode(IoSession session, IoBuffer in,    
  14. ProtocolDecoderOutput out) throws Exception {    
  15. ResultMessage rm = new ResultMessage();    
  16. rm.setResult(in.getInt());    
  17. out.write(rm);    
  18. return MessageDecoderResult.OK;    
  19. }    
  20. @Override    
  21. public void finishDecode(IoSession session, ProtocolDecoderOutput    
  22. out)    
  23. throws Exception {    
  24. // undo    
  25. }    
  26. }    

(8.)组装这些编解码器的工厂:

[java]  view plain copy
  1. public class MathProtocolCodecFactory extends    
  2. DemuxingProtocolCodecFactory {    
  3. public MathProtocolCodecFactory(boolean server) {    
  4. if (server) {    
  5. super.addMessageEncoder(ResultMessage.class,    
  6. ResultMessageEncoder.class);    
  7. super.addMessageDecoder(SendMessageDecoderPositive.class);    
  8. super.addMessageDecoder(SendMessageDecoderNegative.class);    
  9. else {    
  10. super    
  11. .addMessageEncoder(SendMessage.class,    
  12. SendMessageEncoder.class);    
  13. super.addMessageDecoder(ResultMessageDecoder.class);    
  14. }    
  15. }    
  16. }    

这个工厂类我们使用了构造方法的一个布尔类型的参数,以便其可以在Server 端、Client端同时使用。我们以Server 端为例,你可以看到调用两次addMessageDecoder()方法添加了1 号、2 号解码器,其实DemuxingProtocolDecoder 内部在维护了一个MessageDecoder数组,用于保存添加的所有的消息解码器,每次decode()的时候就调用每个MessageDecoder的decodable()方法逐个检查,只要发现一个MessageDecoder 不是对应的解码器,就从数组中移除,直到找到合适的MessageDecoder,如果最后发现数组为空,就表示没找到对应的MessageDecoder,最后抛出异常。
(9.)Server端:

[java]  view plain copy
  1. public class Server {    
  2. public static void main(String[] args) throws Exception {    
  3. IoAcceptor acceptor = new NioSocketAcceptor();    
  4. LoggingFilter lf = new LoggingFilter();    
  5. acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE,    
  6. 5);    
  7. acceptor.getFilterChain().addLast("logger", lf);    
  8. acceptor.getFilterChain().addLast("codec",    
  9. new ProtocolCodecFilter(new    
  10. MathProtocolCodecFactory(true)));    
  11. acceptor.setHandler(new ServerHandler());    
  12. acceptor.bind(new InetSocketAddress(9123));    
  13. }    
  14. }    

(10.)Server端使用的IoHandler:

[java]  view plain copy
  1. public class ServerHandler extends IoHandlerAdapter {    
  2. private final static Logger log = LoggerFactory    
  3. .getLogger(ServerHandler.class);    
  4. @Override    
  5. public void sessionIdle(IoSession session, IdleStatus status)    
  6. throws Exception {    
  7. session.close(true);    
  8. }    
  9. @Override    
  10. public void messageReceived(IoSession session, Object message)    
  11. throws Exception {    
  12. SendMessage sm = (SendMessage) message;    
  13. log.info("The message received is [ " + sm.getI() + " "    
  14. + sm.getSymbol() + " " + sm.getJ() + " ]");    
  15. ResultMessage rm = new ResultMessage();    
  16. rm.setResult(sm.getI() + sm.getJ());    
  17. session.write(rm);    
  18. }    
  19. }    

(11.)Client端:

[java]  view plain copy
  1. public class Client {    
  2. public static void main(String[] args) throws Throwable {    
  3. IoConnector connector = new NioSocketConnector();    
  4. connector.setConnectTimeoutMillis(30000);    
  5. connector.getFilterChain().addLast("logger"new    
  6. LoggingFilter());    
  7. connector.getFilterChain().addLast("codec",    
  8. new ProtocolCodecFilter(new    
  9. MathProtocolCodecFactory(false)));    
  10. connector.setHandler(new ClientHandler());    
  11. connector.connect(new InetSocketAddress("localhost"9123));    
  12. }    
  13. }    

(12.)Client端的IoHandler:

[java]  view plain copy
  1. public class ClientHandler extends IoHandlerAdapter {    
  2. private final static Logger LOGGER = LoggerFactory    
  3. .getLogger(ClientHandler.class);    
  4. @Override    
  5. public void sessionOpened(IoSession session) throws Exception {    
  6. SendMessage sm = new SendMessage();    
  7. sm.setI(100);    
  8. sm.setJ(99);    
  9. sm.setSymbol('+');    
  10. session.write(sm);    
  11. }    
  12. @Override    
  13. public void messageReceived(IoSession session, Object message) {    
  14. ResultMessage rs = (ResultMessage) message;    
  15. LOGGER.info(String.valueOf(rs.getResult()));    
  16. }    
  17. }    

你尝试改变(12.)中的红色代码中的正负号,会看到服务端使用了两个不同的解码器对其进行处理。

你可能感兴趣的:(mina框架)