【问题标题】:Send file with Java Socket and java.nio.file.Files.copy [duplicate]使用 Java Socket 和 java.nio.file.Files.copy 发送文件 [重复]
【发布时间】:2021-07-20 08:53:12
【问题描述】:

我正在尝试制作一个简单的服务器/客户端应用程序来发送消息和文件。

我的客户端首先发送文件名,然后是文件本身,最后等待服务器的响应。

我的服务器做相反的事情,读取文件名,读取文件,发送响应。

问题是客户端卡在String response = dataInputStream.readUTF(); 而服务器卡在Files.copy(Paths.get(fileName), dataOutputStream);

我试图从客户端删除String response = dataInputStream.readUTF();,没有它它工作正常。有人可以帮我理解为什么我在发送文件后执行readUtf() 时卡住了吗?

谢谢

这是我的客户

public static void main(String[] args) throws IOException {
    String fileName = "hello.txt";

    try (Socket clientSocket = new Socket(HOST, PORT)) {
        DataInputStream dataInputStream = new DataInputStream(clientSocket.getInputStream());
        DataOutputStream dataOutputStream = new DataOutputStream(clientSocket.getOutputStream());

        dataOutputStream.writeUTF(fileName);

        Files.copy(Paths.get(fileName), dataOutputStream);

        String response = dataInputStream.readUTF();
        LOGGER.info(response);
    }
}

这是我的服务器

    public static void main(String args[]) throws IOException {

    try (ServerSocket ss = new ServerSocket(PORT)) {
        Socket clientSocket = ss.accept();

        DataOutputStream dataOutputStream = new DataOutputStream(clientSocket.getOutputStream());
        DataInputStream dataInputStream = new DataInputStream(clientSocket.getInputStream());

        String fileName = dataInputStream.readUTF();

        Files.copy(dataInputStream, Paths.get(fileName));

        dataOutputStream.writeUTF("New file saved");
    }
}

【问题讨论】:

  • 你试过flush输出流吗?
  • @QBrute 我尝试在客户端内部的Files.copy 之后添加dataOutputStream.flush();,但结果相同,卡住了
  • @user16320675 谢谢,我尝试刷新流,但不能解决问题,您知道我该怎么做吗?
  • @user16320675 顺便说一句,当我因为卡住而终止进程时,文件是用正确的内容创建的,响应不在文件内。
  • 这里的问题是Files.copy(dataInputStream, Paths.get(fileName)) 不会退出,除非到达流的末尾,但是只有在到达Client 中的try 块的末尾时才会自动关闭该流。因此,当客户端在尝试结束之前运行String response = dataInputStream.readUTF() 时,它实际上永远不会关闭服务器中Files.copy 正在读取的流。因此它们都被卡住了——因为客户端无法读取response,因为服务器永远不会到达“保存的新文件”行。

标签: java nio serversocket java-io


【解决方案1】:

根据我的 cmets,这里的问题是输入/输出流对仅在 try-with-resources 块退出时才关闭。所以在服务器上,Files.copy(dataInputStream, Paths.get(fileName)) 行不会退出,因为没有到达流的末尾,因为客户端中的流只有在到达客户端中的 try 块的末尾时才会自动关闭。

但客户端在尝试结束之前正在运行String response = dataInputStream.readUTF(),因此它实际上从未关闭流。因此他们都被卡住了。

最简单的解决方案是在客户端中的文件名之后发送文件大小并在Server 中替换Files.copy 的使用 - 只需读取Server 中的文件大小并将那么多字节传输到Files.newOutputStream。那么下一个字节就没有阻塞了。

这是一个示例客户端,其中包含发送大小的额外行:

Path path = Path.of(fileName);

try (Socket clientSocket = new Socket(HOST, PORT)) {
    DataInputStream dataInputStream = new DataInputStream(clientSocket.getInputStream());
    DataOutputStream dataOutputStream = new DataOutputStream(clientSocket.getOutputStream());

    dataOutputStream.writeUTF(fileName);
    dataOutputStream.writeLong(Files.size(path));
    Files.copy(path, dataOutputStream);

    String response = dataInputStream.readUTF();
    System.out.println(response);
}

