简体   繁体   中英

killing asynctask when doInBackground hangs on a TCP Socket

IPStringI have a fairly simple Activity which works as follows: in the onCreate, I call an AsyncTask TCP Socket connection to a remote server over a proprietary port, send a quick ASCII command to the server and process the response via the onPostExecute(). Works great, it's fast and functional.

However, if the remote server is down -- or I mistakenly entered in the wrong IP address for the communication -- my AsyncTask will hang with the "logging in" dialog spinning on the screen for as long as it takes for the Socket to timeout.

I've been digging away at this for the last 2 days trying to figure out how to call the cancel(), but am failing. Any suggestions? Here's the current code:

 public class Scratchpad extends AsyncTask<Object, String, String>{
private String returningResponse;
private volatile Socket socket;

@Override 
protected void onPreExecute(){
    //throw the "Logging In" dialog up
    initializeDialog();     
    super.onPreExecute();
}
@Override
protected void onProgressUpdate(String... values) {
    super.onProgressUpdate(values);
}
@Override
protected void onPostExecute(String result) {
    //send the ASCII result off to the function that parses through it
    loginTriggerResult(result);
    super.onPostExecute(result);
}
@Override   
protected String doInBackground(Object... params) {
        /*
         * bunch of stuff goes here to strip out the proper 
         * values from the Object for IP address, Port number etc. 
         * params[0], params[1] etc.
         * 
        */
    InetAddress serverIP = null;
    String IPString = (String) params[1];
    int portnumber = (Integer) params[2];

    //convert the String "IPString" into an InetAddress
    try {
        serverIP = InetAddress.getByName(IPString);
    } catch (UnknownHostException e1) {
        // TODO Auto-generated catch block
        e1.printStackTrace();
    }
    //and then, bingo bango bongo, make the connection with the 'try':
    try{
        socket = new Socket(serverIP.getHostAddress(), portnumber);
        //I tried this too, forcing the timeout...  It crashes immediately.
        //SocketAddress socketAddress = new InetSocketAddress(serverIP.getHostAddress(), portnumber);
        //socket.connect(socketAddress, 3000);
        Log.d("networking","Connection Completed");

        //bunch of stuff goes here that happens AFTER the solid connection,
        //which we never get to if the Socket fails on a "waiting to connect" thing.

        //reader.close();
        //outputStream.close();

    }catch (SocketException e){
        e.printStackTrace();
        Log.w("socket","socket closed with SocketException");
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        Log.w("socket","socket closed with IOException");
    }       
    return returningResponse;   //postExecute will handle this.
}

}

I'm not getting any of the Log messages until the 3 or 4 minutes pass that the socket times out. I'm thinking there must be a way to run a TimerTask or something OUTSIDE of the AsyncTask to track how long it's taking to make the connection (or not) and then somehow "cancel()" the running 'try' inside the AsyncTask, but I've been failing on numerous styles.

This HAS to be something that's commonly used, and there's gotta be an easy solution?

UPDATE: The path I was heading down was trying to use the socket connection from within AsyncTask to "monitor itself", and if/when it timed out without getting a solid connection -- trigger the cancel() of the AsyncTask. I was trying to do this from within the AsyncTask itself, with this.cancel() inside the IOException catch of the Socket timeout.

The solution so far is to implement a timer outside of the AsyncTask that launches at the same time as the AsyncTask, and call the cancel from there after however many seconds have transpired. This solution does gracefully exit from the connection attempt, and I can put any sort of Dialog triggers or Toasts inside the onCancelled() stanza of the AsyncTask. It doesn't crash anymore, BUT: the socket is still trying to connect in the background, even though the task is cancelled.

It definitely seems like the simplest solution (which you have tried) would be to set the timeout when calling connect. Can you post the stack trace generated when the connect with timeout fails?

What exactly is it are you trying to do? Detect when you've entered the wrong IP address or the server is down? Only show logging in for a certain amount of time? I get that you're asking how to cancel your async task, but that doesn't seem to be the root of the problem you're trying to solve.

I think MisterSquonnk is exactly right, you should just be able to set the timeout duration when call connect. You may have something else broken in your code if that timeout doesn't work. Alternatively if you just want something that lets you do your own timeout without having anything to do with socket, see below:

If you want to assume you're connection has failed in a shorter amount of time than the socket takes to time out, start a new CountDownTimer http://developer.android.com/reference/android/os/CountDownTimer.html at the same time and in the same place in your activity as you start your async task with whatever you want the timeout duration to be.

After the timer runs out (and onFinish in the timer is called), call cancel on the async task, or otherwise prevent onPostExecute from executing, and do whatever you want to do when a login attempt fails in your UI. Similarly if the login attempt succeeds before the timer runs out, cancel the timer or otherwise prevent onFinish from being called.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM