SpringBoot整合Netty,实现TCP通信
-
组成部分为:SpringBoot + Mybatis + netty;
-
开发环境为Java8 ,IDEA,Maven;
-
开始教程
-
使用 idea 创建 SpringBoot 项目;
-
创建完成后在 pom.xml 文件 dependencies 写入以下代码:
<!--netty--> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.17.Final</version> </dependency>
-
创建三个 Netty 所需类
-
NettyServer 为服务器监听器
package cn.han.han_parse.service; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import lombok.extern.slf4j.Slf4j; import java.net.InetSocketAddress; /** * @author hanyiming * @create 2021/9/9 10:08 */ @Slf4j public class NettyServer { public void start() { System.out.println("这是Netty"); InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 8080); //new 一个主线程组 EventLoopGroup bossGroup = new NioEventLoopGroup(1); //new 一个工作线程组 EventLoopGroup workGroup = new NioEventLoopGroup(200); ServerBootstrap bootstrap = new ServerBootstrap() .group(bossGroup, workGroup) .channel(NioServerSocketChannel.class) .childHandler(new ServerChannelInitializer()) .localAddress(socketAddress) //设置队列大小 .option(ChannelOption.SO_BACKLOG, 1024) // 两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文 .childOption(ChannelOption.SO_KEEPALIVE, true); //绑定端口,开始接收进来的连接 try { ChannelFuture future = bootstrap.bind(socketAddress).sync(); log.info("服务器启动开始监听端口: {}", socketAddress.getPort()); future.channel().closeFuture().sync(); } catch (InterruptedException e) { log.error("服务器开启失败", e); } finally { //关闭主线程组 bossGroup.shutdownGracefully(); //关闭工作线程组 workGroup.shutdownGracefully(); } } }
-
NettyServerHandler 为处理器
- 注意在此处如果没写Controller层,需要在本类中添加@Component 注解,并初始化所需要的service层和mapper层;如果不需要可以删除,在以下代码中有@Component 注解;
package cn.han.han_parse.service; import cn.han.han_parse.dao.BasicMapper; import cn.han.han_parse.entity.Basic; import cn.han.han_parse.util.ParseMessage; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; /** * @author hanyiming * @create 2021/9/9 10:12 */ @Slf4j @Component public class NettyServerHandler extends ChannelInboundHandlerAdapter { public static NettyServerHandler nettyServerHandler; @Autowired private BasicService basicService; @Autowired private BasicMapper basicMapper; @PostConstruct public void init() { nettyServerHandler = this; nettyServerHandler.basicService = this.basicService; nettyServerHandler.basicMapper = this.basicMapper; // 初使化时将已静态化的testService实例化 } /** * 客户端连接会触发 */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { log.info("连接成功..."); } /** * 客户端发消息会触发 */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { log.info("服务器收到消息: {}", msg.toString()); //此处为解析tcp参数所需要的方法!!!自定义 /*final Basic parse = ParseMessage.parse(ctx, msg.toString()); nettyServerHandler.basicService.addBasic(parse);*/ ctx.write("你也好哦"); ctx.flush(); } /** * 发生异常触发 */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
-
ServerChannelInitializer 服务初始化器,
package cn.han.han_parse.service; import cn.han.han_parse.util.MyDecode; import io.netty.channel.ChannelInitializer; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.util.CharsetUtil; import lombok.extern.slf4j.Slf4j; /** * @author hanyiming * @create 2021/9/9 10:10 */ @Slf4j public class ServerChannelInitializer extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { //添加编解码,此处代码为解析tcp传过来的参数,为UTF-8格式,可以自定义解码格式 socketChannel.pipeline().addLast("decoder", new StringDecoder(CharsetUtil.UTF_8)); socketChannel.pipeline().addLast("encoder", new StringEncoder(CharsetUtil.UTF_8)); socketChannel.pipeline().addLast(new NettyServerHandler()); } }
-
此处为我所需的进制转换规则:
/** * 十六进制转换为十进制 * * @param content * @return */ public static int covert(String content) { int number = 0; String[] HighLetter = {"A", "B", "C", "D", "E", "F"}; Map<String, Integer> map = new HashMap<>(); for (int i = 0; i <= 9; i++) { map.put(i + "", i); } for (int j = 10; j < HighLetter.length + 10; j++) { map.put(HighLetter[j - 10], j); } String[] str = new String[content.length()]; for (int i = 0; i < str.length; i++) { str[i] = content.substring(i, i + 1); } for (int i = 0; i < str.length; i++) { number += map.get(str[i]) * Math.pow(16, str.length - 1 - i); } return number; } /** * 十进制转换为ASCII码 * * @param ascii * @return */ public static String dec2Str(String ascii) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < ascii.length() - 1; i += 2) { String h = ascii.substring(i, (i + 2)); // 这里第二个参数传10表10进制 int decimal = Integer.parseInt(h, 10); sb.append((char) decimal); } return sb.toString(); }
-
-