spingboot整合netty服务器端

1、引入pom
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
        </dependency>
2、创建服务netty服务器
package com.tan.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Slf4j
@Configuration
public class NettyServer {

    //负责处理接受进来的链接
    private EventLoopGroup bossGroup;
    //负责处理已经被接收的连接上的I/O操作
    private EventLoopGroup workerGroup;
    //在这个场景中,它表示服务器的绑定操作的结果
    private ChannelFuture future;

    @PostConstruct
    public void startServer() throws Exception {
        int port = 9999;
        bossGroup = new NioEventLoopGroup();
        workerGroup = new NioEventLoopGroup();
        //创建ServerBootstrap,这个类封装了服务器端的网络配置,使得我们可以轻松地设置服务器参数
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new NettyServerInitializer());

        // 绑定端口并开始接受进来的连接
        future = bootstrap.bind(port).sync();
        // 等待服务器套接字关闭
//            future.channel().closeFuture().sync();
        if (future.isSuccess()) {
            log.info("启动 Netty Server,端口:{}", port);
        }
    }

    @PreDestroy
    public void stopServer() {
        if (future != null && !future.isDone()) {
            future.cancel(true);
        }
        workerGroup.shutdownGracefully();
        bossGroup.shutdownGracefully();
    }
}

3、创建服务端初始化器
package com.tan.netty;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.CharsetUtil;

import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

public class NettyServerInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel ch) {
        ChannelPipeline pipeline = ch.pipeline();
        {
            //这段避免拆包、粘包,结束符为【#】
            ByteBuf byteBuf = Unpooled.copiedBuffer("#".getBytes(StandardCharsets.UTF_8));
            pipeline.addLast(new DelimiterBasedFrameDecoder(2048, byteBuf));
        }
        // 添加一个字符串解码器,用于将接收到的ByteBuf转换成字符串
        // 这里假设使用的是UTF-8字符集
        pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
        // 添加一个字符串编码器,用于将发送的字符串转换成ByteBuf
        // 这样服务器发送字符串时,客户端可以直接接收到字符串
        pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
        {
            // 通道10S钟空闲就会触发 ChannelHandler1.userEventTriggered 方法
            ch.pipeline().addLast(new IdleStateHandler(10, 0, 0, TimeUnit.SECONDS));
            // 添加自定义的ChannelInboundHandlerAdapter来处理业务逻辑
            //一般情况下一个处理器就够了,两个处理器只是为了试试自定义事件【CustomUserEvent】
            pipeline.addLast(new ChannelHandler1());
            pipeline.addLast(new ChannelHandler2());
            //注意点:
            //  1、仅当前代码,通道10S钟空闲就会触发 ChannelHandler1.userEventTriggered 方法,但是并不会触发 ChannelHandler2.userEventTriggered 方法
            //     这个是因为没有复写了父类的方法,没有继续调用父类的userEventTriggered方法,业务处理完加上super.userEventTriggered(ctx, evt);
            //     ChannelHandler2.userEventTriggered 方法就可以触发了
            //  2、其他方法的也一样
        }
    }


}

4、创建管道拦截器(用于处理业务逻辑,通常一个拦截器就够了)
4.1、管道拦截器1
package com.tan.netty;

import com.tan.netty.event.CustomUserEvent;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;

public class ChannelHandler1 extends ChannelInboundHandlerAdapter {
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println("============ChannelHandler1=====================");
        System.out.println("msg = " + msg);
        //继续调用下一个处理器
        super.channelRead(ctx, msg);
        {
            CustomUserEvent event = new CustomUserEvent("Hello from CustomUserEvent!");
            //触发事件 只能进入 ChannelHandler2.userEventTriggered 方法,不能进入当前类的 userEventTriggered 方法
            ctx.fireUserEventTriggered(event);
        }
    }

    /**
     * 该事件是由IdleStateHandler触发
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.READER_IDLE) {
                System.out.println("读空闲,关闭连接");
                ctx.close();
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }
}

4.2、管道拦截器2
package com.tan.netty;

import com.tan.netty.event.CustomUserEvent;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class ChannelHandler2 extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (true) {
            CustomUserEvent event = new CustomUserEvent("Hello from CustomUserEvent!");
            ctx.fireUserEventTriggered(event);
        }
        // 在这里处理接收到的数据
        System.out.println("MyChannelHandler");
        System.out.println("msg = " + msg);
        int i = 1 / 0;
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("处理异常");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("断开连接时触发");
    }

    /**
     * 该事件是由IdleStateHandler触发
     */
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof CustomUserEvent) {
            CustomUserEvent evt1 = (CustomUserEvent) evt;
            System.out.println(evt1.getMessage());
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }
}

5、定义事件

可以跑一下这个demo试一下自定义事件,感觉这个一般没啥用。

public class CustomUserEvent {
    private final String message;

    public CustomUserEvent(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值