Mina自定义编解码器

本文摘自《Mina用户指南》Chapter 9 - Codec Filter,但是用户指南中客户端没有完整版代码,在这里我简单的补上了。此文主要是为了下一篇使用Mina发送数据执行过程分析做准备。

实现内容: client端向server端发送请求(请求图片),server端向client端传输请求的图片。
准备工作:将Mina2.0.7 导入到项目中。

	<dependencies>
        <!-- https://mvnrepository.com/artifact/org.apache.mina/mina-core -->
        <dependency>
            <groupId>org.apache.mina</groupId>
            <artifactId>mina-core</artifactId>
            <version>2.0.7</version>
        </dependency>

    </dependencies>

第一步构架请求/响应消息实体。

  // 请求实体
  public class ImageRequest {

        private int width;
        private int height;
        private int numberOfCharacters;

        ImageRequest(int width, int height, int numberOfCharacters){
            this.width = width;
            this.height = height;
            this.numberOfCharacters = numberOfCharacters;
        }
        public int getWidth() {
            return width;
        }
        public int getHeight() {
            return height;
        }
        public int getNumberOfCharacters() {
            return numberOfCharacters;
        }
  }
    // 响应实体
  public class ImageResponse {
    private BufferedImage image1;
    private BufferedImage image2;
	public ImageResponse(BufferedImage image1, BufferedImage image2) {
        this.image1 = image1;
        this.image2 = image2;
    }
    public BufferedImage getImage1() {
        return image1;
    }
    public BufferedImage getImage2() {
        return image2;
    }
}

第二步,编写 编解码器。

public class ImageCodecFactory implements ProtocolCodecFactory {

    private ProtocolEncoder encoder;
    private ProtocolDecoder decoder;

    public ImageCodecFactory(boolean client) {
        if(client){
            encoder = new ImageRequestEncoder();       // 客户端编码器
            decoder = new ImageResponseDecoder();  // 客户端解码器
        } else {
            encoder = new ImageResponseEncoder(); // 服务端编码器
            decoder = new ImageRequestDecoder();   // 服务端解码器
        }
    }

    public ProtocolEncoder getEncoder(IoSession session) throws Exception {
        return encoder;
    }

    public ProtocolDecoder getDecoder(IoSession session) throws Exception {
        return decoder;
    }
}

public class ImageRequestEncoder extends ProtocolEncoderAdapter {

    public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
        ImageRequest request = (ImageRequest) message;
        IoBuffer buffer = IoBuffer.allocate(12, false);
        buffer.putInt(request.getWidth());
        buffer.putInt(request.getHeight());
        buffer.putInt(request.getNumberOfCharacters());
        buffer.flip();
        out.write(buffer);
     }

}
public class ImageRequestDecoder extends CumulativeProtocolDecoder {
    protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
        if (in.remaining() >= 12) {
            int width = in.getInt();
            int height = in.getInt();
            int numberOfCharachters = in.getInt();
            ImageRequest request = new ImageRequest(width, height, numberOfCharachters);
            out.write(request);
            return true;
        } else {
            return false;
        }
    }
}

public class ImageResponseDecoder extends CumulativeProtocolDecoder{

    private static final String DECODER_STATE_KEY = ImageResponseDecoder.class.getName() + ".STATE";

    public static final int MAX_IMAGE_SIZE = 5 * 1024 * 1024;

    private static class DecoderState {
        BufferedImage image1;
    }

 protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
        DecoderState decoderState = (DecoderState) session.getAttribute(DECODER_STATE_KEY);
        if (decoderState == null) {
            decoderState = new DecoderState();
            session.setAttribute(DECODER_STATE_KEY, decoderState);
        }
        if (decoderState.image1 == null) {
            // try to read first image
            if (in.prefixedDataAvailable(4, MAX_IMAGE_SIZE)) {
                decoderState.image1 = readImage(in);
            } else {
                // not enough data avaliable to read first image
                return false;
            }
        }
        if (decoderState.image1 != null) {
            // try to read second image
            if (in.prefixedDataAvailable(4, MAX_IMAGE_SIZE)) {
                BufferedImage image2 = readImage(in);
                ImageResponse imageResponse = new ImageResponse(decoderState.image1, image2);
                out.write(imageResponse);
                decoderState.image1 = null;
                return true;
            } else {
                // not enough data avaliable to read second image
                return false;
            }
        }
        return false;
    }

    private BufferedImage readImage(IoBuffer in) throws IOException {
        int length = in.getInt();
        byte[] bytes = new byte[length];
        in.get(bytes);
        ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
        return ImageIO.read(bais);
    }
}


public class ImageResponseEncoder extends ProtocolEncoderAdapter {
    public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {
        // 传输了一个对象消息
        ImageResponse imageResponse = (ImageResponse) message;
        byte[] bytes1 = getBytes(imageResponse.getImage1());
        byte[] bytes2 = getBytes(imageResponse.getImage2());
        int capacity = bytes1.length + bytes2.length + 8;
        IoBuffer buffer = IoBuffer.allocate(capacity, false);
        buffer.setAutoExpand(true);
        buffer.putInt(bytes1.length);
        buffer.put(bytes1);
        buffer.putInt(bytes2.length);
        buffer.put(bytes2);
        buffer.flip();
        out.write(buffer);
    }

    private byte[] getBytes(BufferedImage image) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(image, "JPG", baos);
        return baos.toByteArray();
    }
}


关于其中的 校验功能prefixedDataAvailable源码如下:

