Spring Boot集成netty实现客户端服务端交互示例详解

Spring Boot集成Netty实现客户端服务端交互示例详解

介绍

Netty是一个基于Java的专业高性能网络通信框架,其提供了非常优秀的网络通信功能和容易扩展的API。而Spring Boot则是一个具有高度自动化和约定优于配置的约定框架,其简化了Spring的开发流程。通过将两者结合起来,可以更加轻松、方便地实现网络通信的开发。

本文将详细讲解如何通过Spring Boot集成Netty来实现客户端服务端交互,包括基础概念、实现过程和示例说明。

基础概念

Netty

Netty是一个异步事件驱动的网络通信框架,其提供了优秀的可扩展性、良好的性能和安全性。其核心是基于Java NIO(New IO)机制实现的,可以支持高并发、低延迟的网络通信。

常见的Netty应用场景包括:服务端和客户端通信、长连接、文件传输等场景。

Spring Boot

Spring Boot是一个构建基于Spring框架的独立应用程序的工具,其采用约定优于配置的方式简化了Spring的开发流程。Spring Boot 通过自动配置和快速开发可辅助开发人员更快更容易地搭建Spring应用程序。

实现过程

引入依赖

首先需要在Spring Boot中引入Netty和相关依赖:

<!-- netty依赖 -->
<dependency>
  <groupId>io.netty</groupId>
  <artifactId>netty-all</artifactId>
  <version>4.1.25.Final</version>
</dependency>

<!-- netty相关依赖 -->
<dependency>
  <groupId>io.netty</groupId>
  <artifactId>netty-common</artifactId>
  <version>4.1.25.Final</version>
</dependency>
<dependency>
  <groupId>io.netty</groupId>
  <artifactId>netty-handler</artifactId>
  <version>4.1.25.Final</version>
</dependency>
<dependency>
  <groupId>io.netty</groupId>
  <artifactId>netty-transport</artifactId>
  <version>4.1.25.Final</version>
</dependency>

定义通信协议

在进行网络通信时,需要定义通信协议,常见的协议格式有:长度+内容、TCP/IP等。这里以长度+内容两种协议为例。

长度+内容协议

使用长度+内容协议时,通信双方预定义好数据报文的格式,其中包含数据的长度和数据的内容。协议一般由两部分组成:消息头和消息体。消息头通常包含数据长度等元信息,消息体包含实际传输的业务数据。

定义一个LengthFieldPrepender类型的编码器,用于把消息长度添加到数据报前:

public class MyEncoder extends LengthFieldPrepender {
    public MyEncoder() {
        super(2);
    }

    @Override
    protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out) throws Exception {
        super.encode(ctx, msg, out);
    }
}

在服务端和客户端进行数据传输之前,需要添加MyEncoder编码器。

TCP/IP协议

在使用TCP/IP协议时,通信双方之间通过字节流传输数据,通信过程中使用Java NIO机制实现的Channel和Buffer。

定义一个简单的EchoServerHandler实现类,用于处理客户端连接和数据读写等事件:

public class EchoServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ctx.write(msg); // 写回客户端信息
        ctx.flush(); // 刷新缓冲区
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 
        // 异常关闭
        cause.printStackTrace();
        ctx.close();
    }
}

实现客户端和服务端

接下来实现客户端和服务端两个部分。

服务端实现

在服务端,需要实现如下的步骤:

  1. 创建EventLoopGroup实例,用于进行事件处理和分发。
  2. 创建ServerBootstrap实例,用于设置各种参数。
  3. 创建ServerChannel,用于接受客户端连接。
  4. 配置ChannelPipeline,以处理来自客户端的请求和响应。
  5. 绑定到指定的端口,开始监听请求。

完整代码如下:

@Component
public class EchoServer {
    private EventLoopGroup parentGroup = new NioEventLoopGroup();
    private EventLoopGroup childGroup = new NioEventLoopGroup();
    private ServerBootstrap bootstrap = new ServerBootstrap();

