Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Error: java.net.SocketException: Socket closed

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!

like image 277
D-Nice Avatar asked Jul 07 '14 22:07

D-Nice


3 Answers

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.

like image 32
Christian Esken Avatar answered Oct 11 '22 15:10

Christian Esken


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.

like image 174
nKn Avatar answered Oct 11 '22 14:10

nKn


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.

like image 1
Robert Rowntree Avatar answered Oct 11 '22 14:10

Robert Rowntree