download multiple subsequent files from one server with one TCP handshake - android

I try to create an application that can download files from a server in order to measure the speed that I can get with the particular server. I do this by using the Asynctask class. All the files that I want to download are located on the same directory. My question is, how can I download the subsequent files by keeping the connection and not by creating every time a new one? I know that for a TCP connection, there must be a 3-way-handshake established, before downloading a file. I want to connect to the server and then keep the connection and perform the download.
My code looks like this
#Override
protected Integer doInBackground(String... sUrl) {
try {
speed=0; //initial value
int i=0;
while ((i<sUrl.length)) {
URL url = new URL(sUrl[i]); //sUrl[] contains the links that i want
// for example http://myserver.net/file1.jpg, http://myserver.net/file2.jpg ... etc
URLConnection connection = url.openConnection();
connection.connect(); //connection to be established 3WAY HANDSHAKE
int fileLength = connection.getContentLength();
// download the file
InputStream input = new BufferedInputStream(url.openStream());
OutputStream output = new FileOutputStream(file);
byte data[] = new byte[1024];
long total = 0;
int count;
long start = System.currentTimeMillis();
while ((count = input.read(data)) != -1) {
total += count;
publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
long finish = System.currentTimeMillis();
long tempSpeed= (fileLength *8)/(finish-start);
if (tempSpeed>speed) {
speed=tempSpeed;
}
output.flush();
output.close();
input.close(); // connection is closed
i++;
}
}catch(Exception e) {
exceptions.add(e);
}
return 1;
}
By creating a new connection I loose time (for the download speed), because of the 3way handsharke . Also when transfering files in TCP, there is something called a tcp window (when you dowload data, initialy you start with low speed transmission, and if the connection is good this rate increases).
How can I do the above without creating and tearing down the connection for each file?

Looking at you code, you keep receiving until the socket is close at the other side, so there is no way of using the same socket since it's closed. If you can program both, the server and the client, then, I would suggest one possible way of doing it, and that is with a protocol, instead of receiving the file directly, the first packet you get is an integer which indicates the size of the file you're going to receive. If that length is 0 (cero) it means there no more files and the connection should be closed.
At the server:
While (the_are_files_to_send)
{
Socket.write((int) FileSize);
Socket.write(file's content);
}
Socket.Write(0); // No more files;
Socket.Close();
At the Client:
While ((size = Socket.read(buffer, 0, 4)) != -1)
{
int FileLength = convert_to _int(buffer);
if (FileLength==0) break;
Socket.read(FileLength bytes);
}
Socket.Close();

Related

Android - Best Practice for Downloading Medium - Large Files Quickly

I need to download a few large zip files into my application (each approx 25mb) however it seems quite slow (5 minutes +) and when we test the same files being downloaded on an iPad it's downloading several times faster. I've considered using Volley, however it seems asynctask is the best for large files (from what I've read).
Does anyone have any suggestions or ideas on how I might be able to download/write these files faster?
My current implementation is show below:
My AsyncTaskExample:
#Override
protected String doInBackground(String... sUrl) {
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL url = new URL(sUrl[0]);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return "Server returned HTTP " + connection.getResponseCode()
+ " " + connection.getResponseMessage();
}
// this will be useful to display download percentage
// might be -1: server did not report the length
int fileLength = connection.getContentLength();
// download the file
input = connection.getInputStream();
output = new FileOutputStream("/sdcard/file_name.extension");
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
// allow canceling with back button
if (isCancelled()) {
input.close();
return null;
}
total += count;
// publishing the progress....
if (fileLength > 0) // only if total length is known
publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
} catch (Exception e) {
return e.toString();
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
return null;
}
AsyncTask should be used only for relatively short background processes (i.e. processes that last a few seconds). From the docs:
AsyncTask is designed to be a helper class around Thread and Handler
and does not constitute a generic threading framework. AsyncTasks
should ideally be used for short operations (a few seconds at the
most.) If you need to keep threads running for long periods of time,
it is highly recommended you use the various APIs provided by the
java.util.concurrent package such as Executor, ThreadPoolExecutor and
FutureTask.
For long operations you should use a Service:
A Service is an application component representing either an
application's desire to perform a longer-running operation while not
interacting with the user or to supply functionality for other
applications to use.

Check integrity of Database SQLite Android

I have a SQLite database file in my server, and from time to time my Android App checks if there is a new SQLite database file. If true the App downloads the File and replaces the old database.
The problem is, that some times the new database file gets corrupted and the App start to crashing and never recovers if I dont manualy clean the app in the Android Settings.
My question is, there is a way to check the integrity of SQLite Database after the Downloaded?
This is my code for download the new Database from the server this code is placed in an AssyncTask :
protected Boolean doInBackground(String... Url) {
try {
URL url = null;
if(Url[0].equals("")){
mSyncDate = mConnectionManager.getSyncDate();
url = new URL(Constants.HF_SERVER_DATABASE+"db_fxbus_"+convertDateToFormatYYYYMMDD(mSyncDate.getServerDate())+".sqlite");
}else{
url = new URL(Url[0]);
}
URLConnection connection = url.openConnection();
connection.connect();
// this will be useful so that you can show a typical 0-100% progress bar
int fileLength = connection.getContentLength();
mDB.getReadableDatabase();
// download the file
InputStream input = new BufferedInputStream(url.openStream());
Log.i(TAG, "Path:"+mContext.getDatabasePath("HorariosDoFunchal").getAbsolutePath());
OutputStream output = new FileOutputStream(mContext.getDatabasePath("HorariosDoFunchal").getAbsolutePath());
startWriting = true;
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
total += count;
// publishing the progress....
publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
//Log.i(TAG, "Executing ...");
}
//Log.i(TAG, "Finish ...");
output.flush();
output.close();
input.close();
} catch (Exception e) {
Log.e(TAG, e.toString());
return false;
}
return true;
}
Look into:
pragma integrity_check;
it will scan the Database and check it for errors and other things too.
More info(and more commands) can be found at this link:
http://www.sqlite.org/pragma.html
also check out the documentation of isDatabaseIntegrityOk().
You could try to use PRAGMA integrity_check (or Android's equivalent isDatabaseIntegrityOk()), but this checks only the database structure for errors, and can detect only errors where it can prove that the structure is wrong.
To be able to detect all errors (especially in your own data), you need to compute a checksum for the entire database file.

URLConnection.getContentLength() returns -1 on Android KitKat

I'm new to Android and developing a file downloading app with a ProgressDialog which shows the downloading percentage.
I use AsyncTask and here is the trouble part of my code.
protected String doInBackground(String... f_url){
int count;
try {
URL url = new URL(f_url[0]);
URLConnection conn = url.openConnection();
conn.connect();
// getting file length
int lenghtOfFile = conn.getContentLength();
// input stream to read file - with 8k buffer
InputStream input = new BufferedInputStream(url.openStream(), 8192);
File direct = new File(folder);
if(!direct.exists()) {
direct.mkdirs();
}
// Output stream to write file
OutputStream output = new FileOutputStream(apkPath);
byte data[] = new byte[1024];
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
// publishing the progress....
// After this onProgressUpdate will be called
publishProgress(""+(int)((total*100)/lenghtOfFile));
// writing data to file
output.write(data, 0, count);
}
// flushing output
output.flush();
// closing streams
output.close();
input.close();
} catch (Exception e) {
Log.e("Error: ", e.getMessage());
}
return null;
}
My issue is this code works really well on Android API 16 (JB) but not on API 19 (KitKat). On KitKat devices, the progress bar percentage does not update (always 0). After checking the codes, I found conn.getContentLength() returns -1 when I run it on KitKat. So it can not update the progress. But it returns correct file size when I run it on API 16 (JB).
Can somebody please help me to solve this?
Thank you in advance.
Have you read Migrating to WebView in Android 4.4: http://developer.android.com/guide/webapps/migrating.html
Blockquote
If you call methods on WebView from any thread other than your app's UI thread, it can cause unexpected results. For example, if your app uses multiple threads, you can use the runOnUiThread() method to ensure your code executes on the UI thread:
runOnUiThread(new Runnable() {
#Override
public void run() {
// Code for WebView goes here
}
});
You can try this:
conn.setRequestProperty("Accept-Encoding", "identity");

Android client / server, client not receiving all data

I have probably been staring at this too long and can't see what the problem is.
I have a server which accepts multiple client connections and saves the sockets. The server can then send out control messages to the clients to perform tasks and, among other things, I want to send files out to all the connected clients but the client only seems to receive part of them.
For testing purposes I have a randomly generated text file 69075 bytes but although it seems like whole whole file is sent, the client only receives upto around 57000 bytes (varies). I have added a button on the server app which lets me close the client socket and when I do that the client receives -1 followed by the rest of the missing file.
Server sending file:
try {
getClientList().get(0).setLocked(true);
byte [] mybytearray = new byte [1024];
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(myFile));
OutputStream os = getClientList().get(0).getmSocket().getOutputStream();
Boolean eof = false;
int bytesRead;
int total = 0;
do {
bytesRead = bis.read(mybytearray, 0, mybytearray.length);
if (bytesRead != -1) {
os.write(mybytearray, 0, bytesRead);
total += bytesRead;
Log.d(TAG_SF, "Total: "+total+" :Sent: "+ bytesRead);
} else {
eof = true;
Log.d(TAG_SF, "EOF, Total sent: " + total);
}
} while (!eof);
os.flush();
bis.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
getClientList().get(0).setLocked(false);
}
A message is sent to the clients with the file name and file size before the file is send and it is received ok.
Client receiving file:
try {
int total = 0;
byte[] myByteArray = new byte[1024];
int bytesRead;
InputStream is = serverSocket.getInputStream();
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(path+SD_DIRECTORY+fileName));
do {
bytesRead = is.read(myByteArray, 0, myByteArray.length);
if (bytesRead != -1){
bos.write(myByteArray, 0, bytesRead);
total = total+bytesRead;
Log.e(TAG,"Total > "+total+ ": No Bytes : "+bytesRead);
} else {
Log.e(TAG,"EOF, total received: "+total);
break;
}
} while (total < fileLength); //fileLength and filename are sent before file
Log.e(TAG,"Total > "+total+ ": No Bytes : "+bytesRead);
bos.flush();
bos.close();
} catch (Exception e) {
e.printStackTrace();
}
The problem was not with the code for sending and receiving, it works fine.
I was sending a message to the client first with a control code indicating a file was to be send, followed by the file size and file name. The client then waits for the file but the server started sending before the client was ready and was missing the start of the file.
To test, put the server thread to sleep for a couple of seconds after sending the file name and before sending the file and it works fine. I will probably have the client send a 'ready' message to the server as a better solution.
EDIT:
Although this worked, it is not the answer, it does seem to be related to using both buffered and unbuffered streams (see EJP comment). Will do a bit of re writing and update accordingly. Not sure of SO protocol, should I delete a wrong answer?
EDIT (again)
EJPs comment is absolutely correct, it was related to having a buffered input stream open to receive the file name and then opening an unbuffered stream elsewhere to transfer the file. My knowledge is limited in this area (and generally) so I just re wrote to use the same unbuffered stream for both.