@Override
    public boolean prefixedDataAvailable(int prefixLength, int maxDataLength) {
        if (remaining() < prefixLength) {
            return false;
        }

        int dataLength;
        switch (prefixLength) {
        case 1:
            dataLength = getUnsigned(position());
            break;
        case 2:
            dataLength = getUnsignedShort(position());
            break;
        case 4:
            dataLength = getInt(position());   // 上文中使用的是4 ,
            break;
        default:
            throw new IllegalArgumentException("prefixLength: " + prefixLength);
        }

        if (dataLength < 0 || dataLength > maxDataLength) {
            throw new BufferDataException("dataLength: " + dataLength);
        }

        return remaining() - prefixLength >= dataLength;
    }

第三步 编写消息处理器

public class ImageClientIoHandler extends IoHandlerAdapter {

    private ImageListener imageListener;
    public ImageClientIoHandler( ImageListener imageListener) {
        this.imageListener = imageListener;
    }

    @Override
    public void messageReceived(IoSession session, Object message) throws Exception {
        ImageResponse response = (ImageResponse) message;
        imageListener.onImages(response.getImage1(), response.getImage2());
    }

    public void sendRequestMessage(IoSession session){
        // 发送请求
        ImageRequest imageRequest = new ImageRequest(200,200, 10);
        session.write(imageRequest);
    }

}

public class ImageServerIoHandler extends IoHandlerAdapter {
    private final static String characters = "mina rocks abcdefghijklmnopqrstuvwxyz0123456789";
    public static final String INDEX_KEY = ImageServerIoHandler.class.getName() + ".INDEX";
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    @Override
    public void sessionOpened(IoSession session) throws Exception {
        session.setAttribute(INDEX_KEY, 0);
    }
    @Override
    public void messageReceived(IoSession session, Object message) throws Exception {
        ImageRequest request = (ImageRequest) message;
        String text1 = generateString(session, request.getNumberOfCharacters());
        String text2 = generateString(session, request.getNumberOfCharacters());
        BufferedImage image1 = createImage(request, text1);
        BufferedImage image2 = createImage(request, text2);
        ImageResponse response = new ImageResponse(image1, image2);
        session.write(response);
    }

    private BufferedImage createImage(ImageRequest request, String text) {
        BufferedImage image = new BufferedImage(request.getWidth(), request.getHeight(), BufferedImage.TYPE_BYTE_INDEXED);
        Graphics graphics = image.createGraphics();
        graphics.setColor(Color.YELLOW);
        graphics.fillRect(0, 0, image.getWidth(), image.getHeight());
        Font serif = new Font("serif", Font.PLAIN, 30);
        graphics.setFont(serif);
        graphics.setColor(Color.BLUE);
        graphics.drawString(text, 10, 50);
        return image;
    }

    private String generateString(IoSession session, int length) {
        Integer index = (Integer) session.getAttribute(INDEX_KEY);
        StringBuffer buffer = new StringBuffer(length);

        while (buffer.length() < length) {
            buffer.append(characters.charAt(index));
            index++;
            if (index >= characters.length()) {
                index = 0;
            }
        }
        session.setAttribute(INDEX_KEY, index); // 因为有两张图片,所以这里需要记录一下上次位置。
        return buffer.toString();
    }
}


第四步 编写程序入口

public class ImageServer {
    public static final int PORT = 33789;
    public static final String HOST = "127.0.0.1";

    public static void main(String[] args) throws IOException {
        ImageServerIoHandler handler = new ImageServerIoHandler();
        NioSocketAcceptor acceptor = new NioSocketAcceptor();
        acceptor.getFilterChain().addLast("protocol", new ProtocolCodecFilter(new ImageCodecFactory(false)));
        acceptor.setDefaultLocalAddress(new InetSocketAddress(HOST, PORT));
        acceptor.setHandler(handler);
        acceptor.bind();
        System.out.println("server IP " + acceptor.getLocalAddress());
        System.out.println("server is listenig at port " + PORT);
    }
}



public class ImageClient {

    public static void main(String[] args) throws InterruptedException {
        String host = "127.0.0.1";
        int port = 33789;
        ImageListener imageListener = new ImageListener();
        ImageClientIoHandler clientIoHandler = new ImageClientIoHandler(imageListener);
        SocketConnector connector = new NioSocketConnector();

        connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ImageCodecFactory(true)));
        connector.setHandler(clientIoHandler);
        connector.setDefaultRemoteAddress(new InetSocketAddress(host, port));
        // 连接到传入的目标PC
        ConnectFuture connect = connector.connect();
        connect.awaitUninterruptibly(); // 等待连接创建完成, Mina 是基于NIO同步非阻塞
        IoSession session = connect.getSession();
        System.out.println(connect.isConnected());
        clientIoHandler.sendRequestMessage(session);
    }
}

附:创建图片以及浏览器查看图片实现。

public class ImageListener {
    public void onImages(BufferedImage image1, BufferedImage image2) throws IOException {
        // 为了方便效果的查看,调用浏览器访问保存目录
        // 先保存
        File file1 = new File("image1.jpg");
        File file2 = new File("image2.jpg");
        ImageIO.write(image1, "jpg", file1);
        ImageIO.write(image2, "jpg", file2);
        // 启动浏览器,打开文件目录
        String chromPath = "D:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe";   // 本机chrom.exe的位置,其他浏览器也可以
        String imagesPath = file1.getAbsolutePath();
        String target = "file:///" + imagesPath;
        Runtime.getRuntime().exec(new String[]{chromPath, target});
        System.out.println("please go to default browser");
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值