1、引入pom
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
2、创建服务netty服务器
package com.tan.netty;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@Slf4j
@Configuration
public class NettyServer {
//负责处理接受进来的链接
private EventLoopGroup bossGroup;
//负责处理已经被接收的连接上的I/O操作
private EventLoopGroup workerGroup;
//在这个场景中,它表示服务器的绑定操作的结果
private ChannelFuture future;
@PostConstruct
public void startServer() throws Exception {
int port = 9999;
bossGroup = new NioEventLoopGroup();
workerGroup = new NioEventLoopGroup();
//创建ServerBootstrap,这个类封装了服务器端的网络配置,使得我们可以轻松地设置服务器参数
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new NettyServerInitializer());
// 绑定端口并开始接受进来的连接
future = bootstrap.bind(port).sync();
// 等待服务器套接字关闭
// future.channel().closeFuture().sync();
if (future.isSuccess()) {
log.info("启动 Netty Server,端口:{}", port);
}
}
@PreDestroy
public void stopServer() {
if (future != null && !future.isDone()) {
future.cancel(true);
}
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
3、创建服务端初始化器
package com.tan.netty;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.CharsetUtil;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
public class NettyServerInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) {
ChannelPipeline pipeline = ch.pipeline();
{
//这段避免拆包、粘包,结束符为【#】
ByteBuf byteBuf = Unpooled.copiedBuffer("#".getBytes(StandardCharsets.UTF_8));
pipeline.addLast(new DelimiterBasedFrameDecoder(2048, byteBuf));
}
// 添加一个字符串解码器,用于将接收到的ByteBuf转换成字符串
// 这里假设使用的是UTF-8字符集
pipeline.addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
// 添加一个字符串编码器,用于将发送的字符串转换成ByteBuf
// 这样服务器发送字符串时,客户端可以直接接收到字符串
pipeline.addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
{
// 通道10S钟空闲就会触发 ChannelHandler1.userEventTriggered 方法
ch.pipeline().addLast(new IdleStateHandler(10, 0, 0, TimeUnit.SECONDS));
// 添加自定义的ChannelInboundHandlerAdapter来处理业务逻辑
//一般情况下一个处理器就够了,两个处理器只是为了试试自定义事件【CustomUserEvent】
pipeline.addLast(new ChannelHandler1());
pipeline.addLast(new ChannelHandler2());
//注意点:
// 1、仅当前代码,通道10S钟空闲就会触发 ChannelHandler1.userEventTriggered 方法,但是并不会触发 ChannelHandler2.userEventTriggered 方法
// 这个是因为没有复写了父类的方法,没有继续调用父类的userEventTriggered方法,业务处理完加上super.userEventTriggered(ctx, evt);
// ChannelHandler2.userEventTriggered 方法就可以触发了
// 2、其他方法的也一样
}
}
}
4、创建管道拦截器(用于处理业务逻辑,通常一个拦截器就够了)
4.1、管道拦截器1
package com.tan.netty;
import com.tan.netty.event.CustomUserEvent;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
public class ChannelHandler1 extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println("============ChannelHandler1=====================");
System.out.println("msg = " + msg);
//继续调用下一个处理器
super.channelRead(ctx, msg);
{
CustomUserEvent event = new CustomUserEvent("Hello from CustomUserEvent!");
//触发事件 只能进入 ChannelHandler2.userEventTriggered 方法,不能进入当前类的 userEventTriggered 方法
ctx.fireUserEventTriggered(event);
}
}
/**
* 该事件是由IdleStateHandler触发
*/
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state() == IdleState.READER_IDLE) {
System.out.println("读空闲,关闭连接");
ctx.close();
}
} else {
super.userEventTriggered(ctx, evt);
}
}
}
4.2、管道拦截器2
package com.tan.netty;
import com.tan.netty.event.CustomUserEvent;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
public class ChannelHandler2 extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (true) {
CustomUserEvent event = new CustomUserEvent("Hello from CustomUserEvent!");
ctx.fireUserEventTriggered(event);
}
// 在这里处理接收到的数据
System.out.println("MyChannelHandler");
System.out.println("msg = " + msg);
int i = 1 / 0;
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
System.out.println("处理异常");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("断开连接时触发");
}
/**
* 该事件是由IdleStateHandler触发
*/
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof CustomUserEvent) {
CustomUserEvent evt1 = (CustomUserEvent) evt;
System.out.println(evt1.getMessage());
} else {
super.userEventTriggered(ctx, evt);
}
}
}
5、定义事件
可以跑一下这个demo试一下自定义事件,感觉这个一般没啥用。
public class CustomUserEvent {
private final String message;
public CustomUserEvent(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}