当前位置: 首页 > news >正文

Spring Boot 从Socket 到Netty网络编程(下):Netty基本开发与改进【心跳、粘包与拆包、闲置连接】

上一篇:《Spring Boot 从Socket 到Netty网络编程(上):SOCKET 基本开发(BIO)与改进(NIO)》

前言

        前文中我们简单介绍了基于Socket的BIO(阻塞式)与NIO(非阻塞式)网络编程,无疑NIO在网络传输中具备更先进;但是采用Socket的NIO需要大量的优化,对于开发人员来说我们要站在巨人的肩膀上开发,所以Netty才是真爱。

        TCP/IP  -> Socket ->Netty 是这样一个关系,        Netty是基于Socket,Socket是基于TCP/IP。

基本开发

Netty

个人总结:基于NIO,提供简洁的API,开发者专注于业务逻辑实现,而非关注于实现实现细节。

maven

<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.94.Final</version>
</dependency>

服务端 ServerBootstrap

服务

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
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.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;public class NettyServer {public static void main(String[] args) throws Exception {// 创建一个线程组,用于接收客户端的连接EventLoopGroup bossGroup = new NioEventLoopGroup();// 创建数据处理线程组,用于处理已经连接的客户端的数据读写操作EventLoopGroup workerGroup = new NioEventLoopGroup();try {//创建服务端的启动对象,设置参数ServerBootstrap bootstrap = new ServerBootstrap();//设置两个线程组boosGroup和workerGroupbootstrap.group(bossGroup, workerGroup)//设置服务端通道实现类型.channel(NioServerSocketChannel.class)//设置线程队列得到连接个数.option(ChannelOption.SO_BACKLOG, 128)//设置保持活动连接状态.childOption(ChannelOption.SO_KEEPALIVE, true)//使用匿名内部类的形式初始化通道对象.childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel socketChannel) throws Exception {//给pipeline管道设置处理器socketChannel.pipeline().addLast(new NettyServerHandler());//服务端处理器}});//给workerGroup的EventLoop对应的管道设置处理器System.out.println("服务已启动...");//绑定端口号,启动服务端ChannelFuture channelFuture = bootstrap.bind(6666).sync();//对关闭通道进行监听channelFuture.channel().closeFuture().sync();} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}
}

方法类


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;public class NettyClient {public static void main(String[] args) throws Exception {NioEventLoopGroup eventExecutors = new NioEventLoopGroup();try {//创建bootstrap对象,配置参数Bootstrap bootstrap = new Bootstrap();//设置线程组bootstrap.group(eventExecutors)//设置客户端的通道实现类型.channel(NioSocketChannel.class)//使用匿名内部类初始化通道.handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {//添加客户端通道的处理器ch.pipeline().addLast(new NettyClientHandler());//给pipeline管道设置处理器}});System.out.println("客户端准备就绪");//连接服务端ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6666).sync();//对通道关闭进行监听channelFuture.channel().closeFuture().sync();} finally {//关闭线程组eventExecutors.shutdownGracefully();}}}

客户端 Bootstrap

客户端

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;public class NettyClient {public static void main(String[] args) throws Exception {NioEventLoopGroup eventExecutors = new NioEventLoopGroup();try {//创建bootstrap对象,配置参数Bootstrap bootstrap = new Bootstrap();//设置线程组bootstrap.group(eventExecutors)//设置客户端的通道实现类型.channel(NioSocketChannel.class)//使用匿名内部类初始化通道.handler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {//添加客户端通道的处理器ch.pipeline().addLast(new NettyClientHandler());//给pipeline管道设置处理器}});System.out.println("客户端准备就绪");//连接服务端ChannelFuture channelFuture = bootstrap.connect("127.0.0.1", 6666).sync();//对通道关闭进行监听channelFuture.channel().closeFuture().sync();} finally {//关闭线程组eventExecutors.shutdownGracefully();}}}

方法类

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;import java.io.BufferedReader;
import java.io.InputStreamReader;public class NettyClientHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {String userInput;System.out.println("请输入消息(输入 exit 断开连接):");BufferedReader console = new BufferedReader(new InputStreamReader(System.in));while ((userInput = console.readLine()) != null) {ctx.writeAndFlush(Unpooled.copiedBuffer(userInput , CharsetUtil.UTF_8));if ("exit".equalsIgnoreCase(userInput)) {System.out.println("连接已关闭。");break;}}}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {//接收服务端发送过来的消息ByteBuf byteBuf = (ByteBuf) msg;System.out.println("收到服务端" + ctx.channel().remoteAddress() + "的消息:" + byteBuf.toString(CharsetUtil.UTF_8));}
}

效果

优化加强

心跳机制

        为了解决主从之间的服务的断开与连接情况是否更深,所以需要用心跳做连接试验从而在业务上做断开重连等业务需求实现。

原理设计

  • IdleStateHandler 配置心跳
  • 继承类处理心跳事件:userEventTriggered
  • Pipeline中处理

 服务

方法

客户

方法

效果

处理粘包与拆包

        由于网络原因可能会出来数据切割不完整的情况,为了在拆解数据时能得到完整的数据需要用一定的约束使得双方统一成一个固定标准。

原理设计

  • 配置类型:一般使用动态长度 LengthFieldBasedFrameDecoder
  • 自定义加码与解码
  • 配置到 pipeline中去

代码

服务端

客户端

加码

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;import java.util.List;public class NetyyDecoder extends ByteToMessageDecoder {@Overrideprotected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {if (byteBuf.readableBytes() < 4){return;}byteBuf.markReaderIndex();int dataLength = byteBuf.readInt();byte[] data = new byte[dataLength];byteBuf.readBytes(data);list.add(new String(data, "UTF-8"));}
}

解码

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;import java.nio.charset.StandardCharsets;public class NetyyEncoder extends MessageToByteEncoder<String> {@Overrideprotected void encode(ChannelHandlerContext channelHandlerContext, String s, ByteBuf byteBuf) throws Exception {byte[] bytes = s.getBytes(StandardCharsets.UTF_8);int length = bytes.length;byteBuf.writeInt(length);byteBuf.writeBytes(bytes);}
}

后记

以上代码环境是JDK17、Spring Boot 3.X+ ,对Netty这个框架做了一些基本的介绍,当然凭这短短的一篇文章不可能做到详尽,仅供初学都参考。整理不易,如有帮助请收藏,转发请注明出处,关注博主不断更新更多技术文章。

相关文章:

  • FFMPEG 提取视频中指定起始时间及结束时间的视频,给出ffmpeg 命令
  • 数据库容量暴涨时优化方案
  • AI全栈之路:Ubuntu云服务器部署Spring + Vue + MySQL实践指南
  • 使用jstack排查CPU飙升的问题记录
  • 匀速旋转动画的终极对决:requestAnimationFrame vs CSS Animation
  • Redis 集群批量删除key报错 CROSSSLOT Keys in request don‘t hash to the same slot
  • GlobalSign、DigiCert、Sectigo三种SSL安全证书有什么区别?
  • 第二章 2.3 数据存储安全风险之数据存储风险防范
  • HRI-2025 | 大模型驱动的个性化可解释机器人人机交互研究
  • RabbitMQ 在解决数据库高并发问题中的定位和核心机制
  • Haystack:AI与IoT领域的全能开源框架
  • 快充诱骗协议芯片,支持全协议支持最大功率140W给产品快速供电
  • 一文读懂RAG流程中用到的请求参数与返回字段
  • 【JVM】Java类加载机制
  • 【YiFeiWebApi】新增根据ERP单据性质设定自动生成单号
  • Java 线程池原理详解
  • 电气架构/域控制器/中央计算平台技术论坛
  • Linux进程调度:从时间片到实时任务的交响乐
  • 02.TypeScript 接口和对象类型
  • 高性能图片优化方案
  • 做网站的知名品牌公司/网络营销概念
  • 怀化网络营销/seo技术培训价格表
  • 网站建设 武汉/友缘在线官网
  • 世界500强企业logo图片/潍坊关键词优化软件
  • 怎样在网站做宣传/seo搜索引擎优化求职简历
  • 建设什么网站可以赚钱/友情链接买卖代理