如何修复从Netty服务器下载不完整的文件

时间:2019-04-30 16:54:22

标签: java netty

我正在实现一些简单的Netty服务器和客户端来发送和查看文件。类似于云存储。

我有一台处理传入请求并将其发送回客户端的服务器。我还希望我的应用程序能够处理大文件,这就是为什么我将此类文件分成多个块并逐块发送的原因。但是有一个我无法解决的问题。

让我们说:

  • 我们在服务器上有一个4 gb文件。
  • 它分为40000个块。
  • 然后将它们发送到客户端应用程序,我可以看到服务器上的所有块均已写入套接字,因为我将int字段用作消息号(组号)并将消息号写入日志正在被写。

但是,当客户端接收到消息(块)时,对于大文件而言,该过程无法成功完成,并且客户端仅接收到一些块(取决于文件的大小)。 / p>

客户端开始接收连续的消息-1, 2, 3, 4 ... 27878, 27879,然后毫无例外地停止,尽管来自服务器的最后一条消息是例如40000。

几乎忘了说我在客户端应用程序中使用JavaFX。

因此,我尝试使用xms xmx java vm选项,但无济于事。

服务器

public class Server {
    public void run() throws Exception {
        EventLoopGroup mainGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(mainGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(
                                    new ObjectDecoder(Constants.FRAME_SIZE, ClassResolvers.cacheDisabled(null)),
                                    new ObjectEncoder(),
                                    new MainHandler()
                            );
                        }
                    })
                    .childOption(ChannelOption.SO_KEEPALIVE, true);

            ChannelFuture future = b.bind(8189).sync();
            future.channel().closeFuture().sync();
        } finally {
            mainGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        new Server().run();

    }
}

服务器处理程序

@Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        try {
            if (msg == null) {
                return;
            }
            if (msg instanceof FileRequest) {
                FileRequest fr = (FileRequest) msg;
                switch (fr.getFileCommand()) {
                    case DOWNLOAD:
                        sendFileToClient(ctx, fr.getFilename());
                        break;
                    case LIST_FILES:
                        listFiles(ctx);
                        break;
                    case DELETE:
                        deleteFileOnServer(fr);
                        listFiles(ctx);
                        break;
                    case SEND:
                        saveFileOnServer(fr);
                        listFiles(ctx);
                        break;
                    case SEND_PARTIAL_DATA:
                        savePartialDataOnServer(fr);
                        break;
                }
            }
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }

大块发送文件的方法

private void sendFileToClient(ChannelHandlerContext ctx, String fileName) throws IOException {
        Path path = Paths.get("server_storage/" + fileName);
        if (Files.exists(path)) {
            if (Files.size(path) > Constants.FRAME_SIZE) {
                sendServerDataFrames(ctx, path);
                ctx.writeAndFlush(new FileRequest(FileCommand.LIST_FILES));
            } else {
                FileMessage fm = new FileMessage(path);
                ctx.writeAndFlush(fm);
            }
        }
    }

    private void sendServerDataFrames(ChannelHandlerContext ctx, Path path) throws IOException {
        byte[] byteBuf = new byte[Constants.FRAME_CHUNK_SIZE];

        FileMessage fileMessage = new FileMessage(path, byteBuf, 1);
        FileRequest fileRequest = new FileRequest(FileCommand.SEND_PARTIAL_DATA, fileMessage);

        FileInputStream fis = new FileInputStream(path.toFile());
        int read;
        while ((read = fis.read(byteBuf)) > 0) {
            if (read < Constants.FRAME_CHUNK_SIZE) {
                byteBuf = Arrays.copyOf(byteBuf, read);
                fileMessage.setData(byteBuf);
            }
            ctx.writeAndFlush(fileRequest);
            fileMessage.setMessageNumber(fileMessage.getMessageNumber() + 1);
        }

        System.out.println("server_storage/" + path.getFileName() +  ", server last frame number: " + fileMessage.getMessageNumber());
        System.out.println("server_storage/" + path.getFileName() +  ": closing file stream.");

        fis.close();
    }

客户端处理程序

@Override
    public void initialize(URL location, ResourceBundle resources) {
        Network.start();
        Thread t = new Thread(() -> {
            try {
                while (true) {
                    AbstractMessage am = Network.readObject();
                    if (am instanceof FileMessage) {
                        FileMessage fm = (FileMessage) am;
                        Files.write(Paths.get("client_storage/" + fm.getFilename()), fm.getData(), StandardOpenOption.CREATE);
                        refreshLocalFilesList();
                    }
                    if (am instanceof FilesListMessage) {
                        FilesListMessage flm = (FilesListMessage) am;
                        refreshServerFilesList(flm.getFilesList());
                    }
                    if (am instanceof FileRequest) {
                        FileRequest fr = (FileRequest) am;
                        switch (fr.getFileCommand()) {
                            case DELETE:
                                deleteFile(fr.getFilename());
                                break;
                            case SEND_PARTIAL_DATA:
                                receiveFrames(fr);
                                break;
                            case LIST_FILES:
                                refreshLocalFilesList();
                                break;
                        }
                    }
                }
            } catch (ClassNotFoundException | IOException e) {
                e.printStackTrace();
            } finally {
                Network.stop();
            }
        });
        t.setDaemon(true);
        t.start();
        refreshLocalFilesList();
        Network.sendMsg(new FileRequest(FileCommand.LIST_FILES));
    }

 private void receiveFrames(FileRequest fm) throws IOException {
        Utils.processBytes(fm.getFileMessage(), "client_storage/");
    }


public final class Utils {

    public static void processBytes(FileMessage fm, String pathPart) {
        Path path = Paths.get(pathPart + fm.getFilename());
        byte[] data = fm.getData();

        System.out.println(pathPart + path.getFileName() + ": " + fm.getMessageNumber());

        try {
            if (fm.getMessageNumber() == 1) {
                Files.write(path, data, StandardOpenOption.CREATE_NEW);
            } else {
                Files.write(path, data, StandardOpenOption.WRITE, StandardOpenOption.APPEND);
            }
        }
        catch (IOException e) {
            e.printStackTrace();
        }

    }

}

我在服务器上看到的。

server_storage/DVD5_OFFICE_2010_SE_SP2_VOLUME_X86_RU-KROKOZ.iso: 42151
server_storage/DVD5_OFFICE_2010_SE_SP2_VOLUME_X86_RU-KROKOZ.iso: 42152
server_storage/DVD5_OFFICE_2010_SE_SP2_VOLUME_X86_RU-KROKOZ.iso, server last frame number: 42153
server_storage/DVD5_OFFICE_2010_SE_SP2_VOLUME_X86_RU-KROKOZ.iso: closing file stream.

这是在客户端上。

client_storage/DVD5_OFFICE_2010_SE_SP2_VOLUME_X86_RU-KROKOZ.iso: 29055
client_storage/DVD5_OFFICE_2010_SE_SP2_VOLUME_X86_RU-KROKOZ.iso: 29056
client_storage/DVD5_OFFICE_2010_SE_SP2_VOLUME_X86_RU-KROKOZ.iso: 29057

从客户端向服务器发送文件时没有问题。我可以在调试器和Windows任务管理器中看到两个进程同时工作,但是当文件从服务器发送到客户端时却不是这样。首先,读取所有块,然后将它们发送到客户端,客户端开始接收它们,但无法全部获取。
请帮忙。我不知道那会是什么。预先感谢。

0 个答案:

没有答案