- 1. 概述
- 2. 构建 Netty 服务端与客户端
- 3. 通信协议
- 4. 消息分发
- 5. 断开重连
- 6. 心跳机制与空闲检测
- 7. 认证逻辑
- 8. 单聊逻辑
- 9. 群聊逻辑
- 666. 彩蛋
“1. 概述本文在提供完整代码示例,可见 https://github.com/YunaiV/SpringBoot-Labs 的 lab-67 目录。
原创不易,给点个 Star 嘿,一起冲鸭!
在《芋道 Spring Boot WebSocket 入门》文章中,我们使用 WebSocket 实现了一个简单的 IM 功能,支持身份认证、私聊消息、群聊消息。
然后就有胖友私信艿艿,希望使用纯 Netty 实现一个类似的功能。良心的艿艿,当然不会给她发红人卡,因此就有了本文。可能有胖友不知道 Netty 是什么,这里简单介绍下:
“Netty 是一个 Java 开源框架。
Netty 提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
也就是说,Netty 是一个基于 NIO 的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。
Netty 相当简化和流线化了网络应用的编程开发过程,例如,TCP 和 UDP 的 Socket 服务开发。
下面,我们来新建三个项目,如下图所示:
三个项目
lab-67-netty-demo-server
项目:搭建 Netty 服务端。lab-67-netty-demo-client
项目:搭建 Netty 客户端。lab-67-netty-demo-common
项目:提供 Netty 的基础封装,提供消息的编解码、分发的功能。
另外,我们也会提供 Netty 常用功能的示例:
- 心跳机制,实现服务端对客户端的存活检测。
- 断线重连,实现客户端对服务端的重新连接。
不哔哔,直接开干。
“2. 构建 Netty 服务端与客户端友情提示:可能会胖友担心,没有 Netty 基础是不是无法阅读本文?!
艿艿的想法,看!就硬看,按照代码先自己能搭建一下哈~文末,艿艿会提供一波 Netty 基础入门的文章。
本小节,我们先来使用 Netty 构建服务端与客户端的核心代码,让胖友对项目的代码有个初始的认知。
2.1 构建 Netty 服务端
创建 lab-67-netty-demo-server
项目,搭建 Netty 服务端。如下图所示:
项目结构
下面,我们只会暂时看看 server
包下的代码,避免信息量过大,击穿胖友的秃头。
2.1.1 NettyServer
创建 NettyServer 类,Netty 服务端。代码如下:
@Component
public class NettyServer {
private Logger logger = LoggerFactory.getLogger(getClass());
@Value("${netty.port}")
private Integer port;
@Autowired
private NettyServerHandlerInitializer nettyServerHandlerInitializer;
/**
* boss 线程组,用于服务端接受客户端的连接
*/
private EventLoopGroup bossGroup = new NioEventLoopGroup();
/**
* worker 线程组,用于服务端接受客户端的数据读写
*/
private EventLoopGroup workerGroup = new NioEventLoopGroup();
/**
* Netty Server Channel
*/
private Channel channel;
/**
* 启动 Netty Server
*/
@PostConstruct
public void start() throws InterruptedException {
// <2.1> 创建 ServerBootstrap 对象,用于 Netty Server 启动
ServerBootstrap bootstrap = new ServerBootstrap();
// <2.2> 设置 ServerBootstrap 的各种属性
bootstrap.group(bossGroup, workerGroup) // <2.2.1> 设置两个 EventLoopGroup 对象
.channel(NioServerSocketChannel.class) // <2.2.2> 指定 Channel 为服务端 NioServerSocketChannel
.localAddress(new InetSocketAddress(port)) // <2.2.3> 设置 Netty Server 的端口
.option(ChannelOption.SO_BACKLOG, 1024) // <2.2.4> 服务端 accept 队列的大小
.childOption(ChannelOption.SO_KEEPALIVE, true) // <2.2.5> TCP Keepalive 机制,实现 TCP 层级的心跳保活功能
.childOption(ChannelOption.TCP_NODELAY, true) // <2.2.6> 允许较小的数据包的发送,降低延迟
.childHandler(nettyServerHandlerInitializer);
// <2> 绑定端口,并同步等待成功,即启动服务端
ChannelFuture future = bootstrap.bind().sync();
if (future.isSuccess()) {
channel = future.channel();
logger.info("[start][Netty Server 启动在 {} 端口]", port);
}
}
/**
* 关闭 Netty Server
*/
@PreDestroy
public void shutdown() {
// <3.1> 关闭 Netty Server
if (channel != null) {
channel.close();
}
// <3.2> 优雅关闭两个 EventLoopGroup 对象
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}