Java Multiple File Transfer Over Socket

Java multiple file transfer over socket

You are reading the socket until read() returns -1. This is the end-of-stream condition (EOS). EOS happens when the peer closes the connection. Not when it finishes writing one file.

You need to send the file size ahead of each file. You're already doing a similar thing with the file count. Then make sure you read exactly that many bytes for that file:

String filename = dis.readUTF();
long fileSize = dis.readLong();
FileOutputStream fos = new FileOutputStream(filename);
while (fileSize > 0 && (n = dis.read(buf, 0, (int)Math.min(buf.length, fileSize))) != -1)
{
fos.write(buf,0,n);
fileSize -= n;
}
fos.close();

You can enclose all this in a loop that terminates when readUTF() throws EOFException. Conversely of course you have to call writeUTF(filename) and writeLong(filesize) at the sender, before sending the data.

send multiple files with JAVA socket

I've rewritten your send file method slightly so that you can send multiple files, you will need to pass it the DataOutputStream and close the stream after you have sent all the files you wish to send.

When reading, you should use a DataInputStream and call long len = dis.getLong() and then read from the stream the len bytes, then repeat for the next file. You may find it useful to send the number of files at the start to.

public void sendFile(File file, DataOutputStream dos) throws IOException {
if(dos!=null&&file.exists()&&file.isFile())
{
FileInputStream input = new FileInputStream(file);
dos.writeLong(file.getLength());
System.out.println(file.getAbsolutePath());
int read = 0;
while ((read = input.read()) != -1)
dos.writeByte(read);
dos.flush();
input.close();
System.out.println("File successfully sent!");
}
}

Sending Multiple Files Over a Java Socket

TCP is a byte stream. It has no concept of messages. As such, you need to frame the file data in such a way that the receiver knows where one file ends and the next begins. In this case, you need to send the file size before sending the file data, so the receiver knows how many bytes to expect per file.

Also, you are not paying attention properly to the number of bytes that read() tells you were actually read each time you call it. You are giving it a large buffer, but there is no guarantee that the entire buffer will be filled up. Especially at the end of a file.

And don't transfer binary file data using strings!

Try something more like this instead:

Client:

File fs = new File("Test1.txt");
long fileLength = fs.length();

InetAddress host = InetAddress.getLocalHost();
Socket clientSocket = new Socket(host, 6789);
DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());

long startTime, endTime, tGap, totalTime = 0;
byte[] sendData = new byte[1024];
int numBytes, filesSent = 0;

while (filesSent < 100) {
FileInputStream fin = new FileInputStream(fs);
long thisFileLength = fileLength;

System.out.println("Sending file to server...");
startTime = System.currentTimeMillis();

outToServer.writeLong(thisFileLength);

while (thisFileLength > 0) {
if (thisFileLength < sendData.length) {
numBytes = fin.read(sendData, 0, (int) thisFileLength);
} else {
numBytes = fin.read(sendData, 0, sendData.length);
}

System.out.println(numBytes);
if (numBytes < 1) {
break;
}

outToServer.write(sendData, 0, numBytes);
thisFileLength -= numBytes;
}

outToServer.flush();

endTime = System.currentTimeMillis();
tGap = endTime - startTime;
totalTime = totalTime + tGap;
System.out.println("Finished run " + filesSent + " with a time of " + tGap);
filesSent++;

fin.close();

if (thisFileLength > 0) {
break;
}
}

clientSocket.close();

System.out.println("I am done to send " + filesSent + " times file, and the average time is: " + (double) totalTime / filesSent);

Server:

ServerSocket welcomeSocket = new ServerSocket(6789);
System.out.println("I am starting now....");

Socket connectionSocket;
connectionSocket = welcomeSocket.accept();
DataInputStream inFromClient = new DataInputStream(new BufferedInputStream(connectionSocket.getInputStream()));

long startTime, endTime, tGap, totalTime = 0;
byte[] receiveData = new byte[1024];
int numBytes, filesRecieved = 0;

while (filesRecieved < 100) {
string filename = "receivedFile" + filesRecieved + ".txt";
FileOutputStream outPut = new FileOutputStream(filename);

startTime = System.currentTimeMillis();

long fileLength = inFromClient.readLong();
System.out.println("OG Bytes: " + fileLength);

while (fileLength > 0) {
if (fileLength < receiveData.length) {
numBytes = inFromClient.read(receiveData, 0, (int) fileLength);
}
else {
numBytes = inFromClient.read(receiveData, 0, receiveData.length);
}

if (numBytes < 1) {
break;
}

try {
outPut.write(receiveData, 0, numBytes);
System.out.println(numBytes);
}
catch (Exception e) {
break;
}

fileLength -= numBytes;
}

outPut.close();

endTime = System.currentTimeMillis();
tGap = endTime - startTime;
totalTime = totalTime + tGap;
System.out.println("I have received " + filename + " using time = " + tGap);
filesRecieved++;

if (fileLength > 0) {
break;
}
}

connectionSocket.close();

System.out.println("I am done now... and the average time used to receive each copy is: " + totalTime / filesRecieved);
welcomeSocket.close();

How to send a list of files over a socket in Java

