Springboot集成Netty

一、Springboot+Netty环境搭建

1、引入依赖:

<dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
</dependency>

2、application.yml配置 

server:
  port:8080
netty:
  host: 127.0.0.1
  port: 7397

3、配置服务端 

(1)定义nettyServer组件,用于绑定端口,关闭通道,获取通道操作

@Component("nettyServer")
public class NettyServer {
    private Logger logger = LoggerFactory.getLogger(NettyServer.class);

    private final EventLoopGroup parentGroup = new NioEventLoopGroup();
    private final EventLoopGroup childGroup = new NioEventLoopGroup();
    private Channel channel;


    /**
     * 绑定端口
     * @param address
     * @return
     */
    public ChannelFuture bind(InetSocketAddress address) {
        ChannelFuture channelFuture = null;
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(parentGroup, childGroup)
                    .channel(NioServerSocketChannel.class)    //非阻塞模式
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childHandler(new MyChannelInitializer());
            channelFuture = b.bind(address).syncUninterruptibly();
            channel = channelFuture.channel();
        } catch (Exception e) {
            logger.error(e.getMessage());
        } finally {
            if (null != channelFuture && channelFuture.isSuccess()) {
                logger.info("netty server start done.");
            } else {
                logger.error("netty server start error.");
            }
        }
        return channelFuture;
    }


    /**
     * 销毁
     */
    public void destroy() {
        if (null == channel) return;
        channel.close();
        parentGroup.shutdownGracefully();
        childGroup.shutdownGracefully();
    }


    /**
     * 获取通道
     * @return
     */
    public Channel getChannel() {
        return channel;
    }


}

(2)创建继承通道初始化类的类,用于初始化通道

public class MyChannelInitializer extends ChannelInitializer<SocketChannel> {
    /**
     * 初始化channel
     * @param channel
     * @throws Exception
     */
    @Override
    protected void initChannel(SocketChannel channel) throws Exception {
        // 日志打印
        channel.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
        // 基于换行符号
        channel.pipeline().addLast(new LineBasedFrameDecoder(1024));
        // 解码转String,注意调整自己的编码格式GBK、UTF-8
        channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK")));
        // 解码转String,注意调整自己的编码格式GBK、UTF-8
        channel.pipeline().addLast(new StringEncoder(Charset.forName("GBK")));
        // 在管道中添加我们自己的接收数据实现方法
        channel.pipeline().addLast(new MyServerHandler());

    }
}

(3)定义一个类继承通道的拦截类 ,处理通道连接请求

public class MyServerHandler extends ChannelInboundHandlerAdapter {

    private Logger logger = LoggerFactory.getLogger(MyServerHandler.class);