    public void start(int port) throws InterruptedException {
        try {
            bootstrap.group(parentGroup, childGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {

                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2));
                            pipeline.addLast(new MyDecoder());
                            pipeline.addLast(new MyEncoder());
                            pipeline.addLast(new EchoServerHandler());
                        }
                    });
            ChannelFuture channelFuture = bootstrap.bind(port).sync();
            channelFuture.channel().closeFuture().sync();
        } finally {
            childGroup.shutdownGracefully();
            parentGroup.shutdownGracefully();
        }
    }
}

客户端实现

在客户端,需要实现如下的步骤:

  1. 创建EventLoopGroup实例,用于进行事件处理和分发。
  2. 创建Bootstrap实例,用于设置各种参数。
  3. 创建Channel,用于与服务端进行通信。
  4. 配置ChannelPipeline,以处理来自服务端的响应和请求。

完整代码如下:

@Component
public class EchoClient {
    private String host = "localhost";
    private int port = 9999;
    private EventLoopGroup group = new NioEventLoopGroup();
    private Channel channel;

    public void start() throws InterruptedException {
        try {
            Bootstrap bootstrap = new Bootstrap()
                    .group(group)
                    .channel(NioSocketChannel.class)
                    .remoteAddress(new InetSocketAddress(host, port))
                    .option(ChannelOption.SO_KEEPALIVE, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2));
                            pipeline.addLast(new MyDecoder());
                            pipeline.addLast(new MyEncoder());
                            pipeline.addLast(new EchoClientHandler());
                        }
                    });
            channel = bootstrap.connect().sync().channel();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void sendMessage(Object message) {
        channel.writeAndFlush(message);
    }

    public void close() {
        channel.close();
        group.shutdownGracefully();
    }
}

示例说明

示例一:长度+内容协议

在长度+内容协议的示例中,定义了如下的协议格式:

长度 内容
2byte 实际传输数据

具体实现步骤如下:

  1. 在MyEncoder编码器中,继承了LengthFieldPrepender类,用于把消息长度添加到数据报前。
  2. 在EchoServer实现类中,使用LengthFieldBasedFrameDecoder解码器解决粘包/半包问题。

其中,EchoServer部分代码为:

public void start(int port) throws InterruptedException {
    try {
        bootstrap.group(parentGroup, childGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.SO_BACKLOG, 1024)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .childHandler(new ChannelInitializer<SocketChannel>() {

                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        ChannelPipeline pipeline = socketChannel.pipeline();
                        pipeline.addLast(new LengthFieldBasedFrameDecoder(65535, 0, 2, 0, 2));
                        pipeline.addLast(new MyDecoder());
                        pipeline.addLast(new MyEncoder());
                        pipeline.addLast(new EchoServerHandler());
                    }
                });
        ChannelFuture channelFuture = bootstrap.bind(port).sync();
        channelFuture.channel().closeFuture().sync();
    } finally {
        childGroup.shutdownGracefully();
        parentGroup.shutdownGracefully();
    }
}

示例二:TCP/IP协议

在TCP/IP协议的示例中,直接使用SocketChannel和EchoServerHandler等接口实现。

具体代码如下:

@Component
public class EchoServer {
    public void start(int port) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new EchoServerHandler());
                        }
                    });

            ChannelFuture future = serverBootstrap.bind(port).sync();
            System.out.println("Server started and listen on " + future.channel().localAddress());
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

同时,也需要实现一个EchoClient测试类,用于连接服务端并发送请求指令。

参考代码如下:

@Component
public class EchoClient {
    public void start(String host, int port) throws InterruptedException {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new EchoClientHandler());
                        }
                    });

            ChannelFuture future = bootstrap.connect(host, port).sync();
            future.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
}

总结

