Android Error: java.net.SocketException: Socket closed
Asked Answered
H

5

20

I'm seeing this error come up in my crash logs hundreds of times a week but I have spent at this point several weeks trying to chase down the error without any success. I have been unable to reproduce it on any of my devices. Here's the stack trace:

Posix.java:-2 in "libcore.io.Posix.recvfromBytes"
Posix.java:131 in "libcore.io.Posix.recvfrom"
BlockGuardOs.java:164 in "libcore.io.BlockGuardOs.recvfrom"
IoBridge.java:513 in "libcore.io.IoBridge.recvfrom"
PlainSocketImpl.java:489 in "java.net.PlainSocketImpl.read"
PlainSocketImpl.java:46 in "java.net.PlainSocketImpl.access$000"
PlainSocketImpl.java:241 in "java.net.PlainSocketImpl$PlainSocketInputStream.read"
AbstractSessionInputBuffer.java:103 in "org.apache.http.impl.io.AbstractSessionInputBuffer.fillBuffer"
AbstractSessionInputBuffer.java:191 in "org.apache.http.impl.io.AbstractSessionInputBuffer.readLine"
DefaultResponseParser.java:82 in "org.apache.http.impl.conn.DefaultResponseParser.parseHead"
AbstractMessageParser.java:174 in "org.apache.http.impl.io.AbstractMessageParser.parse"
AbstractHttpClientConnection.java:180 in "org.apache.http.impl.AbstractHttpClientConnection.receiveResponseHeader"
DefaultClientConnection.java:235 in "org.apache.http.impl.conn.DefaultClientConnection.receiveResponseHeader"
AbstractClientConnAdapter.java:259 in "org.apache.http.impl.conn.AbstractClientConnAdapter.receiveResponseHeader"
HttpRequestExecutor.java:279 in "org.apache.http.protocol.HttpRequestExecutor.doReceiveResponse"
HttpRequestExecutor.java:121 in "org.apache.http.protocol.HttpRequestExecutor.execute"
DefaultRequestDirector.java:428 in "org.apache.http.impl.client.DefaultRequestDirector.execute"
AbstractHttpClient.java:555 in "org.apache.http.impl.client.AbstractHttpClient.execute"
AbstractHttpClient.java:487 in "org.apache.http.impl.client.AbstractHttpClient.execute"
AbstractHttpClient.java:465 in "org.apache.http.impl.client.AbstractHttpClient.execute"
Utilities.java:484 in "com.myapp.android.Utilities$8.run"

Here's the block of code where the error is coming from... the exact where the crash occurs is HttpResponse response = httpclient.execute(httppost);:

 public static HttpPost postData(String URL, final List<NameValuePair> params, final Handler handler) {
        // Create a new HttpClient and Post Header
        //android.util.Log.d("Utilities", "Called postData");
        final HttpClient httpclient = new DefaultHttpClient();
        //httpclient.
        final HttpPost httppost = new HttpPost(URL);
        final Message msg = new Message();
        final Bundle dataBundle = new Bundle();
        final ByteArrayOutputStream out = new ByteArrayOutputStream();

        new Thread(){
            @Override
            public void run(){
                String error = "";
                String data = "";
                try {
                    httppost.setEntity(new UrlEncodedFormEntity(params));
                    HttpResponse response = httpclient.execute(httppost);
                    StatusLine statusLine = response.getStatusLine();
                    if(statusLine.getStatusCode() == HttpStatus.SC_OK){
                        response.getEntity().writeTo(out);
                        out.close();
                        data = out.toString();
                    } else{
                        error = EntityUtils.toString(response.getEntity());
                    }
                } catch (ClientProtocolException e) {
                    AirbrakeNotifier.notify(e);
                    error = e.toString();
                } catch (IOException e) {
                    AirbrakeNotifier.notify(e);
                    error = e.toString();
                } catch (Exception ex) {
                    AirbrakeNotifier.notify(ex);
                    error = ex.toString();
                }
                dataBundle.putString("error", error);
                dataBundle.putString("data", data);
                msg.setData(dataBundle);
                handler.dispatchMessage(msg);
            }
        }.start();
        return httppost;
    }

