【问题标题】:Netty file transfer causes exceptionNetty 文件传输导致异常
【发布时间】:2018-05-17 22:04:13
【问题描述】:

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

  1. Client0 向 Client1 发送一个 JsonObject,作为 String,其中包含文件应保存的路径、文件的大小、将发送的切片数量等。
  2. Client0 将第一个切片作为 FileChunk 发送并阻塞线程以等待 Client1 成功接收其数据包的答案。
  3. Client1 收到文件的切片并将其写入磁盘。然后向Client0发送成功包
  4. Client0 收到成功数据包并发送下一个切片。

此进度应继续进行,直到文件传输完毕。它有效。 如果我在发送 64kb 文件片段之前添加 1 秒延迟!Derp.

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

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

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();
                }

            }

        }
    }
}

Channel-Initializer(此处设置缓冲区大小,默认不溢出)

@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)
    ...

【问题讨论】:

标签: java netty file-transfer


【解决方案1】:

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

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

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

【讨论】:

  • 嗯,第一种方法听起来不错!明天我会尝试添加它。它应该不会对字符串造成任何问题,对吧?因为你的第二个想法会。也许我也可以使用聚合器..
  • hastebin.com/ucuyubiqak.java 等所有初始化程序中添加了它,但现在成功数据包未发送/文件块未正确接收。我错过了什么?还有@Ferrybig
  • 与 4 个长标题字段完美配合。非常感谢!
猜你喜欢
  • 2013-09-06
  • 1970-01-01
  • 1970-01-01
  • 2016-03-20
  • 2018-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多