【问题标题】:Why is it necessary to call ctx.writeAndFlush when using the MessageToByteEncoder为什么在使用 MessageToByteEncoder 时需要调用 ctx.writeAndFlush
【发布时间】:2015-03-04 09:15:30
【问题描述】:

我在 Netty 上实现为 SIP 服务器,使用 Netty 3.x 一切正常。然而,由于定义明确的线程模型,我决定升级到 Netty 4,但事情发生了很大变化,我迷失了方向。我最初的问题(我得到了大部分工作,但对我来说还不太有意义)是关于MessageToByteEncoder 以及为什么我必须自己打电话给ctx.writeAndFlush

引导程序:

private void createTCPListeningPoint() {
    this.serverBootstrap = new ServerBootstrap();

    this.serverBootstrap.group(this.bossGroup, this.workerGroup)
            .channel(NioServerSocketChannel.class)
            .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(final SocketChannel ch) throws Exception {
                    final ChannelPipeline pipeline = ch.pipeline();
                    pipeline.addLast("decoder", new SipFrameDecoder(Protocol.TCP));
                    pipeline.addLast("encoder", new SipMessageEncoder());
                    pipeline.addLast("handler", NettyNetworkStack.this.sipHandler);
                }
            });
    // .option(ChannelOption.SO_BACKLOG, 128)
    // .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
    // .childOption(ChannelOption.SO_KEEPALIVE, true)
    // .childOption(ChannelOption.TCP_NODELAY, true);
}

(绑定操作稍后进行,此处不显示)

由于传入管道正在工作(解码器和处理程序正在被调用),唯一有趣的部分是SipMessageEncoder,它非常简单,它的代码如下所示。另请注意,我一直在尝试各种选项,但正如怀疑的那样,我没有找到任何改变这种行为的组合。

编码器:

public final class SipMessageEncoder extends MessageToByteEncoder<SipMessage> {

@Override
protected void encode(final ChannelHandlerContext ctx, final SipMessage msg, final ByteBuf out) throws Exception {
    final Buffer b = msg.toBuffer();
    for (int i = 0; i < b.getReadableBytes(); ++i) {
        out.writeByte(b.getByte(i));
    }
    out.writeByte(SipParser.CR);
    out.writeByte(SipParser.LF);
    // ctx.writeAndFlush(out);
}

}

SipMessageSipParser 等取自我的另一个开源项目 pkts.io,但与这个问题并不真正相关。只要知道SipMessage.toBuffer() 本质上只是吐出一个原始的byte[],然后我将其转移到Netty 传入的ByteBuff

除非我执行ctx.writeAndFlush(out),否则上述代码不起作用。然而,我认为,就像在 Netty 3 中一样,这个稍微高级一点的编码器的目的是为了保护我免受这些细节的影响。通过代码逐步调试ByteBuf 写入ctx 但没有刷新,我猜这是它没有出现在套接字上的原因。所以,我的问题很简单:

  • 为什么我在使用SipMessageEncoder 时必须自己打电话给ctx.writeAndFlush。 Factorial 示例中的 NumberEncoder 没有这样做,并且通过 HTTP 编解码器查看我也看不到它这样做,所以很明显我错过了一些明显的东西。
  • 另外,ByteBuf 正在写入上下文,但从未刷新,所以什么时候会刷新?我尝试通过单个 TCP 连接(使用 sipp)推送大量流量,看看这是否最终会强制刷新,但不,什么都没有出现。

顺便说一句,我也尝试使用 MessageToMessageEncoder 并通过上下文分配 ByteBuf 并将其添加到 List 但结果相同。

顺便说一句——我使用的是 Netty 4.0.10.Final

谢谢,

/乔纳斯

【问题讨论】:

    标签: netty


    【解决方案1】:

    在向频道写入消息时,是否在其他地方调用了 flush()?

    "..你必须非常小心,不要忘记在写完东西后调用 ctx.flush()。或者,你可以使用快捷方法 writeAndFlush()"

    实际上您不需要在 SipMessageEncoder 中调用 write(),因为消息将由父 MessageToByteEncode 写入。如果之前没有完成,你只需要调用 flush()。

    【讨论】:

    • 嗨,Igor,不,我不调用flush,它实际上并没有帮助,因为我自己没有写任何东西。也许我误解了 MessageToByteEncoder 的用法,但我的印象是我将一个对象转换为另一个对象(在本例中为 ByteBuf),并且稍后在管道中的某个时间点它将被写入并刷新。我确信我只是错过了一些非常明显的东西。如果我在 bytebuf 上执行 ctx.writeAndFlush 它可以工作,但是我真的不明白 MessageToByteEncoder 的意义。
    • 你好,Igor,其实我想我明白你的意思。不,我没有在我的 SipMessageEncoder 中调用flush,也许问题出在哪里。我会试一试,如果可行,那么感谢您让我走上正轨!非常感谢。我会在尝试后立即更新,但首先是实际工作:-)
    • 您会看到,写入通道的所有消息都驻留在输出缓冲区中,直到您调用 flush()。主要使用场景如下:你将消息写入通道,然后是flush()。每条消息都由 MessageToByteEncoder 序列化。这里不需要调用 write()。在最终的 flush() 之后,所有消息都将写入该行。如果您在 SipMessageEncoder 中执行之前不调用 flush()。
    • 是的,你完全正确。尽管我自己读了好几遍,但我非常专注于编码器,以至于完全忽略了这个显而易见的事情。即使是阶乘示例也会这样做,但由于某种原因,我只是没有将两个和两个加在一起。感谢您耐心等待伊戈尔,非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-19
    • 2015-03-21
    • 2021-03-27
    • 2011-09-30
    • 1970-01-01
    相关资源
    最近更新 更多