File Transfer via Java Sockets
I suppose you have been doing some socket programming in pure java [ for whatever reason(s) you might be having️ ] and this occurred to you “hmm.. can I send files over sockets?” well you can, but there are two main problems I encountered, which we will discuss later in this read. for now, let’s begin with prerequisites and basics of socket programming (TCP).
prerequisites: File Handling in java, viz FileInputStream, FileOutputStream and their read, write methods
What is a socket?
A socket is a software ( logical ) endpoint that establishes bidirectional communication between a server and one or more client programs. A socket is bound to a port number so that the TCP layer can identify the application that data is destined to be sent to. An application software defines a socket so that it utilizes ports in the underlying computer for its implementation. This enables programmers to comfortably deal with the low-level details of the network communication such as ports, routing, etc inside their application code.
How do sockets work?
The TCP socket communication between a client and the server consists of several phases.
Implementing TCP socket communication in Java
Before we start looking into file transfer let’s see how we can implement socket communication in Java. Here, we will be writing two Java programs. One will be the program running on the server while the other one is the client program, which will be communicating with the server.
- Here, ServerSocket opens a socket at port 5000 of the local machine and waits for a client,
server.accept()
accepts the incoming client connection. - Once a client is connected, output and input streams are instantiated, which can be used to communicate with the connected client using one of the
read
,write
methods provided by the stream. - In the above program, the server loops and accepts the string input coming from the client using
dataInputStream.readUTF()
then prints it on the console and breaks out of the loop when the received input isexit().
- Here Socket connects the client application to the above server listening at localhost:5000, after the connection is accepted output and input streams are instantiated.
- this program accepts string input from the console then sends it to the server using
dataOutputStream.writeUTF()
and exits out of the loop when the scanned input matchedexit()
.
Running the programs:
First, run the
Server.java
program and then run theClient.java
program. You will see the connection accepted message at the server side and client side will be acceping input. The typed inputs on client side will be echoed at the server side, to exit typeexit()
at the client application which breaks thewhile(true)
loop.
if you made it this far, you’ve acquired just enough knowledge to accomplish the task.
What are the Problems?
file transfer would’ve been as simple as this:
void sendFile(String filePath){
byte[] buffer = new byte[Integer.MAX_VALUE];
fileInputStream = new FileInputStream(filePath);
int bytes = fileInputStream.read(buffer,0,buffer.length);
dataOutputStream.write(buffer,0,bytes);
}void receiveFile(String newFileName){
byte[] buffer = new byte[Integer.MAX_VALUE];
int bytes = dataInputStream.read(buffer,0,buffer.length);
fileOutputStream = new FileOutputStream(newFileName);
fileOutputStream.write(buffer,0,bytes);
}
but there are two main problems with this code.
File Size:
one may say what if the file size is larger than Integer.MAX_VALUE
bytes i.e. 2GB, which is more than enough if that was the case.
The real problem is the socket is allowing only 65482 bytes i.e. 64KB at once, [I’m not sure why perhaps due to frame size limit if it's even a thing 😅] which is quite less than most of the document file size uploaded on the web.
Multiple Files:
even if the files are lesser the 64KB, another problem with this code is when sending multiple files and detecting the EOF, there won’t be any problem at the sender’s side as fileInputStream.read()
does it implicitly for the opened file (as EOF causes end of stream itself).
But at the receiver side where data is received from dataInputStream
the multiple files gets queued one after other and there won’t be any means to detect EOF for each file separately, as a result, it will read the entire bytes queued as a single file. On writing this byte buffer using fileOutputStream.write(buffer)
the writing stops when EOF for the first file is detected and remaining files are lost.
Note: both Sender and receiver are running on independent threads so, we can’t restrict the system to perform one
dataIstream.read()
followed by eachdataOutputStream.write()
, hence avoiding multiple files getting queued.
- Therefore, the above code works only for a single file of size less than 64KB
Solution:
File Size: an obvious solution to this problem is breaking the file into multiple chunks of let’s say 4KBs then sending them through the socket within a looping statement and receiving in a similar manner i.e. wile(stream.read(buffer)!=-1){ ... }
Multiple Files: the main cause of this problem is we don’t know the EOF for each file in the bytes receives by dataInputStream
, this can be solved by sending file size (in bytes) before sending each file, thus we can break reading from dataInputStream
after file size and repeat the same for remaining files queued in the byte buffer.
Modified Code:
The above example is an illustration for pdfs only but, can be modified to support any type of file by managing the extensions properly i’ll leave that upto upto you.
— all responses and queries are welcomed.