SpringBoot集成Mina快速入门Demo

本文详细介绍了ApacheMina的原理、组件、工作流程,包括服务端和客户端流程,以及如何在SpringBoot项目中集成Mina,涉及长连接与短连接的区别和配置。展示了如何创建IoHandler和Mina配置类,以及一个简单的客户端测试示例。
摘要由CSDN通过智能技术生成

目录

1. Mina概述

2. Mina组件

3. Mina工作流程

3.1 服务端流程

3.2 客户端流程

4. Mina长链接与短连接

5. Mina示例

5.1 SpringBoot基础工程搭建

5.2 引入Mina依赖

5.3 IoHandler类,顾名思义即接收到消息的处理类

5.4 Mina配置类

5.5 测试


1. Mina概述

  • Apache的Mina(Multipurpose Infrastructure Networked Applications)是一个基于java nio的网络通信框架。主要屏蔽了网络通信的一些细节,对Socket进行封装,并且是NIO的一个实现架构,可以帮助我们快速的开发网络通信,常用于游戏的开发、中间件服务端的程序中。

  • Mina最主要的工作就是把底层传输的字节码转换为Java对象,提供给应用程序;或者把应用程序返回的结果转换为字节码,交给底层传输。

  • Mina 的API 将真正的网络通信与我们的应用程序隔离开来,我们只需要关心我们要发送、接收的数据以及业务逻辑即可。

2. Mina组件

  • IOService:最底层的是IOService,负责具体的IO相关工作。用于描述我们的客户端和服务端接口,(IoAccept、IoConnector是其子类),分别用于服务端和客户端使用;这一层的典型代表有IOSocketAcceptor和 IOSocketChannel。每当有数据到达时,IOService会先调用底层IO接口读取数据,封装成IoBuffer,之后以事件的形式通知上层代码,从而将Java NIO的同步IO接口转化成了异步IO。

  • IOprocessor:是多线程的环境来处理我们的连接请求;IoProcessor 负责调用注册在 IoService 上的过滤器,并在过滤器链之后调用IoHandler。

  • IOFilter: 这个接口定义一组拦截器,用于数据的过滤工作(包含编解码、日志等信息的过滤),其中数据的encode与decode是最为重要的、也是你在使用Mina时最主要关注的地方。

  • IOHandler:这个接口负责编写业务逻辑,也就是接收、发送数据的地方。需要有开发者自己来实现这个接口。

  • IOsession: 是对底层连接(服务器与客户端的特定连接,该连接由服务器地址、端口以及客户端地址、端口来决定)的封装。

3. Mina工作流程

3.1 服务端流程

  1. 通过SocketAcceptor 同客户端建立连接;

  2. 连接建立之后 I/O的读写交给了I/O Processor线程,I/O Processor是多线程的;

  3. 通过I/O Processor 读取的数据经过IoFilterChain里所有配置的IoFilter,IoFilter进行消息的过滤,格式的转换,在这个层面可以制定一些自定义的协议;

  4. 最后IoFilter将数据交给 Handler 进行业务处理,完成了整个读取的过程;

        写入过程也是类似,只是刚好倒过来, 通过IoSession.write 写出数据,然后Handler进行写入的业务处理,处理完成后交给IoFilterChain,进行消息过滤 和协议的转换,最后通过 I/O Processor 将数据写出到 socket 通道。

3.2 客户端流程

  1. 客户端首先创 建一个IOConnector 用来和服务端通信,顾名思义这就是建立的一个连接对象;

  2. 在这个连接上创建一个session, 客 户端中的业务方法可以向session中写入数据,数据经过Filter Chain的过滤后会发送给服务端;

  3. 从服务端发回的数据也会首先经过Filter Chain的过滤,然后交给IOHandler做进一步的处理

4. Mina长链接与短连接

  • 长连接:通信双方长期的保持一个连接状态不断开,一旦建立连接后,就不断开,除非发生异常,比较消耗IO资源。

  • 短连接:通信双方不是保持一个长期的连接状态,比如Http协议,当客户端发起http请求,服务器处理http请求,当服务器处理完成后,返回客户端数据后就断开链接。

        默认是长连接的,处于监听会话状态,可以改成短连接。如下图 服务端设置      

  

5. Mina示例

5.1 SpringBoot基础工程搭建

         参阅 SpringBoot快速入门

5.2 引入Mina依赖

<!--MINA-->
<dependency>
    <groupId>org.apache.mina</groupId>
    <artifactId>mina-core</artifactId>
    <version>2.0.0-RC1</version>
</dependency>

5.3 IoHandler类,顾名思义即接收到消息的处理类

package com.miaxis.controller;

import lombok.extern.slf4j.Slf4j;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;

import java.util.Date;

/**
 * IoHandler继承类,socket服务器端处理类
 * @author chen.gs
 * @date - 2024-01-14
 */
@Slf4j
public class ServerHandler extends IoHandlerAdapter {
    /**
     * 当一个新客户端连接后触发此方法.
     */
    @Override
    public void sessionCreated(IoSession session) throws Exception{
        super.sessionCreated(session);
        // To do
        //log.info("连接创建: " + session.getRemoteAddress().toString());
    }

    /**
     * 当连接后打开时触发此方法,一般此方法与 sessionCreated 会被同时触发
     */
    @Override
    public void sessionOpened(IoSession session) throws Exception{
        super.sessionOpened(session);
        // To do
        log.info("连接打开: " + session.getRemoteAddress().toString());
    }