Android Check if connection was interrupted

My app needs to download a database from the internet. This is done in an AsyncTask so when the database is being downloaded it't is being shown a Progress Dialog.
This is the code for the database download:
public void downloadDB(String dbFile, String dbFileName) {
int count;
try {
URL url = new URL(dbFile);
URLConnection conection = url.openConnection();
conection.connect();
InputStream input = new BufferedInputStream(url.openStream(), 8192);
OutputStream output = new FileOutputStream(ctx.getApplicationInfo().dataDir + "/databases/" + dbFileName);
byte data[] = new byte[1024];
while ((count = input.read(data)) != -1) {
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
} catch (IOException e) {
Log.e("Error: ", e.getMessage());
}
}
}
I'm doing some tests and when the database is being downloaded I turn off the internet connection. The problem is that the Progress Dialog doesn't terminate (it's in an infinite loop). I was expecting that after turning off the internet, there would be the IOException, the function returned to the doInBackground method of AsyncTask and executed the onPostExecute method where I dismiss the Progress Dialog.
The strange thing is that if I have my phone disconnected from the computer, the progress dialog is never dismissed, the same thing if I'm running the app with phone connected to computer and looking at the log cat. If I enter debug mode, the downloadDB ends and is returned to the caller and then, the progress dialog is dismissed.
What can I do to return from the downloadDB function when the network is turned off?

Categories

Resources