Any help on finally figuring this one out is greatly appreciated!

Hairstreak answered 7/7, 2014 at 22:29 Comment(5)
Debugging code that is invisible is hard.Conversant
Sorry, added some code to look at.Hairstreak
Another thread is closing the socket while this one is blocked in the read(). Are you reusing the HTTPClient by any chance?Iconostasis
No, there is no other thread that closes the socket. We use the postData function every time we want to make a request in the app.Hairstreak
I assume your description means that users of your app are seeing this crash and you can't reproduce it. If that is the case, maybe they just have bad internet connectivity and you need to handle these errors more gracefully. When testing your application, try cutting the internet connection while your app is running and see if it reproduces something similar. If it's a timing problem, put a sleep before the failing line so you can be sure to cut data connection before moving on.Crater
R
17

In my opinion, the culprit of this problem is not your app, but the remote side (i.e., the HTTP server). The most probable thing is that the HTTP server is suddenly resetting the connection and this causes a SocketException in your app. In production environments, these things happen quite often. It might be caused by an overload of the HTTP server, some exceptional circumstance that might make the server close (HTTP request flood, or even an incremented number of requests when the remote server has run out of resources; the server could also run out of its local socket pool... reasons might be dozens).

If the proportion of these errors is low in comparison with the successful HTTP requests, I wouldn't worry much, I'd just wrap that piece of code into a try { ... } catch (SocketException e) { ... } statement and show to the user a dialog telling them that the request has failed and they should retry.

What I would certainly do is try to determine the reason of this behavior: I'd try to match the time of one of these exceptions and try to dig into the HTTP server logs nearly to that time to try to determine the cause of this sudden disconnection (assuming you have access to that logs and other diagnostic tools). As I said before, it might be a silly thing or a bit more complex to debug, but I'd bet this is the problem.

Rockingham answered 10/7, 2014 at 22:52 Comment(0)
S
6

A "java.net.SocketException: Socket closed" exception can happen in various situations. Either the server side closed the connection like nKn suggested, or the client side (your app) closed the connection. Even if you are not aware of doing it, there may be some less obvious code that may lead to closing the socket, like Thread.interrupt() or ExecutorService.shutdownNow().

If on the other hand it actually happens on the server side, I would advise that you implement retries - 3 tries are common practice and usually sufficient.

Striate answered 14/7, 2014 at 22:44 Comment(2)
how would you go about implementing a retry ?Probationer
You can implement retries by doing a simple for or while loop. This can be done with all HTTP clients. But the easiest route would be to use a HTTPClient class that does it automatically for you. The Apache HttpClient documentation on hc.apache.org/httpclient-3.x/tutorial.html states: "Per default HttpClient will automatically attempt to recover from the not-fatal errors, that is, when a plain IOException is thrown. HttpClient will retry the method three times provided that the request has never been fully transmitted to the target server."Striate
S
1

You are currently accepting whatever defaults the client lib is config'd with. Maybe you want to take more control of your Httpclient lib particularly regarding the socket TIMEOUT setting. Dont wait for the server to do something unexpected. Set a shorter timeout than the default and control the errors in a way that will make some sense to your users "Try later msg"....

If u are using default android httpclient you may want to look at alternatives that keep up with the newer Apache client releases...

https://hc.apache.org/httpcomponents-client-4.3.x/android-port.html

https://code.google.com/p/httpclientandroidlib/

general background async client

and note that with either of these you can take into account ( on Wifi ) OR ( on 4G ) that you can dial in detailed timeout profiles where you control the timeouts with code like below:

