当前位置:   article > 正文

Springboot+Netty搭建基于TCP协议的服务端(一)_spring boot netty

spring boot netty

Netty是业界最流行的nio框架之一,它具有功能强大、性能优异、可定制性和可扩展性的优点

Netty的优点:

1.API使用简单,开发入门门槛低。

2.功能十分强大,预置多种编码解码功能,支持多种主流协议。

3.可定制、可扩展能力强,可以通过其提供的ChannelHandler进行灵活的扩展。

4.性能优异,特别在综合性能上的优异性。

5.成熟,稳定,适用范围广。

6.可用于智能GSM/GPRS模块的通讯服务端开发,使用它进行MQTT协议的开发。

1、Netty结合Springboot快速开发框架搭建服务端程序:

  1. <properties>
  2. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  3. <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  4. <java.version>1.8</java.version>
  5. </properties>
  6. <parent>
  7. <groupId>org.springframework.boot</groupId>
  8. <artifactId>spring-boot-starter-parent</artifactId>
  9. <version>2.1.6.RELEASE</version>
  10. <relativePath /> <!-- lookup parent from repository -->
  11. </parent>
  12. <dependencies>
  13. <!--web模块的启动器 -->
  14. <dependency>
  15. <groupId>org.springframework.boot</groupId>
  16. <artifactId>spring-boot-starter-web</artifactId>
  17. </dependency>
  18. <!-- netty依赖 springboot2.x自动导入版本 -->
  19. <dependency>
  20. <groupId>io.netty</groupId>
  21. <artifactId>netty-all</artifactId>
  22. </dependency>
  23. </dependencies>

2、Springboot启动类,Netty启动

  1. package boot.netty.base.server;
  2. import org.springframework.boot.CommandLineRunner;
  3. import org.springframework.boot.SpringApplication;
  4. //import org.springframework.boot.WebApplicationType;
  5. import org.springframework.boot.autoconfigure.SpringBootApplication;
  6. import org.springframework.scheduling.annotation.Async;
  7. import org.springframework.scheduling.annotation.EnableAsync;
  8. @SpringBootApplication
  9. @EnableAsync
  10. public class BootNettyApplication implements CommandLineRunner{
  11. public static void main( String[] args )
  12. {
  13. /**
  14. * 启动springboot
  15. */
  16. SpringApplication app = new SpringApplication(BootNettyApplication.class);
  17. //app.setWebApplicationType(WebApplicationType.NONE);//不启动web服务
  18. app.run(args);
  19. System.out.println( "Hello World!" );
  20. }
  21. @Async
  22. @Override
  23. public void run(String... args) throws Exception {
  24. /**
  25. * 使用异步注解方式启动netty服务端服务
  26. */
  27. new BootNettyServer().bind(8888);
  28. }
  29. }

3、Netty的server类

  1. package boot.netty.base.server;
  2. import boot.netty.base.server.channel.BootNettyChannelInitializer;
  3. import io.netty.bootstrap.ServerBootstrap;
  4. import io.netty.channel.AdaptiveRecvByteBufAllocator;
  5. import io.netty.channel.ChannelFuture;
  6. import io.netty.channel.ChannelOption;
  7. import io.netty.channel.EventLoopGroup;
  8. import io.netty.channel.nio.NioEventLoopGroup;
  9. import io.netty.channel.socket.SocketChannel;
  10. import io.netty.channel.socket.nio.NioServerSocketChannel;
  11. /**
  12. * netty的server
  13. *
  14. */
  15. public class BootNettyServer {
  16. public void bind(int port) throws Exception {
  17. /**
  18. * 配置服务端的NIO线程组
  19. * NioEventLoopGroup 是用来处理I/O操作的Reactor线程组
  20. * bossGroup:用来接收进来的连接,workerGroup:用来处理已经被接收的连接,进行socketChannel的网络读写,
  21. * bossGroup接收到连接后就会把连接信息注册到workerGroup
  22. * workerGroup的EventLoopGroup默认的线程数是CPU核数的二倍
  23. */
  24. EventLoopGroup bossGroup = new NioEventLoopGroup(1);
  25. EventLoopGroup workerGroup = new NioEventLoopGroup();
  26. try {
  27. /**
  28. * ServerBootstrap 是一个启动NIO服务的辅助启动类
  29. */
  30. ServerBootstrap serverBootstrap = new ServerBootstrap();
  31. /**
  32. * 设置group,将bossGroup, workerGroup线程组传递到ServerBootstrap
  33. */
  34. serverBootstrap = serverBootstrap.group(bossGroup, workerGroup);
  35. /**
  36. * ServerSocketChannel是以NIO的selector为基础进行实现的,用来接收新的连接,这里告诉Channel通过NioServerSocketChannel获取新的连接
  37. */
  38. serverBootstrap = serverBootstrap.channel(NioServerSocketChannel.class);
  39. /**
  40. * option是设置 bossGroup,childOption是设置workerGroup
  41. * netty 默认数据包传输大小为1024字节, 设置它可以自动调整下一次缓冲区建立时分配的空间大小,避免内存的浪费 最小 初始化 最大 (根据生产环境实际情况来定)
  42. * 使用对象池,重用缓冲区
  43. */
  44. serverBootstrap = serverBootstrap.option(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(64, 10496, 1048576));
  45. serverBootstrap = serverBootstrap.childOption(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(64, 10496, 1048576));
  46. /**
  47. * 设置 I/O处理类,主要用于网络I/O事件,记录日志,编码、解码消息
  48. */
  49. serverBootstrap = serverBootstrap.childHandler(new BootNettyChannelInitializer<SocketChannel>());
  50. System.out.println("netty server start success!");
  51. /**
  52. * 绑定端口,同步等待成功
  53. */
  54. ChannelFuture f = serverBootstrap.bind(port).sync();
  55. /**
  56. * 等待服务器监听端口关闭
  57. */
  58. f.channel().closeFuture().sync();
  59. } catch (InterruptedException e) {
  60. } finally {
  61. /**
  62. * 退出,释放线程池资源
  63. */
  64. bossGroup.shutdownGracefully();
  65. workerGroup.shutdownGracefully();
  66. }
  67. }
  68. }