    /**
     * 当连接被关闭时触发
     */
    @Override
    public void sessionClosed(IoSession session) throws Exception {
        super.sessionClosed(session);
        // To do
        log.info("连接关闭 : " + session.getRemoteAddress().toString());
        int size = session.getService().getManagedSessions().values().size();
        log.info("连接关闭时session数量==》" + size);
    }

    /**
     * 当连接空闲时触发此方法.
     */
    @Override
    public void sessionIdle(IoSession session, IdleStatus status) throws Exception{
        super.sessionIdle(session,status);
        // To do
        if (status == IdleStatus.READER_IDLE) {
            log.info("进入读空闲状态");
            session.close(true);
        } else if (status == IdleStatus.BOTH_IDLE) {
            log.info("BOTH空闲");
            session.close(true);
        }
    }

    /**
     * 抛出异常未被捕获触发此方法
     */
    @Override
    public void exceptionCaught(IoSession session, Throwable cause) throws Exception{
        super.exceptionCaught(session,cause);
        // To do
        log.error("出现异常 :" + session.getRemoteAddress().toString() + " : " + cause.toString());
        //cause.printStackTrace();
        session.write("exceptionCaught");
        session.close(true);
    }

    /**
     * 接收到客户端的请求信息触发此方法.
     */
    @Override
    public void messageReceived(IoSession session, Object message) throws Exception {
        super.messageReceived(session, message);// 消息的接受
        // To do
        //String server_ip = session.getLocalAddress().toString();
        String client_ip = session.getRemoteAddress().toString();
        String text = String.valueOf(message);
        log.info("接受到客户端["+client_ip+"]数据 :" + text);
        log.info("数据业务处理开始...... ");
        String result = analyzeData(session,text);
        log.info("数据业务处理结束...... ");
        session.write(result);
    }

    private String analyzeData(IoSession session,String text) {
        String address = session.getLocalAddress().toString();
        // 返回数据
        Date date = new Date();
        String responseMessage = date.toString()+"||"+text;
        return responseMessage;
    }

    /**
     * 当信息已经传送给客户端后触发此方法.
     */
    @Override
    public void messageSent(IoSession session, Object message)throws Exception{
        super.messageSent(session,message);
        // To do
        log.info("返回客户端[" + session.getRemoteAddress().toString() +"]信息:"+message);
    }
}

5.4 Mina配置类

package com.miaxis.config;

import com.miaxis.controller.ServerHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoHandler;
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.transport.socket.nio.NioSocketAcceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.net.InetSocketAddress;

@Configuration
@Slf4j
public class MinaConfig {
    private static final int PORT = 9123;
    /**
     * 拦截器, Mina事件类ServerHandler
     */
    @Bean
    public IoHandler ioHandlerG() {
        return new ServerHandler();
    }

    @Bean
    public IoAcceptor ioAcceptorG() throws Exception {
        IoAcceptor acceptor = new NioSocketAcceptor();
        // 增加编码过滤器,统一编码UTF-8,TextLineCodecFactory基于文本的,根据回车换行来断点传输数据
        // 在mina中,一般的应用场景用TextLine的Decode和Encode就够用了
        // TextLine的默认分割符虽然是\n,但其实分隔符是可以自己指定的,如:newTextLineDecoder(charset, decodingDelimiter);
        acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory()));
        // 使用自定义编码解码工厂类
        //acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new SocketFactory(Charset.forName("utf-8"))));
        // 设置服务端逻辑处理器
        acceptor.setHandler(ioHandlerG());
        // 设置读缓存大小
        acceptor.getSessionConfig().setReadBufferSize(2048);
        // 设置指定类型的空闲时间(单位:秒),空闲时间超过这个值将触发sessionIdle方法
        acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
        // 绑定端口
        acceptor.bind(new InetSocketAddress(PORT));
        //System.out.println("Mina服务已启动,端口:" + PORT);
        log.info("Mina服务已启动,端口:" + PORT);
        return acceptor;
    }
}

5.5 测试

package com.miaxis.mina;

import org.apache.mina.core.future.ConnectFuture;
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.NioSocketConnector;
import org.junit.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.net.InetSocketAddress;
import java.nio.charset.Charset;

@SpringBootTest
public class MinaClientTest {
    @Test
    public void mina() {
        // 创建客户端连接器.
        NioSocketConnector connector = new NioSocketConnector();
        connector.getFilterChain().addLast("logger", new LoggingFilter());
        // 设置编码过滤器
        connector.getFilterChain().addLast("codec",
            new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("utf-8"))));
//        connector.getFilterChain().addLast("codec",
//                new ProtocolCodecFilter(new SocketEncoder(),new SocketDecoder()));
        // 设置事件处理器
        connector.setHandler(new ClientHandler());
        // 建立连接
        ConnectFuture cf = connector.connect(new InetSocketAddress("127.0.0.1", 9123));
        // 等待连接创建完成
        cf.awaitUninterruptibly();
        // 发送消息,中英文符号都有
        cf.getSession().write("hello,d的十多年弗兰克萨洛克{id:丹参粉!!!}");

        try {
            Thread.sleep(1000); //1000 毫秒,也就是1秒.
        } catch(InterruptedException ex) {
            Thread.currentThread().interrupt();
        }
        
        cf.getSession().close(true);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值