    /**
     * 当客户端主动链接服务端的链接后,这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        SocketChannel channel = (SocketChannel) ctx.channel();
        //通知客户端链接建立成功
        String str = "通知客户端链接建立成功" + " " + new Date() + " " + channel.localAddress().getHostString() + "\r\n";
        ctx.writeAndFlush(str);
    }

    /**
     * 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        logger.info("客户端断开链接{}", ctx.channel().localAddress().toString());
    }


    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        //接收msg消息{与上一章节相比,此处已经不需要自己进行解码}
        logger.info(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 服务端接收到消息:" + msg);
        //通知客户端链消息发送成功
        String str = "服务端收到:" + new Date() + " " + msg + "\r\n";
        ctx.writeAndFlush(str);
    }

    /**
     * 抓住异常,当发生异常的时候,可以做一些相应的处理,比如打印日志、关闭链接
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
        logger.info("异常信息:\r\n" + cause.getMessage());
    }


}

(4)最后调用,启动服务端

@SpringBootApplication
@ComponentScan("com.shu")
public class NettyApplication implements CommandLineRunner {

    @Value("${netty.host}")
    private String host;
    @Value("${netty.port}")
    private int port;
    @Autowired
    private NettyServer nettyServer;

    public static void main(String[] args) {
        SpringApplication.run(NettyApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        InetSocketAddress address = new InetSocketAddress(host, port);
        ChannelFuture channelFuture = nettyServer.bind(address);
        Runtime.getRuntime().addShutdownHook(new Thread(() -> nettyServer.destroy()));
        channelFuture.channel().closeFuture().syncUninterruptibly();
    }
}

4.配置客户端

public class ApiTest {
    public static void main(String[] args) {
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(workerGroup);
            b.channel(NioSocketChannel.class);
            b.option(ChannelOption.AUTO_READ, true);
            b.handler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel channel) throws Exception {
                    // 日志打印
                    channel.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
                    // 基于换行符号
                    channel.pipeline().addLast(new LineBasedFrameDecoder(1024));
                    // 解码转String,注意调整自己的编码格式GBK、UTF-8
                    channel.pipeline().addLast(new StringDecoder(Charset.forName("GBK")));
                    // 解码转String,注意调整自己的编码格式GBK、UTF-8
                    channel.pipeline().addLast(new StringEncoder(Charset.forName("GBK")));
                    // 在管道中添加我们自己的接收数据实现方法
                    channel.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                        @Override
                        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                            //接收msg消息{与上一章节相比,此处已经不需要自己进行解码}
                            System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + " 客户端接收到消息:" + msg);
                        }
                    });
                }
            });
            ChannelFuture f = b.connect("127.0.0.1", 7397).sync();
            System.out.println(" client start done");
            //向服务端发送信息
            f.channel().writeAndFlush("你好,SpringBoot启动的netty服务端,“我的结尾是一个换行符,用于传输半包粘包处理”\r\n");
            f.channel().writeAndFlush("你好,SpringBoot启动的netty服务端,“我的结尾是一个换行符,用于传输半包粘包处理”\r\n");
            f.channel().writeAndFlush("你好,SpringBoot启动的netty服务端,“我的结尾是一个换行符,用于传输半包粘包处理”\r\n");
            f.channel().writeAndFlush("你好,SpringBoot启动的netty服务端,“我的结尾是一个换行符,用于传输半包粘包处理”\r\n");
            f.channel().writeAndFlush("你好,SpringBoot启动的netty服务端,“我的结尾是一个换行符,用于传输半包粘包处理”\r\n");
            f.channel().closeFuture().syncUninterruptibly();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            workerGroup.shutdownGracefully();
        }
    }
}

二、 Netty实现简单的文件传输

1、定义两个实体,一个用于接收一个用于返回

@Data
public class FileResponse implements Serializable {
    private long length;
    private File file;

    public FileResponse() {
    }

    public FileResponse(long length, File file) {
        this.length = length;
        this.file = file;
    }

    public long getLength() {
        return length;
    }

    public File getFile() {
        return file;
    }
}

@Data
public class FileRequest implements Serializable {
    private String fileName;

    public FileRequest() {
    }

    public FileRequest(String fileName) {
        this.fileName = fileName;
    }

    public String getFileName() {
        return fileName;
    }
}

2、 配置服务端

 (1)定义配置连接的组件

@Component("fileServer")
public class FileServer {

    private Logger logger = LoggerFactory.getLogger(FileServer.class);


    /**
     * 绑定端口
     *
     * @param port
     * @return
     */
    public void bind(int port) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap()
                    .group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline()
                                    .addLast(new ObjectEncoder())
                                    .addLast(new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.weakCachingConcurrentResolver(null)))
                                    .addLast(new FileServerHandler());
                        }
                    });

            ChannelFuture future = bootstrap.bind(port).sync();
            future.channel().closeFuture().sync();
            System.out.println("服务端启动成功,端口:" + port);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }

}

(2)继承通道处理器,自定义通道处理器

public class FileServerHandler extends ChannelInboundHandlerAdapter  {

    private static final String FILE_PATH = "D:\\coreconfig.txt";

    private Logger logger = LoggerFactory.getLogger(FileServerHandler.class);


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        logger.info("channelActive");
    }


    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        logger.info("channelRead");
        if (msg instanceof FileRequest) {
            FileRequest request = (FileRequest) msg;
            if (request.getFileName().equals(FILE_PATH)) {
                File file = new File(FILE_PATH);
                if (file.exists()) {
                    RandomAccessFile raf = new RandomAccessFile(file, "r");
                    long length = raf.length();
                    FileResponse response = new FileResponse(length, file);
                    ctx.writeAndFlush(response);

                    ChannelFuture sendFileFuture = ctx.writeAndFlush(new DefaultFileRegion(raf.getChannel(), 0, length), ctx.newProgressivePromise());
                    sendFileFuture.addListener(new ChannelProgressiveFutureListener() {
                        @Override
                        public void operationComplete(ChannelProgressiveFuture future) throws Exception {
                            System.out.println("File transfer completed.");
                            raf.close();
                        }

                        @Override
                        public void operationProgressed(ChannelProgressiveFuture future, long progress, long total) throws Exception {
                            if (total < 0) {
                                System.err.println("File transfer progress: " + progress);
                            } else {
                                System.err.println("File transfer progress: " + progress + " / " + total);
                            }
                        }
                    });
                } else {
                    System.err.println("File not found: " + FILE_PATH);
                }
            } else {
                System.err.println("Invalid file name: " + request.getFileName());
            }
        }
    }

}