4、通道初始化

  1. package boot.netty.base.server.channel;
  2. import boot.netty.base.server.adapter.BootNettyChannelInboundHandlerAdapter;
  3. import io.netty.channel.Channel;
  4. import io.netty.channel.ChannelInitializer;
  5. import io.netty.handler.codec.string.StringDecoder;
  6. import io.netty.handler.codec.string.StringEncoder;
  7. /**
  8. * 通道初始化
  9. */
  10. public class BootNettyChannelInitializer<SocketChannel> extends ChannelInitializer<Channel> {
  11. @Override
  12. protected void initChannel(Channel ch) throws Exception {
  13. // ChannelOutboundHandler,依照逆序执行
  14. ch.pipeline().addLast("encoder", new StringEncoder());
  15. // 属于ChannelInboundHandler,依照顺序执行
  16. ch.pipeline().addLast("decoder", new StringDecoder());
  17. /**
  18. * 自定义ChannelInboundHandlerAdapter
  19. */
  20. ch.pipeline().addLast(new BootNettyChannelInboundHandlerAdapter());
  21. }
  22. }

5、I/O数据读写处理类

  1. package boot.netty.base.server.adapter;
  2. import java.io.IOException;
  3. import java.net.InetSocketAddress;
  4. import io.netty.channel.ChannelHandlerContext;
  5. import io.netty.channel.ChannelInboundHandlerAdapter;
  6. /**
  7. * I/O数据读写处理类
  8. *
  9. */
  10. public class BootNettyChannelInboundHandlerAdapter extends ChannelInboundHandlerAdapter{
  11. /**
  12. * 从客户端收到新的数据时,这个方法会在收到消息时被调用
  13. *
  14. * @param ctx
  15. * @param msg
  16. */
  17. @Override
  18. public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception, IOException
  19. {
  20. System.out.println("channelRead:read msg:"+msg.toString());
  21. //回应客户端
  22. ctx.write("I got it");
  23. }
  24. /**
  25. * 从客户端收到新的数据、读取完成时调用
  26. *
  27. * @param ctx
  28. */
  29. @Override
  30. public void channelReadComplete(ChannelHandlerContext ctx) throws IOException
  31. {
  32. System.out.println("channelReadComplete");
  33. ctx.flush();
  34. }
  35. /**
  36. * 当出现 Throwable 对象才会被调用,即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时
  37. *
  38. * @param ctx
  39. * @param cause
  40. */
  41. @Override
  42. public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws IOException
  43. {
  44. System.out.println("exceptionCaught");
  45. cause.printStackTrace();
  46. ctx.close();//抛出异常,断开与客户端的连接
  47. }
  48. /**
  49. * 客户端与服务端第一次建立连接时 执行
  50. *
  51. * @param ctx
  52. * @throws Exception
  53. */
  54. @Override
  55. public void channelActive(ChannelHandlerContext ctx) throws Exception, IOException
  56. {
  57. super.channelActive(ctx);
  58. ctx.channel().read();
  59. InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
  60. String clientIp = insocket.getAddress().getHostAddress();
  61. //此处不能使用ctx.close(),否则客户端始终无法与服务端建立连接
  62. System.out.println("channelActive:"+clientIp+ctx.name());
  63. }
  64. /**
  65. * 客户端与服务端 断连时 执行
  66. *
  67. * @param ctx
  68. * @throws Exception
  69. */
  70. @Override
  71. public void channelInactive(ChannelHandlerContext ctx) throws Exception, IOException
  72. {
  73. super.channelInactive(ctx);
  74. InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
  75. String clientIp = insocket.getAddress().getHostAddress();
  76. ctx.close(); //断开连接时,必须关闭,否则造成资源浪费,并发量很大情况下可能造成宕机
  77. System.out.println("channelInactive:"+clientIp);
  78. }
  79. /**
  80. * 服务端当read超时, 会调用这个方法
  81. *
  82. * @param ctx
  83. * @param evt
  84. * @throws Exception
  85. */
  86. @Override
  87. public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception, IOException
  88. {
  89. super.userEventTriggered(ctx, evt);
  90. InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
  91. String clientIp = insocket.getAddress().getHostAddress();
  92. ctx.close();//超时时断开连接
  93. System.out.println("userEventTriggered:"+clientIp);
  94. }
  95. @Override
  96. public void channelRegistered(ChannelHandlerContext ctx) throws Exception{
  97. System.out.println("channelRegistered");
  98. }
  99. @Override
  100. public void channelUnregistered(ChannelHandlerContext ctx) throws Exception{
  101. System.out.println("channelUnregistered");
  102. }
  103. @Override
  104. public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception{
  105. System.out.println("channelWritabilityChanged");
  106. }
  107. }

仅仅四个类就将Springboot和Netty结合,启动Springboot应用的同时也就启动了Netty,端口:8888

可以使用TCP客户端工具:

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/185421
推荐阅读
相关标签
  

闽ICP备14008679号