本文通过Spring Boot集成Netty的实际操作,分别从基础概念、实现过程和示例说明三个方面讲解了如何使用Netty实现客户端服务端交互,以及两个示例的实际代码。其中,长度+内容协议和TCP/IP协议都有所涉及,可以根据需要选择相应的协议。Netty提供了非常好的网络通信功能和易于扩展的API,而Spring Boot则提供了高度自动化的开发流程和简化的配置方式,不论是开发新的项目还是维护和扩展已有项目,都可以将两者结合起来,实现更加高效的网络通信开发。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring Boot集成netty实现客户端服务端交互示例详解 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • stm32的常用库函数

    stm32的常用库函数 STM32是一款非常流行的微控制器,其拥有强大的处理能力和丰富的外设,可以应用于许多不同的领域。相比于其他微控制器,STM32的库函数由官方提供,极大地方便了开发者的编程。本文将介绍STM32常用的库函数,并提供相应的代码实例。 延时函数 在开发中,我们经常需要进行一些时间上的延时操作,比如等待外设初始化完成、等待数据传输完成等。此时…

    其他 2023年3月28日
    00
  • php的socket编程详解

    PHP的Socket编程详解 简介 Socket编程是一种基于网络编程的方式,可以在网络上不同主机之间进行数据传输。在PHP中,可以利用socket技术进行网络编程,实现网络协议通信、远程调用、实时传输等功能。 原理 Socket是一种相对底层的网络通信模式。Socket通信过程中,需要一个主机作为服务器,另一个主机作为客户端,客户端通过连接服务器来完成数据…

    other 2023年6月27日
    00
  • 一起来看看C语言的预处理注意点

    C语言的预处理器是一种特殊的程序,用于在编译程序之前将源代码进行变换。预处理器会在代码被编译之前对源码文件进行大量处理,例如替换宏定义、条件编译、文件包含等操作。但是,由于预处理器还有一些欠缺之处,因此在学习和使用时需要注意以下几点。 1. 宏定义不要过长 宏定义中的内容应该尽量简单明了,不要太长,否则会让代码读起来难以理解。此外,宏定义中的符号或字符串应该…

    other 2023年6月26日
    00
  • C语言菜鸟基础教程之自定义函数

    C语言菜鸟基础教程之自定义函数是一篇介绍如何在C语言中定义自己的函数的文章。 定义自定义函数的语法 定义自定义函数的语法如下: 返回类型 函数名(参数列表) { 函数体 } 其中, 返回类型:表示函数的返回值类型,可以是任意一种C语言的数据类型。 函数名:表示函数的名称,可以自定义。 参数列表:表示在调用函数时传递给函数的参数,可以是任意一种C语言的数据类型…

    other 2023年6月25日
    00
  • php指定时间戳/日期加一天 一年 一周 一月

    PHP指定时间戳/日期加一天/一年/一周/一月 在开发Web应用程序时,经常需要对时间进行处理。对于PHP开发人员而言,PHP有大量内置函数可以使处理时间更加方便。 下面将介绍如何使用PHP来指定时间戳/日期加一天/一年/一周/一月。 指定时间戳加一天 在PHP中,使用strtotime和date函数可以实现对指定时间戳进行加天数的操作。代码如下: $dat…

    其他 2023年3月28日
    00
  • isp算法:深入聊聊lensshading

    ISP算法:深入聊聊Lens Shading 在数字图像处理中,ISP(Image Signal Processing,图像信号处理)是一个重要的概念。它涉及到诸如降噪、增强对比度、颜色校正等过程,可以让拍摄的图像更加鲜明、逼真。 而Lens Shading(镜头阴影)则是ISP中的一个非常重要的步骤。本文将深入介绍Lens Shading算法的原理和实际应…

    其他 2023年3月28日
    00
  • 浅析Golang中变量与常量的声明与使用

    浅析Golang中变量与常量的声明与使用 变量声明与使用 在Golang中,变量的声明与使用非常简洁明了。可以通过以下步骤来声明和使用变量: 使用关键字var声明变量,后面跟上变量名和类型。例如: var age int 可以在声明变量的同时进行初始化,使用等号=赋值。例如: var name string = \"John\" 如果变量…

    other 2023年8月9日
    00
  • cad出现向程序发送命令时出现问题提示解决方法分享

    CAD出现向程序发送命令时出现问题提示解决方法分享 CAD是一个广泛使用的专业绘图软件,用于制作2D和3D图形。在使用CAD时,可能会遇到一个向程序发送命令时出现问题的错误提示,这会影响我们的工作效率和结果。本篇文章将分享如何解决这个问题。 问题表现 向程序发送命令时出现问题的错误提示可能会表现为以下几种情况: 在命令行中输入命令或点击工具栏的命令按钮时,C…

    其他 2023年3月28日
    00
合作推广
合作推广
分享本页
返回顶部