使用Mina框架实现C/S通讯

什么是Mina?

Apache MINA is a network application framework which helps users develop high performance and high scalability network applications easily. It provides an abstract event-driven asynchronous API over various transports such as TCP/IP and UDP/IP via Java NIO.

Apache MINA是一个网络应用框架,可以帮助我们开发高性能和高扩展性的网络应用。它通过封装Java NIO提供了一个支持各种传输协议(如:TCP/IP和UDP/IP)的抽象事件驱动异步API。


NIO framework library,

client server framework library, or

a networking socket library

Mina是对Java NIO框架进行了一层封装的Socket库。


Apache MINA comes with many subprojects :

Asyncweb : An HTTP server build on top of MINA asynchronous framework

FtpServer : A FTP server

SSHd : A Java library supporting the SSH protocol

Vysper : An XMPP server

Apache MINA自带了许多子项目:

异步http服务

ftp服务

一个支持ssh协议的java库

XMPP服务

Mina框架主页:

https://mina.apache.org/

Mina框架下载地址:

https://mina.apache.org/downloads-mina.html

为什么使用Mina?

传统socket:阻塞式通信

每建立一个Socket连接时,同时创建一个新线程对该Socket进行单独通信(采用阻塞的方式通信)。这种方式具有很高的响应速度,并且控制起来也很简单,在连接数较少的时候非常有效,但是如果对每一个连接都产生一个线程的无疑是对系统资源的一种浪费,如果连接数较多将会出现资源不足的情况。

nio:非阻塞通信

nio设计背后的基石:反应器模式,用于事件多路分离和分派的体系结构模式。

反应器模式的核心功能如下:

将事件多路分用

将事件分派到各自相应的事件处理程序

NIO 的非阻塞 I/O 机制是围绕 选择器和 通道构建的。Channel 类表示服务器和客户机之间的一种通信机制。Selector 类是 Channel 的多路复用器。 Selector 类将传入客户机请求多路分用并将它们分派到各自的请求处理程序。

通道(Channel 类):表示服务器和客户机之间的一种通信机制。

选择器(Selector类):是 Channel 的多路复用器。

Selector 类将传入的客户机请求多路分用并将它们分派到各自的请求处理程序。

简单的来说:NIO是一个基于事件的IO架构。

最基本的思想就是:有事件我通知你,你再去做你的事情。而且NIO的主线程只有一个,不像传统的模型,需要多个线程以应对客户端请求,也减轻了JVM的工作量。

当Channel注册至Selector以后,经典的调用方法如下:nio中取得事件通知,就是在selector的select事件中完成的。在selector事件时有一个线程向操作系统询问,selector中注册的Channel&&SelectionKey的键值对的各种事件是否有发生,如果有则添加到selector的selectedKeys属性Set中去,并返回本次有多少个感兴趣的事情发生。如果发现这个值>0,表示有事件发生,马上迭代selectedKeys中的SelectionKey,根据Key中的表示的事件,来做相应的处理。实际上,这段说明表明了异步socket的核心,即异步socket不过是将多个socket的调度(或者还有他们的线程调度)全部交给操作系统自己去完成,异步的核心Selector,不过是将这些调度收集、分发而已。

参考文章地址:http://lcllcl987.iteye.com/blog/70703

使用Mina框架实现C/S通讯

Mina快速入门

https://mina.apache.org/mina-project/quick-start-guide.html

Mina API文档

https://mina.apache.org/mina-project/apidocs/index.html

在工程中包含以下两个jar

客户端代码

package linchaolong.mina.client;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;

// Client Socket
public class Client {

    private boolean _isExit = false;

    public static void main(String[] args) {
        Client client = new Client();
        client.start();
    }

    private void start() {

        BufferedReader in = null;
        try {
            final Socket socket = new Socket("127.0.0.1", 9999);
            final BufferedReader reader = new BufferedReader(
                    new InputStreamReader(socket.getInputStream()));
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
                    socket.getOutputStream()));

