如何使用 Netty 发送命令和文件

How to send both commands and files using Netty

我不明白我做错了什么。如何发送命令和文件。以下是服务器和客户端管道。

客户端管道:

 pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null)));    //in
            pipeline.addLast(new ObjectEncoder());                           //out
            pipeline.addLast(new ChunkedWriteHandler());                     //out
            pipeline.addLast(new FileSenderHandler());                       //in (write in responce)
            pipeline.addLast(new FileInfoSenderHandler());                   //out

服务器管道:

        pipeline.addLast(new ObjectEncoder());                                   //out
        pipeline.addLast(new ObjectDecoder(ClassResolvers.cacheDisabled(null))); //in
        pipeline.addLast(new FileReceiverHandler());                             //in
        pipeline.addLast(new FileInfoReceiverHandler());                         //in (write in responce)

事实是,在发送文件之前,我发送了一个命令(FileInfo),即将发送此FileInfo中描述的文件。然后发送文件本身(byte Buf),和FileInfo一样,ByteBuf也传递给ObjectDecoder。同时,Java对第一块发誓:

2021-10-09 01:26:02 WARN  DefaultChannelPipeline:1152 - An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exception.
io.netty.handler.codec.TooLongFrameException: Adjusted frame length exceeds 1048576: 4292411364 - discarded
    at io.netty.handler.codec.LengthFieldBasedFrameDecoder.fail(LengthFieldBasedFrameDecoder.java:503)
    at io.netty.handler.codec.LengthFieldBasedFrameDecoder.failIfNecessary(LengthFieldBasedFrameDecoder.java:489)
    at io.netty.handler.codec.LengthFieldBasedFrameDecoder.exceededFrameLength(LengthFieldBasedFrameDecoder.java:376)
    at io.netty.handler.codec.LengthFieldBasedFrameDecoder.decode(LengthFieldBasedFrameDecoder.java:419)
    at io.netty.handler.codec.serialization.ObjectDecoder.decode(ObjectDecoder.java:69)
    at io.netty.handler.codec.LengthFieldBasedFrameDecoder.decode(LengthFieldBasedFrameDecoder.java:332)
    at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:508)
    at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:447)
    at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:276)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
    at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357)
    at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379)
    at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365)
    at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919)
    at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:166)
    at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:719)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:655)
    at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:581)
    at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493)
    at io.netty.util.concurrent.SingleThreadEventExecutor.run(SingleThreadEventExecutor.java:989)
    at io.netty.util.internal.ThreadExecutorMap.run(ThreadExecutorMap.java:74)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:834)

如果您覆盖 decode() 并添加日志,那么您可以看到文件传输在此之后并未结束,并且每个片段都通过 ObjectDecoder 并“解码为空”。其余块上没有这样的异常。

我尝试了什么:

  1. 在ObjectDecoder的构造函数中,设置了number maxObjectSize = MAX.INTEGER,但是还是很小

  2. 在decode()中设置条件:

     @Override
     protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
         System.out.println("decode()");
         Object o = super.decode(ctx, in);
         System.out.println(o);
    
         if(o == null){
             System.out.println(in);
             return in;
         }
         return o;
     }
    

在这种情况下,文件中丢失了 2048 个字节。

3) 如果我在代码中动态更改管道(delete/add 一个 ObjectDecoder,具体取决于状态),则文件将被服务器接受。但是,我不确定这个决定的正确性。

也许我在做一些根本性的错误?几乎把所有的文档都看完了,第二周一直在解决这个问题。读什么?请帮我。谢谢!

您似乎有一个自定义协议,其中包含有关文件的元数据,然后是实际文件数据。由于元数据处理程序 (ObjectEncoder/Decoder) 和文件处理程序都在缓冲区上工作,因此似乎无法区分哪些消息是元数据,哪些是数据。如果网络上没有这样的指示,并且您知道第一条消息始终是元数据,那么使用元数据处理程序启动管道,然后稍后将其与文件处理程序交换似乎是您的最佳选择。