SpringBoot从0-1集成Netty实现自定义协议开发

✨重磅!盹猫的个人小站正式上线啦~诚邀各位技术大佬前来探秘!✨

这里有:

  • 硬核技术干货:编程技巧、开发经验、踩坑指南,带你解锁技术新姿势!
  • 趣味开发日常:代码背后的脑洞故事、工具测评,让技术圈不再枯燥~
  • 独家资源分享:开源项目、学习资料包,助你打怪升级快人一步!

👉 点击直达→ 盹猫猫的个人小站 👈

🌟 来逛逛吧,说不定能挖到你正在找的技术宝藏哦~

目录

一、前言

[1.1 什么是Netty?](#1.1 什么是Netty?)

[1.2 Netty流程图](#1.2 Netty流程图)

二、环境准备

[2.1 Pom依赖](#2.1 Pom依赖)

三、代码步骤

[3.1 创建服务](#3.1 创建服务)

[3.2 创建解码器](#3.2 创建解码器)

[3.3 事件处理器](#3.3 事件处理器)

四、总结


欢迎来到 盹猫(>^ω^<)的博客

本篇文章主要介绍了

**SpringBoot从0-1集成Netty实现自定义协议开发**

❤博主广交技术好友,喜欢文章的可以关注一下❤

一、前言

在使用SpringBoot实际的开发过程中,为适应不同的应用场景,有时会先择使用HTTP协议通信,有时又使用MQTT协议进行通信(当然SpringBoot现在已经有很多比较完善的协议集成,也基本满足大部分的开发场景)。但一些公司自定义了一些协议(比如一些比较小众的手表通信,或自

己用物联网硬件自己DIY了一些设备需要通信),这时使用SpringBoot应该怎么接收呢?

有些朋友可能Java技术很强,从0开始搞了一套Socket通信程序,这其实也不是不行,但如果有多线程、高并发,实现起来很麻烦,而且健壮性得不到保障。

想解决这些问题,其实并不难,只需要让你的SpringBoot集成Netty框架,本篇文章就是记录SpringBoot如何是如何集成Netty框架的。

但在此之前先来看看什么是Netty?

1.1 什么是Netty?

Netty 是一款基于 Java NIO 开发的异步、事件驱动的高性能网络通信框架,由 JBoss 团队开发并开源(Apache 2.0 协议),核心目标是简化高性能、高可靠性的 TCP/UDP 网络应用开发,解决原生 Java NIO 开发复杂度高、易出 bug(如空轮询、缓冲区管理混乱)等问题。

可以看出Netty就是为了简化TCP/UDP通信的网络应用开发而出现,并且使用了异步、事件驱动的方式。

**异步:**主线程只会管理子线程,而不会被阻塞。

**事件驱动:**已经对通信事件进行分类,只需要对不同事件分别进行代码填充即可。

1.2 Netty流程图

从Netty的工作流程图可以看出,其主要工作主要分为两个工作组,即Boss GroupWoker Group

Boss Group:用于处理连接请求,即当有连接发生时,注册一个通道,到Worker Group然后继续等待。(老板安排工作

Worker Group:用于读写数据并分配给不同的通道处理器ChannelHdandler进行处理。(打工人根据要求进行处理😭

废话不多说,来看看如何集成吧!

LET'S GO!🚗

二、环境准备

2.1 Pom依赖

XML 复制代码
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

JDK这里使用JDK1.8。

XML 复制代码
<dependencies>
        <!--Netty主要依赖-->
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.65.Final</version>
        </dependency>
        <!--SpringBoot的Web开发依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.18</version>
        </dependency>
</dependencies>

这里使用SpringBoot的Web依赖,当然如果不是做Web程序也可以使用spring-boot-starter依赖。

三、代码步骤

3.1 创建服务

首先根据流程图,我们需要创建BossGroupWorkerGroup,这两个是主要的工作组,代码如下:

java 复制代码
    /**
     * Desc:[Boss工作组]
     **/
    final EventLoopGroup bossGroup = new NioEventLoopGroup();
    /**
     * Desc:[Worker工作组]
     **/
    final EventLoopGroup workGroup = new NioEventLoopGroup();

然后将这两个添加到ServerBootstrap(Netty提供的服务构建器 **)**中,代码如下:

java 复制代码
//创建一个启动器
 ServerBootstrap serverBootstrap = new ServerBootstrap();
//添加boss工作组和worker工作组
 serverBootstrap.group(bossGroup, workGroup);

然后设置通道的类型为异步通道,代码如下:

java 复制代码
serverBootstrap.channel(NioServerSocketChannel.class)

这时Boss工作组其实就已经设置完了(相当于流水线建完了,但是领导还没有决定让工人干什么),我们的目的是为了处理自定义的任务,所以还要添加编码器(Decoder)、事件处理器(Handler)、解码器(Encoder),这里编码器和解码器不是必须的。

代码如下:

java 复制代码
 serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
   @Override
   protected void initChannel(SocketChannel ch) {
        //这里添加编解码器和监听器
        ch.pipeline().addLast(编码器/解码器/事件处理器);
        }
 });

这里就可以在通道中设置具体的工作了,可以通过代码设置具体的工作事项,例如下面的代码:

java 复制代码
//添加一个自定义的ByteMessage解码器
ch.pipeline().addLast(new ByteMessageDecoder());
//添加一个连接的是否读/写超时的事件处理器
ch.pipeline().addLast("idleStateHandler", new IdleStateHandler(600, 0, 0));
//添加一个自定义的设备事件处理器
ch.pipeline().addLast("common-handler", deviceServerHandlerJL);

这里IdleStateHandler是Netty自带的,而解码器和设备处理器一般需要自定义,因为要接收不同的协议格式和自定义处理事件。

3.2 创建解码器

解码器即为入站时要满足不同的需要进行数据格式转换的服务,一般放在pipeline的最上层,Netty有一些已经实现的解码器可以直接使用,如:JsonObjectDecoder (提供基础的Josn格式校验), 如果不配置默认是直接是ByteBuf接收,但一般情况下还是需要进行自定义的,而自定义时一般继承ByteToMessageDecoder(即直接对Byte数据进行操作) 。

例如实现一个固定长度的以16进制发送传输的协议数据时,我们需要按照固定开头(分割符)进行byte进制转换为16进制的字符串的功能,(需要将256转换为FF字符串),将并添加到list中方便后续的事件处理器进行处理,代码如下:

java 复制代码
package com.ydhy.platform.decoder;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.ByteProcessor;

import java.util.List;

/**
 * DES:
 *
 * @author nodcat
 * Time:2023/1/28 下午5:33
 * Implements{@link }
 * extend{@link ByteToMessageDecoder}
 **/
public class DeviceMessageDecoder extends ByteToMessageDecoder {
//
    /**
     * 长度49
     */
    private final int frameLength;

    /**
     * DES:[构造方法]
     *
     * @param frameLength 消息长度 49
     * @link [int]
     */
    public DeviceMessageDecoder(int frameLength) {
        this.frameLength = frameLength;
    }

    /**
     * DES:[入站16进制数据解析]
     *
     * @param channelHandlerContext channel全局管理
     * @param byteBuf               byteBuf 数据缓存
     * @param list                  列表
     * @link [io.netty.channel.ChannelHandlerContext, io.netty.buffer.ByteBuf, java.util.List<java.lang.Object>]
     */

    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) {
        int indexFb = byteBuf.forEachByte(new ByteProcessor.IndexOfProcessor((byte) Integer.parseInt("FB", 16)));
        int index90 = byteBuf.forEachByte(new ByteProcessor.IndexOfProcessor((byte) Integer.parseInt("90", 16)));
        if (indexFb != -1 && index90 != -1) {
            if (indexFb == (index90 - 1)) {
                byteBuf.readerIndex(indexFb);
                if (byteBuf.readableBytes() >= frameLength) {
                    ByteBuf slice = byteBuf.readRetainedSlice(frameLength);
                    byte[] bytes = new byte[frameLength];
                    slice.readBytes(bytes);
                    list.add(bytesToHexString(bytes));
                    slice.release();
                }

            }
        }
    }

    /**
     * DES:[将byte数组转换为16进制的字符串]
     *
     * @param bArray 字节数组
     * @return java.lang.String
     * @link [byte[]]
     */
    public String bytesToHexString(byte[] bArray) {
        StringBuilder sb = new StringBuilder(bArray.length);
        String sTemp;
        for (byte b : bArray) {
            sTemp = Integer.toHexString(0xFF & b);
            if (sTemp.length() < 2) {
                sb.append(0);
            }
            sb.append(sTemp.toUpperCase());
        }
        return sb.toString();
    }
}

重写decode方法,查找起始的字节并限制接收的数据长度为49位,这样在后续处理中就直接对16进制的字符串进行胺位处理(长度为98的16进制字符串,因为一个byte转换为了两个16进制字符串表示),如果不经过Decoder转换,在后续的处理过程中就会繁琐很多。

java 复制代码
//最先执行下面的addLast方法,确保解码器在最上层
ch.pipeline().addLast(new DeviceMessageDecoder(49));
//或者使用addFast添加到解码器到最上层
ch.pipeline().addFast(new DeviceMessageDecoder(49));

提示:这里可以根据实际的协议要求进行处理,确保解码器在处理流程的最上层。

3.3 事件处理器

事件处理器就是对具体的业务进行处理了,继承**SimpleChannelInboundHandler<Object>**来实现对解码器处理完的数据进行处理,继承后一般会重写以下方法:

读事件处理

java 复制代码
@Override
public void channelRead0(ChannelHandlerContext ctx, Object msg) {
    //这里的msg即为解码器处理完成后的数据
}

连接状态

java 复制代码
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {

    //这里处理连接状态异常,如读写超时多久后断开链接

}

通道关闭事件

java 复制代码
@Override
public void channelInactive(ChannelHandlerContext ctx) {
    //通道关闭时调用,一般在remove事件之前被调用
}

事件处理器添加事件

java 复制代码
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
    //当有连接时触发
}

事件处理器移除事件

java 复制代码
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
    //事件处理器移除时触发
}

同样需要添加到3.1的pipeline中,一般放在解码器之后,代码如下:

java 复制代码
//这里可选有解码器

//添加一个自定义的设备事件处理器
ch.pipeline().addLast("handler", deviceServerHandler);


//这里可选有编码器

通过分别对上述不同事件触发的方法进行重写实现,以实现自定义的功能,例如:

当事件处理器添加时,我们可以提示用户上线了。

当事件处理器移除时,提示用户断开链接下线了。

当发生读事件时,我们接收数据并存储到数据库,或直接进行数据的响应。

可以使用下述代码进行数据的响应:

java 复制代码
ctx.channel().writeAndFlush(响应内容);

这里ctx即为ChannelHandlerContext类,该类的功能可以在Handler方便获取事件处理器的上下文。

四、总结

本文通过集成Netty,可以更容易的实现高性能的异步通信功能,在自定义协议时可以使用分层架构,自定义解码器、编码器、事件处理器,并添加到ch.pipeline中,提高了代码的可维护性和健壮性,相比自己去实现socket通信,这种通信方式是更简单更推荐的。

上面就是所有文章内容了,如果内容对你有帮助,麻烦留一个赞👍和收藏⭐支持一下!


如果你对区块链 内容感兴趣可以查看我的专栏:小试牛刀-区块链

感谢您的关注和收藏!!!!!!

相关推荐
长安城没有风2 小时前
在 IntelliJ IDEA 中高效使用 Git 的实用指南
java·git·intellij-idea
程序员爱钓鱼2 小时前
Node.js 编程实战:WebSocket实时通信详解
后端·node.js·trae
武子康2 小时前
Java-195 RabbitMQ BlockingQueue 手搓“消息中间件”雏形:生产者-消费者模型到企业级 MQ 差在哪
java·分布式·架构·消息队列·rabbitmq·java-rabbitmq·mq
Propeller2 小时前
【Android】动态操作 Window 的背后机制
android·java
zzzgw_20012 小时前
io_uring的机理和跟epoll的对比
java·开发语言
pandarking2 小时前
[CTF]攻防世界:easy_laravel 学习
java·学习·web安全·laravel·ctf
Han.miracle2 小时前
数据结构与算法--006 和为s的两个数字(easy)
java·数据结构·算法·和为s的两个数字
程序员爱钓鱼2 小时前
Node.js 编程实战:Cookie与Session深度解析
后端·node.js·trae
青梅主码2 小时前
麦肯锡最新发布《今天的消费者是如何花费他们的时间和金钱的?》:揭示2025年消费者在时间和金钱上的五大关键变化趋势
后端