Netty所有的数据传输处理都需要在ChannelPipline中的ChannelHandler来处理,这次我们来看看Netty是怎么处理这些数据的。
首先我们需要分清楚什么是出站数据,什么是入站数据。
出站数据:是指你需要发送的数据,不论你是客户端还是服务器只要是写进Channel里的数据都是出站的数据。
入站数据:是指你接收到的数据,不论你是客户端还是服务器,对方写进Channel里的数据,对于你来说都是入站数据。
现在我们来说说看ChannelPipline怎么处理出站数据和入站数据的。
入站数据都是从头部开始处理,pipline会先调用第一个ChannelInboundHandler,第一个消费后调用fireChannelRead方法触发下一个Handler的channelRead方法,如果不调用fireChannelRead方法,那么数据传递就到此为止。
对于出站数据如果通过调用Channel的write方法写数据的,数据会从Channelpipline的尾端开始传递,会先调用第一个ChannelOutboundHandler,只有Handler调用ChannelHandlerContext的write,才会继续触发下一个Handler,如果调用Channel的write数据将重头开始传递。
我们来定义一个服务端,并打印出日志看一下数据的流转过程
ServerIn1 的代码如下
@Slf4j
public class ServerInChannelAdapter1 extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
log.debug("服务器收到消息:{}",in.toString(CharsetUtil.UTF_8));
ctx.fireChannelRead(msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
log.debug("服务器读取完成");
ctx.fireChannelReadComplete();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
log.error("服务器异常",cause);
//关闭连接
ctx.close();
}
}
ServerIn2的代码
@Slf4j
public class ServerInChannelAdapter2 extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
log.debug("服务器收到消息:{}",in.toString(CharsetUtil.UTF_8));
//读取完成后需要手动释放
ReferenceCountUtil.release(in);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
log.debug("服务器读取完成");
//从尾部 发送数据
ctx.channel().writeAndFlush(Unpooled.copiedBuffer("这里是服务器收到请回复",CharsetUtil.UTF_8));
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
log.error("服务器异常",cause);
//关闭连接
ctx.close();
}
}
ServerOut1 和ServerOut2 代码一样的,代码如下
@Slf4j
public class ServerOutChannelAdapter1 extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
log.debug("服务器写入数据-> {}", ((ByteBuf) msg).toString(CharsetUtil.UTF_8));
ctx.write(msg);
promise.setSuccess();
}
@Override
public void flush(ChannelHandlerContext ctx) throws Exception {
log.debug("服务器刷新数据");
ctx.flush();
}
}
服务器端的Handler添加顺序如下
public void start() throws InterruptedException {
//线程组
group = new NioEventLoopGroup();
//服务器
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(group)
//设置非阻塞的服务器Socket连接
.channel(NioServerSocketChannel.class)
//绑定端口
.localAddress(new InetSocketAddress(port))
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast(new ServerInChannelAdapter1())
.addLast(new ServerOutChannelAdapter1())
.addLast(new ServerInChannelAdapter2())
.addLast(new ServerOutChannelAdapter2());
}
});
//绑定端口,并获取Channel
channelFuture = bootstrap.bind().sync();
}
执行的结果如下
11:25:43.816 [nioEventLoopGroup-2-2] DEBUG com.github.xuejike.javap2p.server.channel.ServerInChannelAdapter1 - 服务器收到消息:这里是客户端,收到请回复
11:25:43.817 [nioEventLoopGroup-2-2] DEBUG com.github.xuejike.javap2p.server.channel.ServerInChannelAdapter2 - 服务器收到消息:这里是客户端,收到请回复
11:25:43.818 [nioEventLoopGroup-2-2] DEBUG com.github.xuejike.javap2p.server.channel.ServerInChannelAdapter1 - 服务器读取完成
11:25:43.818 [nioEventLoopGroup-2-2] DEBUG com.github.xuejike.javap2p.server.channel.ServerInChannelAdapter2 - 服务器读取完成
11:25:43.825 [nioEventLoopGroup-2-2] DEBUG com.github.xuejike.javap2p.server.channel.ServerOutChannelAdapter2 - 服务器写入数据-> 这里是服务器收到请回复
11:25:43.825 [nioEventLoopGroup-2-2] DEBUG com.github.xuejike.javap2p.server.channel.ServerOutChannelAdapter1 - 服务器写入数据-> 这里是服务器收到请回复
11:25:43.826 [nioEventLoopGroup-2-2] DEBUG com.github.xuejike.javap2p.server.channel.ServerOutChannelAdapter2 - 服务器刷新数据
11:25:43.826 [nioEventLoopGroup-2-2] DEBUG com.github.xuejike.javap2p.server.channel.ServerOutChannelAdapter1 - 服务器刷新数据