public void create(int method, final String url, final String data) {
    this.method = method;
    this.url = url;     
    this.data = data;
    if(method == GET){
        this.config = RequestConfig.custom()
            .setConnectTimeout(6 * 1000)
            .setConnectionRequestTimeout(30 * 1000)
            .setSocketTimeout(30 * 1000)                
            .build();
    } else{
        this.config = RequestConfig.custom()
                .setConnectTimeout(6 * 1000)
                .setConnectionRequestTimeout(30 * 1000)
                .setSocketTimeout(60 * 1000)                
                .build();           
    }
    this.context = HttpClientContext.create(); 

using handlers and callbacks to the UI so you can show any alert dialog you want

in runnable where you have the '..client.exec(request$Type)'

        if(httprc < HttpStatus.SC_METHOD_NOT_ALLOWED){

            Log.d(TAG, "entityTYP " +response.getEntity().getClass().getName());
            processEntity(response.getEntity());
            response.close();
        }else{
            Log.d(TAG, "ERR httprc " +httprc);
            throw new IOException("httprc " +httprc +" on " +method);
            }                               
        this.context.getConnection().close();
} catch (Exception e) {  // this will catch your 'socketException'
   // catch and use the looper to direct to the desired UI thread handle
    handler0.sendMessage(Message.obtain(handler,
            HttpConnection.DID_ERROR, e.getMessage()));
}

back on the UI thread you control the alert for as many diff handlers as u need....

           handler0 = new Handler() {
               public void handleMessage(Message message) {
                 switch (message.what) {
                 case HttpConnection.DID_START: {
                   break;
                 }
                 case HttpConnection.DID_SUCCEED: {                                          
                   break;
                 }
                 case HttpConnection.DID_ERROR: {
                       toggleSpin(false);
                       cleanup();
                    //have access to orig message.obj here as well
                       Toast.makeText(Speech_API_Activity.this, getResources().getString(R.string.heroku_msg_mux),
                            Toast.LENGTH_SHORT).show();
                       break;
                     }

If necessary, you may establish diff timeout profiles by domain. It takes time to learn all the builder stuff and config stuff with these other 2 httpclient packages but , it may be worth the time because you can set it up to do anything you want and you have total control of exceptions and of what you want to route back to the UI.

Sangraal answered 10/7, 2014 at 23:53 Comment(0)
C
1
  • What are you doing with HttpPost object returned from method postData(...) ? That may be one of the cause to consider. Note there are two threads, one main thread and another one that you spawn above.

  • In finally block you need to close resources like httpClient and
    response as well as completely empty out response inputstream. See examples for reference.

  • Explicitly set charset on UrlEncodedFormEntity, may be to UTF-8

Constrictor answered 14/7, 2014 at 21:27 Comment(0)
E
0

try to use this

public static String WebserviceResponseHandle(String url,
        List<NameValuePair> namvaluePair) {
    String result = null;
    try {
        HttpParams httpParams = new BasicHttpParams();
        HttpConnectionParams.setConnectionTimeout(httpParams, 10000);
        HttpConnectionParams.setSoTimeout(httpParams, 10000);
        HttpClient client = new DefaultHttpClient(httpParams);
        HttpPost httppost = new HttpPost(url);
        httppost.setEntity(new UrlEncodedFormEntity(namvaluePair));
        HttpResponse response = client.execute(httppost);
        HttpEntity entity = response.getEntity();

        // If the response does not enclose an entity, there is no need
        if (entity != null) {
            InputStream instream = entity.getContent();
            result = convertStreamToString(instream);

        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return result;
}


    private static String convertStreamToString(InputStream is) {
    /*
     * To convert the InputStream to String we use the
     * BufferedReader.readLine() method. We iterate until the BufferedReader
     * return null which means there's no more data to read. Each line will
     * appended to a StringBuilder and returned as String.
     */
    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
    StringBuilder sb = new StringBuilder();

    String line = null;
    try {
        while ((line = reader.readLine()) != null) {
            sb.append(line + "\n");
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            is.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    return sb.toString();
}
Edelmiraedelson answered 17/7, 2014 at 10:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.