初识mina

来源:互联网 发布:oppor11网络模式在哪 编辑:程序博客网 时间:2024/06/06 03:14
    最近在看郭霖大神的慕课网视频学习mina框架,把学习的情况记录下来,方便以后查阅。
    网上关于mina详细介绍很多,我就不班门弄斧了。一边看视频一边慢慢更新。
    这次把注释放到代码里面,更加细致。
    官网下载mina的jar包,先只导入mina-core-x.x.x.jar和slf4j-api-x.x.x.jar(mina日志打印包,不导入会报错)。
    使用mina最基本的功能只需要四步。

主函数类

public class MinaServer {    public static void main(String[] args) {        try {            NioSocketAcceptor acceptor = new NioSocketAcceptor();            /*             * 自己定义Handler对象来处理消息             */            acceptor.setHandler(new MyServerHandler());            /*             * 获取拦截器来过滤信息             * 添加新的拦截器             * MyTextLineFactory()是自己定义的拦截器             */            acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MyTextLineFactory()));            /*             * 定义idle,即设置连接多久无消息收发为进入空闲状态             * 第一个参数IdleStatus.BOTH_IDLE为多久没有读取且没有收到向客户端发送消息             * 第二个参数是设置时间,单位是秒。每隔60秒再次输出表示你又进入空闲状态             */            acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 60);            /*             * 绑定一个端口             */            acceptor.bind(new InetSocketAddress(9090));        } catch (Exception e) {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}

自定义MyserverHandler类

public class MyServerHandler extends IoHandlerAdapter {    /**     * 网络异常的时候调用方法     */    @Override    public void exceptionCaught(IoSession session, Throwable cause)            throws Exception {        System.out.println("exceptionCaught");    }    /**     * 接收到消息的时候调用方法     */    @Override    public void messageReceived(IoSession session, Object message)            throws Exception {        String s = (String)message;        System.out.println("messageReceived : " + s);    }    /**     * 发出消息的时候调用方法     */    @Override    public void messageSent(IoSession session, Object message) throws Exception {        System.out.println("messageSent");    }    /**     * 客户端会话关闭时调用的方法     */    @Override    public void sessionClosed(IoSession session) throws Exception {        System.out.println("sessionClosed");    }    /**     * 客户端会话开启的时候调用的方法     */    @Override    public void sessionCreated(IoSession session) throws Exception {        System.out.println("sessionCreated");    }    /**     * 客户端进入空闲状态的时候调用的方法     */    @Override    public void sessionIdle(IoSession session, IdleStatus status)            throws Exception {        System.out.println("sessionIdle");    }    /**     * 会话打开的时候调用的方法     */    @Override    public void sessionOpened(IoSession session) throws Exception {        System.out.println("sessionOpened");    }}
继承于IoHandlerAdapter类,实现里面的几个方法,将log打印出来、

自定义拦截器来拦截收发消息

