我正在为协议编写Netty管道,该协议能够传输文件和结构化消息。文件传输以结构化消息(握手)开始,然后是代表文件的字节流。

传入文件的确切消息流如下所示(服务器是我的Netty实现,客户端是另一个软件):

            +---------+                                    +---------+
            | Client  |                                    | Server  |
            +---------+                                    +---------+
                 |                                              |
                 | Connect (1)                                  |
                 |--------------------------------------------->|
                 |                                              |
                 | Handshake to announce incoming file (2)      |
                 |--------------------------------------------->|
                 |                                              |
                 |                Acknowledge file transfer (3) |
                 |<---------------------------------------------|
                 |                                              |
                 | Send file (4)                                |
                 |--------------------------------------------->|


协议消息如下所示:

            +---------+----------------+----------------+
            | Length  |      Type      | Actual Message |
            | 4 bytes |     1 byte     |   N bytes      |
            +---------+----------------+----------------+


对于握手消息,Actual Message仅由单个Long值(token)组成。

这是ReplayingDecoder:

    public class ProtocolDecoder extends ReplayingDecoder<State> {

      private Integer msgType;
      private Long token;


      public enum State {
        LENGTH,
        MSG_TYPE,
        TOKEN
      }

      public ProtocolDecoder() {
        super(State.LENGTH);
      }

      @Override
      protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {

        switch (state()) {

          case LENGTH:
            // (2) read the message length from the handshake
            long messageLength = in.readUnsignedIntLE();
            checkpoint(State.MSG_TYPE);

          case MSG_TYPE:
            // (2) read the message type from the handshake
            msgType = in.readBoolean();
            checkpoint(State.TOKEN);

          case TOKEN:
            try {
              // (2) read the token from the handshake
              token = in.readUnsignedIntLE();
              // (3) write back the acknowledgement
              ctx.channel().writeAndFlush(new Acknowledgement(token));
              // (4) done reading the protocol message
              // now switch to the file protocol
              ctx.pipeline().addLast(new FileInboundHandler());
              // write everything that is still in the buffer to the
              // modified pipeline
              ByteBuf rest = in.readBytes(super.actualReadableBytes());
              out.add(rest);
              // remove the protocol handshake decoder and pass
              // the rest of this channel to the `FileInboundHandler`
              ctx.pipeline().remove(this);
            } finally {
              reset();
            }
            break;
          default:
            throw new Error("Shouldn't reach here.");
        }
      }

      private void reset() {
        token = null;
        msgType = null;
      }


FileInboundHandler只是创建一个文件并将所有ByteBuf写入文件。

原则上这是可行的,唯一的问题是每个文件在开始时都会丢失5个字节。

我有两个问题:

1)如果将LoggingHandler作为管道中的第一个处理程序,则无论解码器是否有故障,我都可以确定套接字上的所有流量都已记录下来?

2)调用ctx.channel().writeAndFlush()时,它是否只刷新“出站”缓冲区,这意味着我可以确定自己不刷新任何尚未从入站缓冲区消耗的字节吗?

最佳答案

可能是您忘记了为每个switch情况在语句末尾添加break;吗?现在来看,从第一个LENGTH读取开始,将遍历到类型和令牌读取,从而获得5个字节过多:布尔值(一个字节)和令牌(读取为unsigned int,4个字节)。

关于java - 为什么在Netty管道中的解码过程中会丢失5个字节?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/43518543/

10-16 23:29