            // 创建一个线程用于接收消息
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        while( !_isExit ){
                            String line = reader.readLine();
                            System.out.println("from server : " + line);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();

            // 从控制台输入消息,发送到服务端
            in = new BufferedReader(new InputStreamReader(System.in));
            String line = null;
            while (!"bye".equals((line = in.readLine()))) {
                writer.write(line);
                writer.newLine();
                writer.flush();
            }

            _isExit = true;
            in.close();
            writer.close();
            reader.close();

        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            _isExit = true;
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

服务端代码

package linchaolong.mina.server;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import java.util.Timer;
import java.util.TimerTask;
import linchaolong.mina.client.MyIoHandler;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

public class MinaServer {

    public static void main(String[] args) {
        MinaServer server = new MinaServer();
        server.start();
    }

    private void start() {
        try {
            final NioSocketAcceptor acceptor = new NioSocketAcceptor();

            //  消息的传送会经过一系列拦截器
            // 添加自定义的拦截器
            acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
            acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" )))); // 字符编码

            // 设置会话管理类
            acceptor.setHandler(new MyIoHandler());

            // 设置read buff size(字节)
            acceptor.getSessionConfig().setReadBufferSize( 2048 ); // buff是可自动扩展的,但设置一个合适值,效率会较高。
            // 设置空闲时间(单位:秒,会话空闲时会调用IoHandler的sessionIdle方法,BOTH_IDLE:读和写,READER_IDLE:读,WRITER_IDLE:写)
            acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);

            // 监听一个端口
            acceptor.bind(new InetSocketAddress(9999));

            new Timer().schedule(new TimerTask() {
                @Override
                public void run() {
                    // 发送一个广播,所以会话都能接受到
                    acceptor.broadcast("Hello All.");
                }
            }, 10000);

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

会话管理类

package linchaolong.mina.client;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Date;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;

public class MyIoHandler extends IoHandlerAdapter {

    // 会话创建
    @Override
    public void sessionCreated(IoSession session) throws Exception {
        super.sessionCreated(session);
        // 获取客户端ip
        InetSocketAddress socketAddress = (InetSocketAddress) session.getRemoteAddress();
        InetAddress inetAddress = socketAddress.getAddress();
        System.out.println("sessionCreated  id=" + session.getId() + " , ip=" + inetAddress.getHostAddress());
    }

    // 会话打开
    @Override
    public void sessionOpened(IoSession session) throws Exception {
        super.sessionOpened(session);
        System.out.println("sessionOpened");
    }

    // 会话关闭
    @Override
    public void sessionClosed(IoSession session) throws Exception {
        super.sessionClosed(session);
        System.out.println("sessionClosed");
    }

    // 会话等待
    @Override
    public void sessionIdle(IoSession session, IdleStatus status)
            throws Exception {
        super.sessionIdle(session, status);
        System.out.println( "IDLE " + session.getIdleCount( status ));
    }

    // 会话异常
    @Override
    public void exceptionCaught(IoSession session, Throwable cause)
            throws Exception {
        super.exceptionCaught(session, cause);
        cause.printStackTrace();
    }

    // 接受消息
    @Override
    public void messageReceived(IoSession session, Object message)
            throws Exception {
        // 读取客户端消息
        String str = message.toString();
        System.out.println("from session " + session.getId() + " : " + str);
        if (str.trim().equalsIgnoreCase("quit")) {
            session.close(true);
            return;
        }
        // 给客户端返回数据
        session.write(new Date().toString());
    }

    // 发送消息
    @Override
    public void messageSent(IoSession session, Object message) throws Exception {
        super.messageSent(session, message);
        System.out.println("messageSent : " + message);
    }

    // 关闭输入流
    @Override
    public void inputClosed(IoSession session) throws Exception {
        super.inputClosed(session);
        System.out.println("inputClosed");
    }
}

过滤器(Filter)

过滤器的主要作用就是将协议逻辑从业务逻辑中分离出来。

Codec Filter

https://mina.apache.org/mina-project/userguide/ch9-codec-filter/ch9-codec-filter.html

大多数网络应用程序需要一种方式来找出当前消息结束和下一个消息开始的地方。

你可以实现这一切的逻辑在你的IoHandler,但加入了ProtocolCodecFilter加入将会使你的代码更清洁,更容易维护。

它允许您将协议逻辑从业务逻辑(IoHandler)中分离出来。

1.创建一个工厂类,实现ProtocolCodecFactory接口

package linchaolong.mina.server.filter;

import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFactory;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolEncoder;

// 工厂类
public class MyTextLineCodecFactory implements ProtocolCodecFactory {

    private final MyTextLineEncoder encoder;
    private final MyTextLineDecoder decoder;

    public MyTextLineCodecFactory() {
        // 使用自定义编码/解码类
        encoder = new MyTextLineEncoder();
        decoder = new MyTextLineDecoder();
    }
    @Override
    public ProtocolEncoder getEncoder(IoSession session) throws Exception {
        return encoder;
    }
    @Override
    public ProtocolDecoder getDecoder(IoSession session) throws Exception {
        return decoder;
    }
}

2.自定义编码器

package linchaolong.mina.server.filter;

import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoder;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;

// 自定义编码器
public class MyTextLineEncoder implements ProtocolEncoder {

    @Override
    public void encode(IoSession session, Object message,
            ProtocolEncoderOutput out) throws Exception {
        //字符编码器
        CharsetEncoder encoder = (CharsetEncoder) session.getAttribute("encoder");
        if (encoder == null) {
            encoder = Charset.forName( "GBK" ).newEncoder();
            session.setAttribute("encoder", encoder);
        }

        String value = (message == null ? "" : message.toString());
        IoBuffer buf = IoBuffer.allocate(value.length()) // 初始化buff size为消息长度
                .setAutoExpand(true); //可自动扩展buff size
        buf.putString(value, encoder); //把消息添加到buff,并使用指定编码器

        buf.flip(); // update position
        out.write(buf);
    }

    @Override
    public void dispose(IoSession session) throws Exception {
    }
}

3.自定义解码器

package linchaolong.mina.server.filter;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;

// 自定义解码器
public class MyTextLineDecoder implements ProtocolDecoder {

    @Override
    public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)
            throws Exception {

        // 字符解码器
        CharsetDecoder decoder = (CharsetDecoder) session.getAttribute("decoder");
        if (decoder == null) {
            decoder = Charset.forName( "GBK" ).newDecoder();
            session.setAttribute("decoder", decoder);
        }

        // 开始位置
        int startPos = in.position();
        while(in.hasRemaining()){
            byte b = in.get();
            if (b == ‘\n‘) {
                int pos = in.position();
                int limit = in.limit();

                // 截取从开始位置到当前位置的数据,转换为字符串
                // 1.设置截取的位置
                in.position(startPos);
                in.limit(pos);

                //2.截取
                IoBuffer buff = in.slice();
                byte bytes[] = new byte[limit-2]; // -2 不包含换行符和结束符
                buff.get(bytes);

                //3.解码
                ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
                CharBuffer charBuffer = decoder.decode(byteBuffer);
                out.write(charBuffer.toString());

                //4.复原
                in.position(pos);
                in.limit(limit);
            }
        }

    }

    @Override
    public void finishDecode(IoSession session, ProtocolDecoderOutput out)
            throws Exception {
    }

    @Override
    public void dispose(IoSession session) throws Exception {
    }
}

4.使用自定义编码过滤器

acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new MyTextLineCodecFactory()));

项目地址:https://coding.net/u/linchaolong/p/MinaTest

时间: 2024-08-03 18:18:34

使用Mina框架实现C/S通讯的相关文章

MINA框架

MINA 框架简介 Apache Mina Server 是一个网络通信应用框架,也就是说,它主要是对基于TCP/IP.UDP/IP协议栈的通信框架(然,也可以提供JAVA 对象的序列化服务.虚拟机管道通信服务等),Mina 可以帮助我们快速开发高性能.高扩展性的网络通信应用,Mina 提供了事件驱动.异步(Mina 的异步IO 默认使用的是JAVA NIO 作为底层支持)操作的编程模型.Mina 主要有1.x 和2.x 两个分支,这里我们讲解最新版本2.0,如果你使用的是Mina 1.x,那么

Android Mina框架的学习笔记

Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架.当前发行的 MINA 版本支持基于 Java NIO 技术的 TCP/UDP 应用程序开发.串口通讯程序(只在最新的预览版中提供),MINA 所支持的功能也在进一步的扩展中.目前正在使用 MINA 的软件包括有:Apache Directory Project.Asyn

Mina框架的学习笔记——Android客户端的实现

Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架.当前发行的 MINA 版本支持基于 Java NIO 技术的 TCP/UDP 应用程序开发.串口通讯程序(只在最新的预览版中提供),MINA 所支持的功能也在进一步的扩展中.目前正在使用 MINA 的软件包括有:Apache Directory Project.Asyn

使用Mina框架开发 QQ Android 客户端

Apache MINA是一个网络应用程序框架,用来帮助用户简单地开发高性能和高可靠性的网络应用程序.它提供了一个通过Java NIO在不同的传输例如TCP/IP和UDP/IP上抽象的事件驱动的异步API. Apache MINA 也称为: ● NIO 框架库 ● 客户端服务器框架库 ● 一个网络套接字库 MINA虽然简单但是仍然提供了全功能的网络应用程序框架: ● 为不同的传输类型提供了统一的API: ○ 通过Java NIO提供TCP/IP 和 UDP/IP支持 ○ 通过RXTX提供串口通讯(

Mina框架研究

Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 组织一个较新的项目,它为开发高性能和高可用性的网络应用程序提供了非常便利的框架. 这个框架的优点: – 异步 – 无阻塞 – 事件驱动 – 支持TCP, UDP, APR, 串口- – 通过 过滤器(Filters)实现扩展性 – 同时提供协议框架 总体框架 之前的一个项目用到了MINA,最近想再系统的整理一下,主要参考MINA 2.0 User

长连接神器Mina框架的使用

前段时间学习了mina框架的使用.它是基于Socket进行通信,所以说在项目中要是需要长连接的使用,那mina框架是一个不错的选择. 下面简单介绍一下mina框架的使用,学习mina框架不长时间,现在写下来即时为了记录一下自己的学习经历,又希望可以帮助其他初学者朋友,若有不足与错误之处,还请大神指教. 在使用mina框架之前需要下载所需的jar包.可以在我的网盘中下载,Android端也可以用的.地址:http://pan.baidu.com/s/1skP8YT3 ,提取码:inyl. 所需的j

Mina框架项目运用

近期最一个项目对通信要求比較严格,须要建立长连接,且能处理多并发,所以选择了Mina框架.以下就简单记录开发的过程吧: mina 开发须要的jar包: mina pc端通信: 服务端: package cn.ys.net; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.ProtocolCodecFactory; import org.apache.mina.filter.cod

基于MINA框架快速开发网络应用程序

1.MINA框架简介 MINA(Multipurpose Infrastructure for Network Applications)是用于开发高性能和高可用性的网络应用程序的基础框架.通过使用MINA框架可以可以省下处理底层I/O和线程并发等复杂工作,开发人员能够把更多的精力投入到业务设计和开发当中.MINA框架的应用比较广泛,应用的开源项目有Apache Directory.AsyncWeb.Apache Qpid.QuickFIX/J.Openfire.SubEthaSTMP.red5

使用Apache MINA框架搭建服务端

使用MINA框架搭建服务端步骤: 1.定义一个启动服务的类MinaServer,并实现接口ServletContextListener 2.定义一个处理业务逻辑的类MinaServerHandler,并继承类IoHandlerAdapter 类MinaServer代码例如以下: import java.net.InetSocketAddress; import javax.servlet.ServletContextEvent; import javax.servlet.ServletConte