public class MyTextLineFactory implements ProtocolCodecFactory {    //自定义加码类    private MyTextLineEncoder mEncoder;    //自定义解码类    private MyTextLineDecoder mDecoder;    //自定义防止数据丢失解码类    private MyTextLineCumulativeDecoder mCumulativeDecoder;    public MyTextLineFactory() {        mEncoder = new MyTextLineEncoder();        mDecoder = new MyTextLineDecoder();        mCumulativeDecoder = new MyTextLineCumulativeDecoder();    }    public ProtocolDecoder getDecoder(IoSession arg0) throws Exception {        //返回的是防丢失的Decoder        return mCumulativeDecoder;    }    public ProtocolEncoder getEncoder(IoSession arg0) throws Exception {        return mEncoder;    }}
    你可以用mina中定义的,不过为了适应自己的需要,还是自己定义比较好。
    新建一个MyTextLineFactory继承于ProtocolCodecFactory。
    在MyTextLineFactory中,使用了自定义的加解码器来实现收发数据的加解码。

自定义编码类来给数据加码

public class MyTextLineEncoder implements ProtocolEncoder {    public void dispose(IoSession arg0) throws Exception {    }    public void encode(IoSession session, Object message, ProtocolEncoderOutput out)            throws Exception {        String s = null;        if(message instanceof String) {            s = (String)message;        }        //开始转码操作        if(s != null) {            /*             * 将字符串进行编码,采取系统默认的编码方式             * 为了提高效率(因为每一次都要获取系统默认的编码方式),这里采取一个判断             *              * 类似于ListView的ViewHoder             * 如果session中没有获取到属性 encoder,那么就创建一个为系统默认编码的加密方式             * 如果有的话就使用之前就有的,这样的方法可以提高运行效率             */            CharsetEncoder charsetEncoder = (CharsetEncoder)session.getAttribute("encoder");            if(charsetEncoder == null) {                charsetEncoder = Charset.defaultCharset().newEncoder();                session.setAttribute("encoder", charsetEncoder);            }            //mina框架中开辟内存            IoBuffer ioBuffer = IoBuffer.allocate(s.length());            //设置内存可以自动开辟内存            ioBuffer.setAutoExpand(true);            //设置put数据类型为系统本身的格式            ioBuffer.putString(s,charsetEncoder);            ioBuffer.flip();            //写出数据            out.write(ioBuffer);        }    }}

最后定义解码器,将收到的字节码转化为字符串

public class MyTextLineCumulativeDecoder extends CumulativeProtocolDecoder{    /**     * 当且仅当数据读取完的时候返回true     * 如果没有读取完想要下次再进行读取的时候就返回false     */    @Override    protected boolean doDecode(IoSession session, IoBuffer ioBuffer,            ProtocolDecoderOutput out) throws Exception {        //开始读取的位置        int startPosition = ioBuffer.position();        //判断ioBuffer里面是否还有字节可以读取        while(ioBuffer.hasRemaining()) {            byte b = ioBuffer.get();            if(b == '\n') {                //读取到 '\n'的时候的位置记录下来                int currentPosition = ioBuffer.position();                //当前的总长度                int limit = ioBuffer.limit();                //把初始位置定在字符串开始的第一个字节上                ioBuffer.position(startPosition);                //limit是终点的的位置,这里就是读到currentPosition的地方                //从startPosition到currentPosition位置,就是读取到'\n'的总长度                ioBuffer.limit(currentPosition);                //截取从startPosition位置到currentPosition位置的所有字节,返回的是一个IoBuffer                IoBuffer sliceIoBuffer = ioBuffer.slice();                //把字节码转换成字符串                byte [] dest = new byte[sliceIoBuffer.limit()];                sliceIoBuffer.get(dest);                String str = new String(dest);                //写出数据                out.write(str);                /*                 * 因为在之间重定位在startPosition的位置,如果我们不重新定位到当前位置的话,                 * 就会一直从 startPosition -> currentPosition不停的循环导致死循环                 * 这里要把开始位置定义到currentPosition                 */                ioBuffer.position(currentPosition);                ioBuffer.limit(limit);                return true;            }        }        //如果没有读取完就要把position重新读取到开始的位置        ioBuffer.position(startPosition);        return false;    }}
    这里选择继承的是CumulativeProtocalDecoder类,这个类和ProtocalDecoder的区别在于decode方法有一个返回值,这个返回值是在循环读取的时候判断是否读取完成整个字节。如果是完全读取就返回一个true。否则就返回一个false,并且将position定位在开始的位置重新读取。
    以上是服务器端的代码,下面是客户端的代码。

    写一些简简单单测试的代码测试是否能联通。
public class MinaClient {    public static void main(String[] args) throws Exception{        NioSocketConnector connector = new NioSocketConnector();        connector.setHandler(new MyClientHandler());        connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory()));        ConnectFuture future = connector.connect(new InetSocketAddress("127.0.0.1",9090));        //阻塞连接,直到连接上服务器        future.awaitUninterruptibly();        //当连接上后会返回一个IoSession对象,有这个对象可以完成许多事情        IoSession ioSession = future.getSession();        //获取从控制台输入的流        BufferedReader readIn = new BufferedReader(new InputStreamReader(System.in));        String inputContent;        //当不输入bye的时候就发送输出的内容        while(!(inputContent = readIn.readLine()).equals("bye")) {            ioSession.write(inputContent);        }    }}
首先还是导入那两个包
客户端的代码和服务端的代码差不多。从控制台输出数据,服务器能够接受到数据。
其中new MyClientHandler()中的代码与服务器中的MyServerHandler是一样的,只是改了名字而已。
这样就完成了一个简单的客户端和服务器连接的例子







1 0
原创粉丝点击