Here is a full implementation:

Sender Side:

String directory = ...;
String hostDomain = ...;
int port = ...;

File[] files = new File(directory).listFiles();

Socket socket = new Socket(InetAddress.getByName(hostDomain), port);

BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
DataOutputStream dos = new DataOutputStream(bos);

dos.writeInt(files.length);

for(File file : files)
{
long length = file.length();
dos.writeLong(length);

String name = file.getName();
dos.writeUTF(name);

FileInputStream fis = new FileInputStream(file);
BufferedInputStream bis = new BufferedInputStream(fis);

int theByte = 0;
while((theByte = bis.read()) != -1) bos.write(theByte);

bis.close();
}

dos.close();

Receiver Side:

String dirPath = ...;

ServerSocket serverSocket = ...;
Socket socket = serverSocket.accept();

BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
DataInputStream dis = new DataInputStream(bis);

int filesCount = dis.readInt();
File[] files = new File[filesCount];

for(int i = 0; i < filesCount; i++)
{
long fileLength = dis.readLong();
String fileName = dis.readUTF();

files[i] = new File(dirPath + "/" + fileName);

FileOutputStream fos = new FileOutputStream(files[i]);
BufferedOutputStream bos = new BufferedOutputStream(fos);

for(int j = 0; j < fileLength; j++) bos.write(bis.read());

bos.close();
}

dis.close();

I did not test it, but I hope it will work!

Sending multiple files over java sockets

Sorry but I guess you have missed quite some aspects from the design and code. 1st of all you need the protocol, 2nd you need a single source of input stream on target side.

Protocol

In the output stream of local side, I suggest...

  1. Number of how many files are to be sent.
  2. Length of file name.
  3. The file name.
  4. Length of the file.
  5. File content
  6. Repeat 2~5 for the next file if there are any.

So the output stream would be something like:

[num_of_files][file1_name_length][file1_name][file1_size][file1_data][file2_.....]

Target Side

I suggest opening up ONE input stream ONLY and read according to the protocol. Remember flushing and closing each FileOutputStream in each loop.

How to read Socket InputStream for multiple files over byte[] streams?

Make sure you flush the PrintWriter before you then write raw bytes directly to the OutputStream that the PrintWriter is attached to. Otherwise, you could write any buffer data out of order to the underlying socket.

But more importantly, make sure that if you use buffered reading on the receiving end that you read the file bytes using the same buffer that receives the file name and file size. You should also transfer the File using smaller fixed chunks, don't allocate a single byte[] array for the entire file size, that is a waste of memory for large files, and likely to fail.

Server:

try{
byte[] bytes = new byte[1024];

FileInputStream fis = new FileInputStream(file);
OutputStream os = socket.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(os);

PrinterWriter pw = new PrintWriter(bos);
pw.println(file.getName()); // Send Filename
pw.println(file.length()); // Send filesize
pw.flush();

int count;
while ((count = fis.read(bytes)) > 0) {
bos.write(bytes, 0, count);
}
bos.flush();
fis.close();
}catch(IOException e){
e.printStackTrace();
}
}

Client:

try{
byte [] buf = new byte [1024];

FileOutputStream fos = new FileOutputStream(file);
InputStream is = socket.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);

InputStreamReader isr = new InputStreamReader(bis);
String file = isr.readLine(); // Read filename
long fileSize = Long.parseLong(isr.readLine()); // Read Filesize

int count = 0;
while ((fileSize > 0) && (count = bis.read(buf, 0, (int)Math.min(buf.length, fileSize))) > 0){
fos.write(buf, 0, count);
fileSize -= count;
}

fos.close();
}catch(IOException e){
e.printStackTrace();
}

That being said, you might also consider using DataOutputStream.writeLong() and DataInputStream.readLong() to send/receive the file size in its original binary format instead of as a textual string:

Server:

try{
byte[] bytes = new byte[1024];

FileInputStream fis = new FileInputStream(file);
OutputStream os = socket.getOutputStream();
BufferedOutputStream bos = new BufferedOutputStream(os);

PrinterWriter pw = new PrintWriter(bos);
pw.println(file.getName()); // Send Filename
pw.flush();

DataOutputStream dos = new DataOutputStream(bos);
dos.writeLong(file.length()); // Send filesize
dos.flush();

int count;
while ((count = fis.read(bytes)) > 0) {
bos.write(bytes, 0, count);
}
bos.flush();
fis.close();
}catch(IOException e){
e.printStackTrace();
}
}

Client:

try{
byte [] buf = new byte [1024];

FileOutputStream fos = new FileOutputStream(file);
InputStream is = socket.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);

InputStreamReader isr = new InputStreamReader(bis);
String file = isr.readLine(); // Read filename

DataInputStream dis = new DataInputStream(bos);
long fileSize = dis.readLong(); // Read Filesize

int count = 0;
while ((fileSize > 0) && (count = bis.read(buf, 0, (int)Math.min(buf.length, fileSize))) > 0){
fos.write(buf, 0, count);
fileSize -= count;
}

fos.close();
}catch(IOException e){
e.printStackTrace();
}


Related Topics



Leave a reply



Submit