APACHE MINA之自定义CODEC(编码/解码)

理论知识(个人见解)

要使用MINA搭建一个最基本的自定义协议服务器, 核心在于编码和解码, 也就是Filter. 

在网络通讯中, 收发的都是字节流(byte[])

接收数据流时, 在这些数据变得可被处理(理解)之前, 要经过Filter的处理, 即解码

反之亦反, 发送数据时, 也要经过Filter的处理,  最终变成字节流, 即编码

在MINA中, Filter是以链的方式存储, 在收发数据时都有机会去编码/解码数据

一个Filter具备了处理一种数据类型的能力, 举个栗子

假设有三个Filter, 分别对应 1.打包/解包 2.压缩/解压缩 3.序列化/反序列化

在接收数据时, 先解包, 再解压缩, 最后反序列化, 交给Handler进行业务逻辑处理

在发送数据时, 先序列化, 再压缩, 再打包, 交给MINA把数据送出去

那么如何辨别哪种数据要交给哪个Filter处理?

本人理解的是: MINA会顺序调用这些Filter, 直到对象类型是被期望的

1.接收数据时, 数据经过层层Filter, 最终到达Handler. 

  在接收数据时可以没有Filter去解码数据, 最终还会调用Handler, 但这肯定会引起异常

2.发送数据时, 数据经过层层Filter, 最终变成MINA期望的IoBuffer类型, 发送出去. 

  除非Handler发送的就是IoBuffer类型, 不会调用任何Filter编码

  否则在经过Filter的处理后必须转换成IoBuffer类型, 若不是, MINA报异常

3. IoBuffer可以理解为ByteArray的再封装

说明示例

实现一个服务器

能够和telnet客户端交互, 规定交互的内容必须是数字, 服务器应答此数字乘以2是多少

那么就有以下几个Filter

1.IoBuffer <--> byte[]

2. byte[] <--> String

3. String <--> Long

对于业务层的Handler来说, 它收到的和发送的都是数值型(Long), 转换过程交由Filter处理, 是透明的

为了避免一些问题, 1和2用MINA自带的TextLineCodec来处理

先看看逻辑处理层

class MyIoHandler extends IoHandlerAdapter {
    // 对于Handler来说
    // 收到的和发送出去的都是Long类型
    // 中间的数据转换一概不关心, 全部交由Filter处理

    @Override
    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
        cause.printStackTrace();
    }

    @Override
    public void messageReceived(IoSession session, Object message) throws Exception {
        // 有数据到来时调用
        Long input = (Long)message;

        session.write(input * 2);
    }

    @Override
    public void messageSent(IoSession session, Object message) throws Exception {
        // 数据发送出去后调用此方法, 这里不作处理
    }

    @Override
    public void sessionOpened(IoSession session) throws Exception {
        // 客户端连接时调用
        session.write("0"); // 想一下为什么可以发送字符串
    }

    @Override
    public void sessionClosed(IoSession session) throws Exception {
        // 客户端断开时调用
    }
}


很简单, 看看注释即可, 功能就是把收到的数字乘以2再返回给客户端

TextLineCodec可以在官方源码里找到, 具有缓冲/判断一条信息完整等功能

下面来看自定义Filter的实现

class MyFilter extends IoFilterAdapter {
    // Long <--> String

    @Override
    public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
        Object message = writeRequest.getMessage();

        if (message instanceof Long) {
            Long num = (Long)message;

            nextFilter.filterWrite(session, new DefaultWriteRequest(num.toString(), writeRequest.getFuture(), writeRequest.getDestination()));
            return;
        }
        nextFilter.filterWrite(session, writeRequest);
    }

    @Override
    public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {

        if (message instanceof String) {
            String str = (String)message;

            nextFilter.messageReceived(session, Long.parseLong(str));
            return;
        }
        nextFilter.messageReceived(session, message);
    }

    @Override
    public void messageSent(NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
        Object message = writeRequest.getMessage();

        if (message instanceof String) {
            String str = (String)message;

            nextFilter.messageSent(session, new DefaultWriteRequest(Long.parseLong(str), writeRequest.getFuture(), writeRequest.getDestination()));
            return;
        }
        nextFilter.messageSent(session, writeRequest);
    }
}


也不难, 从String到Long的相互转换

最后附上main函数

public class Server {

    public static void main(String argv[]) {
        new Server().Start();
    }

    public Server() {

    }

    public void Start() {
        IoAcceptor acceptor = new NioSocketAcceptor();

        acceptor.getFilterChain().addLast("buffer2string", new ProtocolCodecFilter(new TextLineCodecFactory()));
        acceptor.getFilterChain().addLast("string2long", new MyFilter());
        acceptor.setHandler(new MyIoHandler());
        try {
            acceptor.bind(new InetSocketAddress(8989));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}


理解和经验还需要自己体会

接收数据时: 字节流-->TextLineCodec-->MyFilter-->MyIoHandler

发送数据时: MyIoHandler-->MyFilter-->TextLineCodec-->字节流

细心的朋友会发现代码中发送了一个字符串"0", 也是可以的

这是因为即使不符合MyFilter处理的类型, 也会传递给下一个Filter, 而TextLineCodec可以处理

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值