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