这里是调整后的服务器,这会将File.copy 替换为size 字节上的循环。

try (ServerSocket ss = new ServerSocket(PORT)) {
    Socket clientSocket = ss.accept();

    DataOutputStream dataOutputStream = new DataOutputStream(clientSocket.getOutputStream());
    DataInputStream dataInputStream = new DataInputStream(clientSocket.getInputStream());

    String fileName = dataInputStream.readUTF();
    long len = dataInputStream.readLong();
    int read = 0;
    byte[] arr = new byte[8192];
    try(var fos = Files.newOutputStream(Paths.get(fileName))) {
        while(len > 0 && (read = dataInputStream.read(arr,0, (int)Math.min(len, arr.length))) != -1) {
            len -= read;
            fos.write(arr, 0, read);
        }
    }
    dataOutputStream.writeUTF("New file saved");
}

【讨论】:

  • 感谢您的解决方案,但如果我不能使用 Files.copy 我会找到 @silentsudo copyFile 方法清洁器
【解决方案2】:

这是我将文件从客户端发送到服务器的整个工作示例。 当我在服务器上使用 Files.copy(...) 时,我不确定是否特别好,所以你可以检查我是否添加了布尔条件,如果读取小于缓冲区大小,那么流中没有项目因此退出并关闭文件。

package com.company.socket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

class SocketClient {
    public static void main(String[] args) {
        Thread clientThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try (Socket serverSocket = new Socket("localhost", 5000)) {
                    final DataOutputStream outputStream = new DataOutputStream(serverSocket.getOutputStream());
                    final BufferedReader br = new BufferedReader(new InputStreamReader(serverSocket.getInputStream()));
                    Files.copy(Path.of("hello.txt"), serverSocket.getOutputStream());
                    outputStream.flush();
                    System.out.println("file sent from client");
                    String content = null;
                    while ((content = br.readLine()) != null) {
                        System.out.println("Client response: " + content);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
        clientThread.start();

    }
}

public class SocketServer {
    public static void main(String[] args) throws InterruptedException, IOException {
        final Thread serverThread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Started server");
                List<Socket> clients = new ArrayList<>();
                Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
                    @Override
                    public void run() {
                        System.out.println("Server shutdown!");
                        final List<String> collect = clients.stream()
                                .map(socket -> {
                                    try {
                                        socket.close();
                                    } catch (IOException e) {
                                        e.printStackTrace();
                                    }
                                    return "Closed client: " + socket.getInetAddress().getHostAddress();
                                })
                                .collect(Collectors.toList());
                        collect.forEach(System.out::println);
                    }
                }));
                ServerSocket serverSocket = null;
                try {
                    serverSocket = new ServerSocket(5000);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                while (true) {
                    try (Socket client = serverSocket.accept()) {
                        clients.add(client);
                        System.out.println("CLient connected: " + client.getInetAddress().getHostAddress() + ":" + client.getPort());
                        client.getOutputStream().write("Hello from server!".getBytes());
                        client.getOutputStream().flush();
                        final InputStream inputStream = client.getInputStream();
                        System.out.println("copyingReceiving file on server");
                        copyFile(inputStream);
                        System.out.println("File received successfully");
                        client.getOutputStream().write("\nFile received successfully".getBytes());
                        client.getOutputStream().flush();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                }
            }

            void copyFile(InputStream inputStream) throws IOException {
                final byte[] buffer = new byte[2048];
                final FileOutputStream outputStream = new FileOutputStream("file-" + UUID.randomUUID().toString() + ".txt");
                int length = 0;
                while ((length = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, length);
                    outputStream.flush();
                    if (length < buffer.length) break;
                }
                outputStream.close();
            }
        });
        serverThread.start();

    }
}

服务器输出

Started server
CLient connected: 127.0.0.1:38118
copyingReceiving file on server
File received successfully

客户端输出

file sent from client
Client response: Hello from server!
Client response: File received successfully

【讨论】:

  • 谢谢你让你的解决方案工作,但我仍然不明白为什么我不能在服务器端使用Files.copy
猜你喜欢
  • 2020-03-15
  • 1970-01-01
  • 1970-01-01
  • 2013-06-09
  • 2013-09-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多