Springboot集成Netty
添加依赖
<!-- https://mvnrepository.com/artifact/io.netty/netty-all --> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.72.Final</version> </dependency>
创建服务
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder; import java.net.InetSocketAddress; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; /** * Netty 服务端 * * @author fangjiaxiaobai * @date 2022-01-07 23:37 */ @Slf4j @Component public class RouterServer2 { /** * boss 线程组用于处理连接工作 */ private final EventLoopGroup boss = new NioEventLoopGroup(); /** * work 线程组用于数据处理 */ private final EventLoopGroup work = new NioEventLoopGroup(); @Resource private ChannelHandler childHandler; /** * SpringBoot 启动的时候 调用 * * @throws InterruptedException 中断异常 */ @PostConstruct public void init() throws InterruptedException { ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(boss, work) // 指定Channel .channel(NioServerSocketChannel.class) //使用指定的端口设置套接字地址 .localAddress(new InetSocketAddress(7788)) //服务端可连接队列数,对应TCP/IP协议listen函数中backlog参数 .option(ChannelOption.SO_BACKLOG, 1024) //设置TCP长连接,一般如果两个小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文 .childOption(ChannelOption.SO_KEEPALIVE, true) //将小的数据包包装成更大的帧进行传送,提高网络的负载,即TCP延迟传输 .childOption(ChannelOption.TCP_NODELAY, true) .childHandler(new ChannelInitializer<Channel>() { ④ @Override protected void initChannel(Channel ch) throws Exception { ch.pipeline() //添加编码器 ⓪ .addLast(new RouterMessageEncode()) //添加Netty 自带的 换行解码器(用来解决 沾包,拆包) ① .addLast(new LineBasedFrameDecoder(1024)) //添加自定义的 解码器 ② .addLast(new RouterMessageDecode()) //添加 接收消息的 处理器 ③ .addLast(new ServiceMessageReceiveHandler()); } }); ChannelFuture future = bootstrap.bind().sync(); if (future.isSuccess()) { log.info("start router Server success on port 7788"); } } /** * SpringBoot 销毁的时候 调用 * * @throws InterruptedException 中断异常 */ @PreDestroy public void destory() throws InterruptedException { boss.shutdownGracefully().sync(); work.shutdownGracefully().sync(); log.info("关闭Netty"); } }
🔥 ⓪:RouterMessageEncode
: 这个是我们自己实现的消息编码器。
🔥①: 使用Netty
提供的包处理工具,处理粘包问题已经用来拆包。
🔥②: 自定义的解码器。
🔥③: 自定义的消息处理器。 通用用来编写业务逻辑。
🔥④: 这里配置的是在Netty
启动时,需要添加的 Channel
.
自定义编码器
import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageEncoder; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.util.List; import lombok.extern.slf4j.Slf4j; /** * 编码器 * * @author fangjiaxiaobai * @date 2022-01-07 23:50 */ @Slf4j public class RouterMessageEncode extends MessageToMessageEncoder<String> { @Override protected void encode(ChannelHandlerContext channelHandlerContext, String message, List<Object> list) throws Exception { list.add(ByteBufUtil.encodeString(channelHandlerContext.alloc(), CharBuffer.wrap(message), Charset.defaultCharset())); } }
自定义解码器
import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageDecoder; import java.nio.charset.Charset; import java.util.List; import lombok.extern.slf4j.Slf4j; /** * 解码器 * * @author fangjiaxiaobai * @date 2022-01-07 23:54 */ @Slf4j public class RouterMessageDecode extends MessageToMessageDecoder<ByteBuf> { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> list) throws Exception { // todo 解码 String message = msg.toString(Charset.defaultCharset()); list.add(message); } }
消息处理逻辑
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.timeout.IdleStateEvent; import lombok.extern.slf4j.Slf4j; /** * 消息处理器 * * @author fangjiaxiaobai * @date 2022-01-07 23:59 */ @Slf4j public class ServiceMessageReceiveHandler extends SimpleChannelInboundHandler<String> { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println("收到消息:" + msg); } /** * 注册一个 channel * * @param ctx ChannelHandlerContext * @throws Exception 异常 */ @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { super.channelRegistered(ctx); log.info("channel registed ..."); } /** * channel 退出 * * @param ctx * @throws Exception */ @Override public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { super.channelUnregistered(ctx); log.info("channel unregistered ...."); } /** * 接收到的事件 * * @param ctx ChannelHandlerContext * @param evt Object * @throws Exception 异常 */ @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { super.userEventTriggered(ctx, evt); if (evt instanceof IdleStateEvent) { IdleStateEvent event = (IdleStateEvent) evt; System.out.println("event:" + event.state()); } } /** * 通道异常 应该在此关闭通道 * * @param ctx * @param cause * @throws Exception */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { super.exceptionCaught(ctx, cause); } }
测试
编写一个客户端进行测试
import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.Delimiters; import io.netty.handler.codec.string.StringEncoder; /** * @author fangjiaxiaobai * @date 2022-01-08 00:40 */ public class RouteClient { public static void main(String[] args) throws InterruptedException { Bootstrap bootstrap = new Bootstrap(); EventLoopGroup group = new NioEventLoopGroup(); bootstrap.group(group); // 第2步 绑定客户端通道 bootstrap.channel(NioSocketChannel.class); //第3步 给NIoSocketChannel初始化handler, 处理读写事件 //通道是NioSocketChannel bootstrap.handler(new ChannelInitializer<NioSocketChannel>() { @Override protected void initChannel(NioSocketChannel ch) throws Exception { //字符串编码器,一定要加在SimpleClientHandler 的上面 ch.pipeline().addLast(new StringEncoder()) .addLast(new DelimiterBasedFrameDecoder( Integer.MAX_VALUE, Delimiters.lineDelimiter()[0])); } }); //连接服务器 ChannelFuture future = bootstrap.connect("localhost", 7788).sync(); System.out.println(future.isSuccess()); for (int i = 0; i < 10; i++) { future.channel().writeAndFlush("test netty message\r\n").sync(); ⑤ } System.out.println("发送成功。。。。。"); } }
🔥⑤ 注意这里,需要加上 \r\n
, 否则你永远收不到消息。
最后
期望与你一起遇见更好的自己
扫码或搜索:方家小白
发送 RFUEDS
即可立即永久解锁本站全部文章

本文作者:方家小白
本文链接:https://www.cnblogs.com/wxy540843763/p/15776474.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步