Netty文件传输导致异常

时间:2018-05-17 22:04:13

标签: java netty file-transfer

我试图通过我的netty实现传输文件。我的自定义解码器和编码器都知道两种类型的对象:String和FileChunk,它们实际上包含块的索引及其内容为byte[]。转移的工作方式如下:

  1. Client0向Client1发送一个JsonObject,其中String包含应保存文件的路径,它的大小,发送的切片数等等。
  2. Client0将第一个片段作为FileChunk发送,并阻止该线程等待Client1成功收到其数据包的答案。
  3. Client1收到文件的片段并将其写入磁盘。然后,它将成功数据包发送到Client0
  4. Client0收到成功数据包并发送下一个片段。
  5. 这个进展应该继续进行,直到文件被转移。它有效。 如果我在发送64kb文件片段之前添加1秒延迟! Derp。

    似乎没有错误 - 但它在重负载和没有阻塞线程的情况下不起作用。我需要在任何地方清除缓冲区还是需要副本?请帮助......如果您在IntelliJ和Maven的示例项目中被告知我在评论中告诉我,我会准备好。

    解释得足够多。这是代码!

    FileTransfer Runnable

    public class FileTransfer implements Runnable {
    
        private FileSlicer slicer;
        private Client client;
    
        public FileTransfer(FileSlicer slicer, Client client) {
            this.slicer = slicer;
            this.client = client;
        }
    
        public void run() {
    
            synchronized(this) {
    
                while(slicer.hasNext()) {
    
                    try {
                        client.getContext().writeAndFlush(slicer.getNextSlice());
                        this.wait(); //Unblocked when success packet received, works
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                }
    
            }
        }
    }
    

    渠道初始化程序(此处设置缓冲区大小,默认情况下不应溢出)

    @Override
    protected void initChannel(Channel channel) throws Exception {
    
        channel.config().setRecvByteBufAllocator(new FixedRecvByteBufAllocator(1024 * 65));
    
        ChannelPipeline pipeline = channel.pipeline();
        pipeline.addLast(new PacketDecoder());
        pipeline.addLast(new PacketEncoder());
        pipeline.addLast(new ChannelEncoder());
        pipeline.addLast(new ServerHandler());
    
    }
    

    解码器(检测它是String还是FileChunk并解析它):

    public class PacketDecoder extends ByteToMessageDecoder {
    
        @Override
        protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> output) throws Exception {
    
            int type = buf.readInt();
            if (buf.readableBytes() <= 0) return;
            byte[] buffer;
    
            switch (type) {
    
                case 0:
                    buffer = buf.readBytes(buf.readInt()).array();
                    output.add(new String(buffer));
                    break;
    
                case 1:
                    int read = buf.readInt();
                    buffer = buf.readBytes(buf.readInt()).array();
                    output.add(new FileChunk(buffer, read));
                    break;
    
                default:
                    System.out.println("Unknown Decodec.");
                    break;
    
            }
    
        }
    
    }
    

    堆栈跟踪

    io.netty.handler.codec.DecoderException: java.lang.IndexOutOfBoundsException: readerIndex(12) + length(65536) exceeds writerIndex(40960): PooledUnsafeDirectByteBuf(ridx: 12, widx: 40960, cap: 66560)
        at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:347)
        at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:230)
        at io.netty.channel.ChannelHandlerInvokerUtil.invokeChannelReadNow(ChannelHandlerInvokerUtil.java:84)
        at io.netty.channel.DefaultChannelHandlerInvoker.invokeChannelRead(DefaultChannelHandlerInvoker.java:153)
        at io.netty.channel.PausableChannelEventExecutor.invokeChannelRead(PausableChannelEventExecutor.java:86)
        at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:389)
        at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:956)
        at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:618)
        at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:331)
        at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:250)
        at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:116)
        at io.netty.util.internal.chmv8.ForkJoinTask$RunnableExecuteAction.exec(ForkJoinTask.java:1412)
        at io.netty.util.internal.chmv8.ForkJoinTask.doExec(ForkJoinTask.java:280)
        at io.netty.util.internal.chmv8.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:877)
        ...
    

1 个答案:

答案 0 :(得分:2)

解码函数假定整个文件可用,但缓冲区可能只包含部分数据,具体取决于接收的流量。

解决此问题的一种方法是添加一个帧解码器,例如LengthFieldBasedFrameDecoder,它根据消息中的长度字段对流进行分段,例如在您的示例中。

另一种选择是使用与文档中的文件服务器示例相同的方法:https://netty.io/4.1/xref/io/netty/example/http/file/package-summary.html