3、配置客户端

 (1)配置客户端连接信息:

public class FileClient {
    private static final int PORT = 8080;
    private static final String HOST = "localhost";
    private Logger logger = LoggerFactory.getLogger(FileClient.class);
    private final EventLoopGroup parentGroup = new NioEventLoopGroup();


    /**
     * 连接服务端
     */
    public void connect() {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap()
                    .group(group)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline()
                                    .addLast(new ObjectEncoder())
                                    .addLast(new ObjectDecoder(ClassResolvers.weakCachingConcurrentResolver(null)))
                                    .addLast(new FileClientHandler());
                        }
                    });

            ChannelFuture future = bootstrap.connect(HOST, PORT).sync();
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            group.shutdownGracefully();
        }
    }
}

(2) 定义客户端连接拦截器

public class FileClientHandler extends ChannelInboundHandlerAdapter {


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        FileRequest request = new FileRequest("D:\\coreconfig.txt");
        ctx.writeAndFlush(request);
    }


    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof FileResponse) {
            FileResponse response = (FileResponse) msg;
            File file = response.getFile();
            long fileLength = response.getLength();
            FileOutputStream fos = new FileOutputStream(file);
            FileChannel channel = fos.getChannel();
//            channel.transferFrom(channel, 0, fileLength);
            System.out.println("File " + file.getName() + " received.");
        } else {
            System.err.println("Invalid response type: " + msg.getClass());
        }
    }

}

转载:Netty+SpringBoot整合(一)-CSDN博客 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot集成Netty可以通过以下步骤完成: 1. 添加Netty依赖:在你的Spring Boot项目的pom.xml文件中添加Netty的依赖。 ```xml <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.53.Final</version> </dependency> ``` 2. 创建Netty服务器:创建一个Netty服务器类,该类需要继承自`io.netty.channel.ChannelInboundHandlerAdapter`。 ```java public class NettyServer extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { // 处理接收到的消息 // ... } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // 处理异常情况 // ... } @Override public void channelActive(ChannelHandlerContext ctx) { // 在连接建立时执行一些初始化操作 // ... } @Override public void channelInactive(ChannelHandlerContext ctx) { // 在连接关闭时执行一些清理操作 // ... } } ``` 3. 配置Netty服务器:在Spring Boot的配置文件中配置Netty服务器的相关参数,例如端口号、线程池等。 ```properties # application.properties netty.server.port=8080 netty.server.workerThreads=10 ``` 4. 启动Netty服务器:在Spring Boot的启动类中初始化并启动Netty服务器。 ```java @SpringBootApplication public class YourApplication { @Autowired private NettyServer nettyServer; public static void main(String[] args) { SpringApplication.run(YourApplication.class, args); } @PostConstruct public void startNettyServer() { // 获取Netty服务器配置参数 int port = Integer.parseInt(env.getProperty("netty.server.port")); int workerThreads = Integer.parseInt(env.getProperty("netty.server.workerThreads")); // 创建EventLoopGroup和ServerBootstrap EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(workerThreads); ServerBootstrap serverBootstrap = new ServerBootstrap(); try { // 设置服务器参数 serverBootstrap.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); // 添加自定义的Handler pipeline.addLast(nettyServer); } }); // 启动Netty服务器 ChannelFuture channelFuture = serverBootstrap.bind(port).sync(); channelFuture.channel().closeFuture().sync(); } catch (InterruptedException e) { // 处理启动异常 } finally { bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } } ``` 这样,你就成功地在Spring Boot项目中集成Netty。你可以在`NettyServer`类中编自定义的业务逻辑来处理接收到的消息,并在`channelRead`方法中进行处理。同时,你也可以根据需要在`exceptionCaught`、`channelActive`和`channelInactive`方法中处理异常、连接建立和连接关闭等事件。记得在服务器关闭时进行资源的释放和清理操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值