自定义Mina编解码器

本文介绍了在Mina框架下如何自定义编解码器,以实现网络数据的二进制与JAVA对象之间的转换。通过ProtocolCodecFactory接口创建ProtocolEncoder和ProtocolDecoder,用于编码和解码。示例中详细展示了自定义的LCLTextLineCodecFactory、LCLTextLineCodecDecoder和LCLTextLineCodecEncoder的实现,涉及文本换行符的匹配与处理。

协议编解码器是在使用Mina 的时候最需要关注的对象,因为网络传输的数据都是二进制数据(byte),而在程序中面向的是JAVA 对象,这就需要在发送数据时将JAVA 对象编码二进制数据,接收数据时将二进制数据解码为JAVA 对象。

编解码器同样是以过滤器的形式安插在过滤器链上,如下所示:

// 设置过滤器(使用Mina提供的文本换行符编解码器)

acceptor.getFilterChain().addLast(

     "codec",

     newProtocolCodecFilter(new TextLineCodecFactory(Charset

           .forName("UTF-8"),

           LineDelimiter.WINDOWS.getValue(),

           LineDelimiter.WINDOWS.getValue())));

协议编解码器是通过ProtocolCodecFilter过滤器构造的,看它的构造方法,它需要一个ProtocolCodecFactory对象:

publicProtocolCodecFilter(ProtocolCodecFactory factory) {

        if (factory == null){

            throw newNullPointerException("factory");

        }

        this.factory =factory;

}

ProtocolCodecFactory接口非常直接,通过ProtocolEncoder和

ProtocolDecoder对象来构建!

public interfaceProtocolCodecFactory {

    /**

     * Returns a new (orreusable) instance of {@link ProtocolEncoder} which

     * encodes message objectsinto binary or protocol-specific data.

     */

    ProtocolEncodergetEncoder(IoSession session) throws Exception;

 

    /**

     * Returns a new (orreusable) instance of {@link ProtocolDecoder} which

     * decodes binary orprotocol-specific data into message objects.

     */

    ProtocolDecodergetDecoder(IoSession session) throws Exception;

}

ProtocolEncoder和ProtocolDecoder接口是Mina负责编码和解码的顶级接口!

编码和解码的前提就是协议的制定:比如上面我们使用的Mina自带的根据文本换行符解码的TextLineCodecFactory(),如果遇到文本换行符就开始编解码!


package lcl.mina.demo2;


import java.nio.charset.Charset;


import org.apache.mina.common.IoSession;

import org.apache.mina.filter.codec.ProtocolCodecFactory;

import org.apache.mina.filter.codec.ProtocolDecoder;

import org.apache.mina.filter.codec.ProtocolEncoder;


import lcl.mina.demo2.LCLTextLineCodecDecoder;

import lcl.mina.demo2.LCLTextLineCodecEncoder;


public class LCLTextLineCodecFactory implements ProtocolCodecFactory {


private Charset charset; // 编码格式


private String delimiter; // 文本分隔符


public LCLTextLineCodecFactory(Charset charset, String delimiter) {

this.charset = charset;

this.delimiter = delimiter;

}


//读取信息执行

public ProtocolDecoder getDecoder(IoSession session) throws Exception {

return new LCLTextLineCodecDecoder(charset, delimiter);

}


//发送编写信息执行

public ProtocolEncoder getEncoder(IoSession session) throws Exception {

return new LCLTextLineCodecEncoder(charset, delimiter);

}


}


package lcl.mina.demo2;


import java.nio.charset.CharacterCodingException;

import java.nio.charset.Charset;

import java.nio.charset.CharsetDecoder;


import org.apache.mina.common.IoBuffer;

import org.apache.mina.common.IoSession;

import org.apache.mina.filter.codec.ProtocolDecoder;

import org.apache.mina.filter.codec.ProtocolDecoderOutput;


public class LCLTextLineCodecDecoder implements ProtocolDecoder {


private Charset charset; // 编码格式


private String delimiter; // 文本分隔符


private IoBuffer delimBuf; // 文本分割符匹配的变量


// 定义常量值,作为每个IoSession中保存解码任务的key值

private static String CONTEXT = LCLTextLineCodecDecoder.class.getName()

+ ".context";


// 构造函数,必须指定Charset和文本分隔符

public LCLTextLineCodecDecoder(Charset charset, String delimiter) {

this.charset = charset;

this.delimiter = delimiter;

}


public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)

throws Exception {

Context ctx = getContext(session);

if (delimiter == null || "".equals(delimiter)) { // 如果文本换行符未指定,使用默认值

delimiter = "\r\n";

}

if (charset == null) {

charset = Charset.forName("utf-8");

}

decodeNormal(ctx, in, out);

}


