0

I'm trying to make a simple server/client application sending messages and files.

My client first sends the file's name, then the file itself and finally waits for server's response.

My server does the opposit, read file's name, read file, send response.

The issue is that the client is stuck at String response = dataInputStream.readUTF(); while the server is stuck at Files.copy(Paths.get(fileName), dataOutputStream);

I tried to remove String response = dataInputStream.readUTF(); from client and it works fine without it. Can someone help me understand why it's stuck when I do the readUtf() after sending file ?

Thank you

Here's my Client

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

And here's my Server

    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");
    }
}
  • Have you tried `flush`ing the output stream? – QBrute Jul 20 '21 at 09:04
  • @QBrute I tried to add `dataOutputStream.flush();` after `Files.copy` inside client but I've the same result, it's stuck – Anthonny Giroud Jul 20 '21 at 13:07
  • @user16320675 Thank you, I tried to flush the stream but it doesn't fix the issue, do you have an idea how I could ? – Anthonny Giroud Jul 20 '21 at 13:32
  • @user16320675 By the way, when I kill the process because it's stuck, the file is created with the right content, the response isn't inside the file. – Anthonny Giroud Jul 20 '21 at 13:41
  • 1
    The problem here is that `Files.copy(dataInputStream, Paths.get(fileName))` won't exit unless the end of stream is reached, but that stream is only closed automatically when reaching the end of try block in `Client`. So as Client is running `String response = dataInputStream.readUTF()` before the end of try then it never actually closes the stream being read by `Files.copy` in the Server. Hence they are both stuck - because the Client can't read `response` as the server never reaches the "New file saved" line. – DuncG Jul 20 '21 at 14:30
  • @DuncG Thank you, but if I close the stream after sending the file it looks like the socket is also closed and I can't send the server's response – Anthonny Giroud Jul 20 '21 at 14:37
  • Correct. The easiest solution is send size of file after the filename in `Client` and replace use of `Files.copy` in `Server` - just read the file size and transfer that many bytes to a `Files.newOutputStream`. Then there is no blocking on next byte. – DuncG Jul 20 '21 at 14:40

2 Answers2

0

Here is my entire working sample of sending files from client to server. I am not sure particularly best when I was using Files.copy(...) on server I was getting stuck so you can check I have added boolean condition if the read is less that buffer size then there is no item present in the stream hence exit and close the file.

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

    }
}

Server output

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

Client output

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

silentsudo
  • 6,730
  • 6
  • 39
  • 81
0

As per my comments the problem here is that the pairs of input/output streams are only closed when the try-with-resources blocks exit. So on Server the line Files.copy(dataInputStream, Paths.get(fileName)) won't exit as the end of stream is not reached, because but that stream in Client is only closed automatically when reaching the end of try block in Client.

But client is running String response = dataInputStream.readUTF() before the end of try so it never actually closes the stream. Hence they are both stuck.

The easiest solution is send size of file after the filename in Client and replace use of Files.copy in Server - just read the file size in Server and transfer that many bytes to a Files.newOutputStream. Then there is no blocking on next byte.

Here is an example client with extra line sending the size:

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

Here is adjusted server, this replaces File.copy with a loop over the size bytes.

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");
}
DuncG
  • 12,137
  • 2
  • 21
  • 33