先说结论:
当收到一个完整的应用层消息报文,channelRead会被触发一次。每次读取完Socket的接收缓冲区的报文,channelReadComplete会被触发一次。
实际中都会使用编码器。
- 比如定长解码器,规定5个字节是一个完整的消息。
那么客户端发送了4字节,服务端收到后,计算长度是4,就不需要读取,缓存区待着就好了,等下次有新数据来了我再判断,此时没有触发channelReadComplete。
后续客户端在发送,如果达到了5,服务端才会去读取,此时channelRead和channelReadComplete都会触发。 - 对于分隔符解码器,服务端收到了必须先读取缓冲区才能知道是不是分隔符,所以发一次,服务端就读一次,自然channelReadComplete执行次数大于channelRead了。
下面贴一个代码,可以自行验证。
public class MyServer {
public static void main(String[] args) throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast(new DelimiterBasedFrameDecoder(1024,
Unpooled.wrappedBuffer("#".getBytes())))
.addLast(new ServerHandler());
}
});
ChannelFuture channelFuture = bootstrap.bind(6666).sync();
channelFuture.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
class ServerHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ctx.fireChannelRead(msg);
System.out.println("server channelRead :" + msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
super.channelReadComplete(ctx);
System.out.println("server channelReadComplete");
}
}
客户端
public static void main(String[] args) throws Exception {
test();
}
private static void test() throws InterruptedException {
NioEventLoopGroup eventExecutors = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventExecutors)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_SNDBUF, 5)
.option(ChannelOption.SINGLE_EVENTEXECUTOR_PER_GROUP, false)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast(new DelimiterBasedFrameDecoder(1024,
Unpooled.wrappedBuffer("#".getBytes())));
}
}).remoteAddress("127.0.0.1", 6666);
ChannelFuture future = bootstrap.connect();
Channel channel = future.sync().channel();
//分隔符编码器
// 先发送4字节,服务端socke读取后,调用channelReadComplete方法。
// 第二次发送5字节,并带上结束符。服务端先channelRead,然后再调用channelReadComplete
ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer(6);
buffer.writeBytes(new byte[]{1, 2, 3, 4});
channel.writeAndFlush(buffer).addListener(future1 -> {
System.out.println("success");
});
ByteBuf buffer1 = ByteBufAllocator.DEFAULT.buffer(6);
buffer1.writeBytes(new byte[]{1, 2, 3, 4,'#'});
channel.writeAndFlush(buffer1).addListener(future1 -> {
System.out.println("success");
});
}
}
参考:netty进阶指南-李林峰
本文作者:WKP9418
本文连接:https://blog.csdn.net/qq_43179428/article/details/140578855