package com.jtc.test;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;
import java.nio.charset.Charset;
@Slf4j
public class NettyServerFe {
public static void main(String[] args) {
//EventLoop 本质是一个单线程执行器(同时维护了一个 Selector),里面有 run 方法处理 Channel 上源源不断的 io 事件
//EventLoopGroup 是一组 EventLoop,Channel 一般会调用 EventLoopGroup 的 register 方法来绑定其中一个 EventLoop,
// 后续这个 Channel 上的 io 事件都由此 EventLoop 来处理(保证了 io 事件处理时的线程安全)
NioEventLoopGroup boss = new NioEventLoopGroup(1); //专门处理 accept 事件
NioEventLoopGroup worker = new NioEventLoopGroup(); //服务器端 NIO worker 工人,处理 read 事件
//打印日志,对入站/出站事件进行日志记录
LoggingHandler LOGGIN_HANDLER = new LoggingHandler(LogLevel.DEBUG);
try {
//服务端ServerBootstrap,客户端Bootstrap
ServerBootstrap serverBootstrap = new ServerBootstrap();
//在客户端建立连接时,如果在指定毫秒内无法连接,会抛出 timeout 异常
serverBootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 30000);
//选择服务 Scoket 实现类,其中 NioServerSocketChannel 表示基于 NIO 的服务器端实现
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.group(boss, worker);
//下面添加的处理器都是给 SocketChannel 用的,而不是给 ServerSocketChannel。ChannelInitializer 处理器(仅执行一次),
// 它的作用是待客户端 SocketChannel 建立连接后,执行 initChannel 以便添加更多的处理器
serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
//SocketChannel 的处理器,解码 ByteBuf => String
//ch.pipeline().addLast(new StringDecoder());
ch.pipeline().addLast(LOGGIN_HANDLER);
// 用来判断是不是 读空闲时间过长,或 写空闲时间过长
// 45s 内如果没有收到 channel 的数据,会触发一个 IdleState#READER_IDLE 事件
ch.pipeline().addLast(new IdleStateHandler(45, 0, 0));
// ChannelDuplexHandler 可以同时作为入站和出站处理器
ch.pipeline().addLast(new ChannelDuplexHandler() {
// 用来触发特殊事件
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception{
IdleStateEvent event = (IdleStateEvent) evt;
// 触发了读空闲事件
if (event.state() == IdleState.READER_IDLE) {
log.debug("已经 45s 没有读到数据了");
ctx.channel().close();
}
}
});
//业务处理器,使用上一个处理器的处理结果
//ChannelInboundHandlerAdapter入站处理器,主要用来读取客户端数据,写回结果
ch.pipeline().addLast(new ChannelInboundHandlerAdapter() {
//ctx 里包含着ChannelHandler中的上下文信息,功能就是用来管理它所关联的ChannelHandler和在同一个ChannelPipeline中ChannelHandler的交互。
//把 msg 理解为流动的数据,最开始输入是 ByteBuf,但经过 pipeline 的加工,会变成其它类型对象,最后输出又变成 ByteBuf
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf buffer = (ByteBuf) msg;
System.out.println(buffer.toString(Charset.defaultCharset()));
// 建议使用 ctx.alloc() 创建 ByteBuf,其引用会计数 + 1
ByteBuf response = ctx.alloc().buffer();
//写入 netty 的 ByteBuf
response.writeBytes(buffer);
ctx.writeAndFlush(response);
//将 ByteBuf 对象的引用计数减 1
response.release();
//把msg传给下一个处理器加工(下一个为出站处理器)
//ctx.channel().write(msg);
}
});
//ChannelOutboundHandlerAdapter出站处理器,主要对写回结果进行加工
// ch.pipeline().addLast(new ChannelOutboundHandlerAdapter(){
// @Override
// public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
// super.write(ctx, msg, promise);
// }
// });
}
});
//ServerSocketChannel 绑定的监听端口
Channel channel = serverBootstrap.bind(8080).sync().channel();
channel.closeFuture().sync();
} catch (InterruptedException e) {
log.error("server error", e);
}finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
}
}
NettyServer fe
最新推荐文章于 2022-12-17 13:27:55 发布