netty服务端
NioEventLoopGroup boss = new NioEventLoopGroup(); // 主连接线程池
NioEventLoopGroup worker = new NioEventLoopGroup(); // 主读写线程池
try {
// 1.启动器负责组装netty组件,启动服务器
ServerBootstrap serverBootstrap = new ServerBootstrap();
// 2. boss负责连接,accept时间 work(child)负责读写,处理器
serverBootstrap.group(boss, worker);
// 3.选择NioServerSocketChannel实现
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
@Override
protected void initChannel(NioSocketChannel nioSocketChannel) throws Exception {
// 12.连接建立后,调用初始化方法
// 负责添加handler
LengthFieldBasedFrameDecoder fieldBasedFrameDecoder = new LengthFieldBasedFrameDecoder(512, 16, 4, 0, 0);
ChannelPipeline pipeline = nioSocketChannel.pipeline();
pipeline.addLast(fieldBasedFrameDecoder); // 处理粘包、半包
pipeline.addLast(new LoggingHandler(LogLevel.DEBUG));
pipeline.addLast(new MsgCoder()); // 17.将bytebuf 自定义解码
pipeline.addLast(new NetMsgBaseHandler()); // 自定义处理handler
}
});
Channel channel = serverBootstrap.bind(8080).sync().channel();
channel.closeFuture().sync();
} catch (Exception e) {
LOG.error("", e);
} finally {
boss.shutdownGracefully();
worker.shutdownGracefully();
}
handler
pipeline.addLast(new NetMsgBaseHandler()); // 自定义处理handler
"每次建立一个连接就会生成一个新的handler对象,每个连接对应一个handler"
如果想多个连接共用一个handler,就需要在handler上加入@Sharable
注解
@Sharable
public class NetMsgBaseHandler
NetMsgBaseHandler msgHandler = new NetMsgBaseHandler(); // 这是个成员变量
......
pipeline.addLast(msgHandler);
"每次建立连接时,用的都是同一个handler,多个连接对应一个handler"
入站和出站
入站继承ChannelInboundHandlerAdapter,主要用来读取客户端数据,回写结果
出站继承ChannelOutboundHandlerAdapter,主要对回写结果进行加工
入站和出站主要是执行顺序相反,入站是顺序执行,出站则是反序执行。
出站只有在服务器往channel写入数据时会触发,其余不会触发
netty客户端
连接建立方式
try {
NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
pipeline.addLast(new ChannelInboundHandlerAdapter() {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
}
});
}
});
// connect异步非阻塞,真正执行的是NIO线程
ChannelFuture channelFuture = bootstrap.connect("localhost", 8888);
// 建立连接
} catch (InterruptedException e) {
logger.error("启动错误", e);
}
- 方式1(sync()方法)
// sync同步处理结果,如果不阻塞,会在netty建立连接之前就往下执行,后面的channel就是没建立连接的channel
channelFuture.sync();
Channel channel = channelFuture.channel();
- 方式2(使用addListener异步处理结果)
// 使用addListener异步处理结果
channelFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture) throws Exception {
// 在nio线程建立完连接后在nio线程执行
channelFuture.channel();
}
});
关闭通道和netty线程
try {
channel.close(); // 关闭channel通道,此操作属于异步操作
channel.closeFuture().sync(); // 会阻塞,等待channel关闭后在往下执行
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
eventLoopGroup.shutdownGracefully(); // 关闭netty线程
logger.info("关闭服务");
}