netty - ReadComplete() 调用了两次
问题描述
注意:这个问题是过去发布的,但今天使用 ChannelOption.MAX_MESSAGES_PER_READ 的解决方案不起作用。此外,该选项已弃用。
注意:作为提醒..我是 Netty 的新手。
使用 Netty 4.1.51,当我使用 'curl -v http://localhost:8080/' 刺激以下代码时,我的程序的控制台输出显示:
Server listening on 8080
Read
GET / HTTP/1.1
Host: localhost:8080
User-Agent: curl/7.71.1
Accept: */*
ReadComplete
Write Hello
flush
ReadComplete
Write Hello
flush
处理程序 read() 方法被调用一次, readComplete() 方法被调用两次,然而,curl 似乎对这种交换很满意。
curl -v http://localhost:8080/
* Trying ::1:8080...
* Connected to localhost (::1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.71.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 ok
< Content-Type: text/plain
< Content-Length: 5
<
Hello* Connection #0 to host localhost left intact
我似乎无法理解发生了什么..最近有人遇到过这个问题吗?
readComplete() 不应该在一次或多次读取后发生一次吗?
感谢大家有空来看这个。
-jmr
private static void bootstrap1() {
int port = 8080;
EventLoopGroup group = new NioEventLoopGroup();
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap
.group(group)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>()
{
@Override protected void initChannel(SocketChannel ch)
throws Exception
{
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("out", new OutHandler());
pipeline.addLast("in", new InHandler());
}
});
ChannelFuture future = bootstrap.bind(new InetSocketAddress(port));
future.addListener((ChannelFutureListener) (ChannelFuture future1) ->
{
if (future1.isSuccess()) {
pr("Server listening on " + port);
} else {
future1.cause().printStackTrace();
}
});
}
private static class InHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
pr("Read");
if (msg instanceof ByteBuf) {
ByteBuf buf = (ByteBuf) msg;
pr("" + buf.readCharSequence(buf.readableBytes(), Charset.defaultCharset()));
}
ctx.fireChannelRead(msg);
}
@Override public void channelReadComplete(ChannelHandlerContext ctx)
throws Exception
{
pr("ReadComplete");
ByteBuf buf = Unpooled.buffer();
buf.writeCharSequence("Hello", Charset.defaultCharset());
ctx.writeAndFlush(buf);
ctx.fireChannelReadComplete();
}
}
private static class OutHandler extends ChannelOutboundHandlerAdapter {
@Override public void write(ChannelHandlerContext ctx, Object msg,
ChannelPromise promise)
throws Exception
{
ByteBuf in = (ByteBuf) msg;
pr("Write " + in.getCharSequence(0, in.readableBytes(), Charset.defaultCharset()));
ByteBuf buf = Unpooled.buffer();
buf.writeCharSequence("HTTP/1.1 200 ok\r\n", Charset.defaultCharset());
buf.writeCharSequence("Content-Type: text/plain\r\n", Charset.defaultCharset());
buf.writeCharSequence("Content-Length: 5\r\n\r\n", Charset.defaultCharset());
buf.writeCharSequence(in.readCharSequence(5, Charset.defaultCharset()), Charset.defaultCharset());
in.release();
ctx.write(buf);
}
@Override public void flush(ChannelHandlerContext ctx)
throws Exception
{
pr("flush");
ctx.flush();
}
}
解决方案
除了channelReadComplete
被调用两次之外,您还编写了两次 HTTP 响应,由重复的“Write Hello”日志消息指示。
channelReadComplete
被调用两次,因为自动读取已打开(默认情况下发生)。要关闭它并触发初始只读,您可以修改ChannelInitializer
如下:
.childHandler(new ChannelInitializer<SocketChannel>()
{
@Override protected void initChannel(SocketChannel ch)
throws Exception
{
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("out", new OutHandler());
pipeline.addLast("in", new InHandler());
ch.config().setAutoRead(false);
ch.read();
}
});
说了这么多,channelReadComplete
实际上并不是创建和发送响应的正确位置。在您的情况下,这样做会导致响应的双重发送。curl 可能没问题,但这不是你想要的。channelRead
一旦您收集了完整的请求,该方法是发送响应的更好地方。
对于参考回声服务器,请看这里:https ://github.com/normanmaurer/netty-in-action/tree/2.0-SNAPSHOT/chapter2/Server/src/main/java/nia/chapter2/echoserver
我强烈推荐 NIA 的书,让你在所有事情上都指向正确的方向。
推荐阅读
- python - 无法提取 HTML 属性
- firebase - Firebase 功能:部署失败 错误 403 无法读取主机 us.gcr.io 的标签
- json - 从嵌套的 Json 字符串中检索 datalab_name
- scala - 没有 Encoder[Row] 类型的隐式参数
- javascript - 如何制作无限滑块
- c# - 如何保护 Web api 以验证客户端应用程序生成的 openid 令牌?
- reactjs - AG Grid 无限行模型不起作用 - 当它位于绘图行的中间时,无法让网格绘制行
- javascript - getByteFrequencyData 获取给定时间的准确数据
- flutter - 在 Dart 中访问 Firestore 数组
- android - 如何将 Koin ViewModel 注入移动到 Base 片段?