我正在实现一些简单的Netty服务器和客户端来发送和查看文件。类似于云存储。
我有一台处理传入请求并将其发送回客户端的服务器。我还希望我的应用程序能够处理大文件,这就是为什么我将此类文件分成多个块并逐块发送的原因。但是有一个我无法解决的问题。
让我们说:
4 gb
文件。但是,当客户端接收到消息(块)时,对于大文件而言,该过程无法成功完成,并且客户端仅接收到一些块(取决于文件的大小)。 / 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任务管理器中看到两个进程同时工作,但是当文件从服务器发送到客户端时却不是这样。首先,读取所有块,然后将它们发送到客户端,客户端开始接收它们,但无法全部获取。
请帮忙。我不知道那会是什么。预先感谢。