// 从IoSession中获取Context对象

private Context getContext(IoSession session) {

Context ctx;

ctx = (Context) session.getAttribute(CONTEXT);

if (ctx == null) {

ctx = new Context();

session.setAttribute(CONTEXT, ctx);

}

return ctx;

}


// 解码

private void decodeNormal(Context ctx, IoBuffer in,

ProtocolDecoderOutput out) throws CharacterCodingException {

// 取出未完成任务中已经匹配的文本换行符的个数

int matchCount = ctx.getMatchCount();

// 设置匹配文本换行符的IoBuffer变量

if (delimBuf == null) {

            IoBuffer tmp = IoBuffer.allocate(2).setAutoExpand(true);

            tmp.putString(delimiter, charset.newEncoder());

            tmp.flip();

            delimBuf = tmp;

        }

int oldPos = in.position(); // 解码的IoBuffer中数据的原始信息

        int oldLimit = in.limit();

        while (in.hasRemaining()) { // 变量解码的IoBuffer

            byte b = in.get();

            if (delimBuf.get(matchCount) == b) { // 匹配第matchCount位换行符成功

                matchCount++;               

                if (matchCount == delimBuf.limit()) { // 当前匹配到字节个数与文本换行符字节个数相同,匹配结束

                    int pos = in.position();   // 获得当前匹配到的position(position前所有数据有效)

                    in.limit(pos);

                    in.position(oldPos);   // position回到原始位置


                    ctx.append(in);   // 追加到Context对象未完成数据后面


                    in.limit(oldLimit); // in中匹配结束后剩余数据

                    in.position(pos);


                    IoBuffer buf = ctx.getBuf();

                    buf.flip();

                    buf.limit(buf.limit() - matchCount);// 去掉匹配数据中的文本换行符

                    try {

                        out.write(buf.getString(ctx.getDecoder())); // 输出解码内容

                    } finally {

                        buf.clear(); // 释放缓存空间

                    }


                    oldPos = pos;

                    matchCount = 0;

                }

            } else {

            // 如果matchCount==0,则继续匹配

            // 如果matchCount>0,说明没有匹配到文本换行符的中的前一个匹配成功字节的下一个字节,

            // 跳转到匹配失败字符处,并置matchCount=0,继续匹配

                in.position(in.position()-matchCount);

                matchCount = 0;  // 匹配成功后,matchCount置空

            }

        }

        

        // 把in中未解码内容放回buf

        in.position(oldPos);

        ctx.append(in);


        ctx.setMatchCount(matchCount);

}


public void dispose(IoSession session) throws Exception {


}


public void finishDecode(IoSession session, ProtocolDecoderOutput out)

throws Exception {

}


// 内部类,保存IoSession解码时未完成的任务

private class Context {

private CharsetDecoder decoder;

private IoBuffer buf; // 保存真实解码内容

private int matchCount = 0; // 匹配到的文本换行符个数


private Context() {

decoder = charset.newDecoder();

buf = IoBuffer.allocate(80).setAutoExpand(true);

}


// 重置

public void reset() {

matchCount = 0;

decoder.reset();

}


// 追加数据

public void append(IoBuffer in) {

getBuf().put(in);

}


// ======get/set方法=====================

public CharsetDecoder getDecoder() {

return decoder;

}


public IoBuffer getBuf() {

return buf;

}


public int getMatchCount() {

return matchCount;

}


public void setMatchCount(int matchCount) {

this.matchCount = matchCount;

}

} // end class Context;


}


package lcl.mina.demo2;


import java.nio.charset.Charset;


import org.apache.mina.common.IoBuffer;

import org.apache.mina.common.IoSession;

import org.apache.mina.filter.codec.ProtocolEncoder;

import org.apache.mina.filter.codec.ProtocolEncoderOutput;


public class LCLTextLineCodecEncoder implements ProtocolEncoder {


private Charset charset; // 编码格式


private String delimiter; // 文本分隔符


public LCLTextLineCodecEncoder(Charset charset, String delimiter) {

this.charset = charset;

this.delimiter = delimiter;

}


public void encode(IoSession session, Object message,

ProtocolEncoderOutput out) throws Exception {

if (delimiter == null || "".equals(delimiter)) { // 如果文本换行符未指定,使用默认值

delimiter = "\r\n";

}

if (charset == null) {

charset = Charset.forName("utf-8");

}


String value = message.toString();

IoBuffer buf = IoBuffer.allocate(value.length()).setAutoExpand(true);

buf.putString(value, charset.newEncoder()); // 真实数据

buf.putString(delimiter, charset.newEncoder()); // 文本换行符

buf.flip();

out.write(buf);

}


public void dispose(IoSession session) throws Exception {

}


}



评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值