Hi all.
I have some very strange issues in my file transfer application. Every part of it seems to work well EXCEPT for the file transfer part, which acts very unstable. For example, I cannot have too small buffers when transfering to another computer, because then the connection dies, I must have a 1 milisecond delay on the server side after I send the buffer (that contains a peace of the file I want to transfer), or the connection dies, I cannot do this, I cannot do that, sometimes the connection just dies and sometimes it works.
When I look at the exceptions they tend to say "Broken pipe" or "Connection reset".
It is really frustrating. And now I may have found a solution (have only tested it locally so far, and it seems to take away an issue I have when I do that), BUT of course it makes the transfer many times slower. This is driving me nuts.
Note that this is my first real attempt to make a file transfer, so I do not know very much about how to do it in a good way. Could anyone take a look at the code for me and help me to develop a better way to transfer the files? One that does not give all these errors?
(Should warn you, there is a lot of code here... probably does not need to be this large, I might add, this is as stated my first real try at something like this.)
The code on the sending side:
/**
* Conducts the file transfer
*/
private void transfer()
{
/**
* Handle exceptions
*/
try
{
/**
* Create the streams to send data to the client, recieve data from
* the client and read data from the file
*/
DataOutputStream out = new DataOutputStream( mTarget.getOutputStream() );
DataInputStream returned = new DataInputStream( mTarget.getInputStream() );
FileInputStream fis = new FileInputStream( mToTransfer );
InputStream in = new BufferedInputStream( fis );
/**
* Send the name and length of the file
*/
out.writeUTF( mToTransfer.getName() ); out.flush();
out.writeLong( mToTransfer.length() ); out.flush();
/**
* Wait for confirmation from the client that the data has been
* recieved
*/
returned.readBoolean();
/**
* Loop, read and send
*/
byte[] buffer = this.getBuffer( in.available() );
do
{
/**
* Read data into the buffer
*/
in.read( buffer );
/**
* Send the data to the "client"
*
* The length of the data first,
* then the data itself
*/
out.writeShort( (short) buffer.length );
out.write( buffer );
out.flush();
/**
* Wait for confirmation from the other side... This is the fix I talked about before that made the code appear to work better locally but drastically reduced the transfer speed
*/
returned.readBoolean();
/**
* Read how many bytes are available, and make sure the size of
* the buffer is not too large
*/
buffer = this.getBuffer( in.available() );
} while ((buffer.length > 0) && mSend);
/**
* Write that all the data has been transfered
*/
out.writeShort( 0 );
/**
* Close all the streams and connections
*/
in.close();
out.close();
mTarget.close();
System.out.println( "Tranfer complete!" );
}
catch (IOException ioe)
{
/**
* Display error message
*/
System.err.println("TRANSFER ERROR: Could not transfer a file, details:\n" + ioe.getMessage() + "\n");
/**
* Prepare error data
*/
Data dta = new Data();
dta.add( mIDClient );
dta.add("Transference error: " + ioe.getMessage());
/**
* Pass on the error data to be handled in the rest of the program
*/
mContainer.signalError( dta );
}
catch (Exception e)
{
/**
* Display error message
*/
System.err.println("TRANSFER ERROR: Techincal thread error, details:\n" + e.getMessage() + "\n");
/**
* Prepare error data
*/
Data dta = new Data();
dta.add( mIDClient );
dta.add("Transference error: " + e.getMessage());
/**
* Pass on the error data to be handled in the rest of the program
*/
mContainer.signalError( dta );
}
finally
{
/**
* Make sure the file transfer is removed
*/
this.cancel();
mContainer.notifyDeath();
}
}
The "getBuffer" method will simply make sure I do not read in the entire file into memory, but a peace at a time, making it theoretically possible to send huge files (basically it just checks how much data is available in the stream, and if it is larger then the set buffer size it will simply return an array with the size of the buffer). If you want to take a closer look, here is the code for it:
/**
* Returns a new buffer to write data to
*
* @param available how much data is available
* @return the new buffer
*/
private byte[] getBuffer(int available)
{
/**
* Declare return
*/
byte[] buffer = null;
/**
* Set the buffer size
*/
if (available > mBufferSize)
{
buffer = new byte[ mBufferSize ];
}
else
{
buffer = new byte[ available ];
}
/**
* Returns the buffer
*/
return buffer;
}
And here is the receiving sides code:
/**
* Transfer the file
*/
public void handle() throws IOException
{
/**
* Init input stream
*/
DataInputStream in = new DataInputStream( mSocket.getInputStream() );
DataOutputStream back = new DataOutputStream( mSocket.getOutputStream() );
/**
* Get the name
*/
String nme = in.readUTF();//this.getName( in );
long exactFileSize = in.readLong();
double fileSize = exactFileSize;
/**
* Init output stream
*/
File file = new File( nme );
this.checkFile( file );
OutputStream out = new DataOutputStream(new FileOutputStream( file ));
mView.setFileName( file.getName() );
/**
* Confirm that the data has been recieved
*/
back.writeBoolean( true ); back.flush();
/**
* Init all the variables that will be used in the transfer
*/
short size = in.readShort();
long time = System.currentTimeMillis();
double lastSize = 0;
double transfered = size;
byte[] inData = null;
/**
* Recieve the file
*/
while ((size > 0) && mTrans)
{
/**
* Read the data
*/
inData = new byte[ size ];
in.read( inData );
/**
* Write the data
*/
out.write( inData );
out.flush();
/**
* Send confirmation
*/
back.writeBoolean( true ); back.flush();
/**
* Read the size
*/
size = in.readShort();
transfered += size;
/**
* Update the progress bar
*/
if ((System.currentTimeMillis() - time) > 1000)
{
mView.setProgress(String.valueOf((transfered - lastSize) / 8000) + "kbps",
(int) (100 * (transfered / fileSize)));
lastSize = transfered;
time = System.currentTimeMillis();
}
}
/**
* Close streams and the socket
*/
out.close();
in.close();
mSocket.close();
/**
* Show that the transfer is complete
*/
if (file.length() == exactFileSize)
{
JOptionPane.showMessageDialog(null, "The transfer of the file \"" + nme +"\" is complete :)");
}
else
{
JOptionPane.showMessageDialog(null, "Could not transfer \"" + nme +"\", sorry :(");
}
mView.dispose();
}
Can anyone help me? I am not sure where the error is, if it has to do with the communication between the server and client or if it occurs when I try to read data from a file. Could be anything, have no real idea myself... and sometimes it appear to work for no reason. Other times it refuses to. Anyone have an idea?
Take care,
Mr Z
EDIT:
Also, I run this transfer in an own thread on both the sending and the receiving side. Do not know if that matters.
"Operator! Give me the number for 911!"
- Homer J. Simpson