Ошибка Android: java.net.SocketException: сокет закрыт

Я вижу, как эта ошибка появляется в моих журналах аварий сотни раз в неделю, но я потратил несколько недель, пытаясь найти ошибку без какого-либо успеха. Я не смог воспроизвести его ни на одном из моих устройств. Вот трассировка стека:

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"

Здесь блок кода, из которого исходит ошибка... точное место сбоя - 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;
    }

Любая помощь в окончательном выяснении этого очень ценится!

Ответ 1

На мой взгляд, виновником этой проблемы является не ваше приложение, а удаленная сторона (то есть HTTP-сервер). Наиболее вероятным является то, что HTTP-сервер внезапно сбрасывает соединение, и это вызывает SocketException в вашем приложении. В производственных условиях такие вещи случаются довольно часто. Это может быть вызвано перегрузкой HTTP-сервера, какими-то исключительными обстоятельствами, которые могут привести к его закрытию (переполнение HTTP-запроса, или даже увеличением количества запросов, когда на удаленном сервере закончились ресурсы; на сервере также может закончиться его локальный пул сокетов... причин может быть несколько десятков).

Если бы доля этих ошибок была низкой по сравнению с успешными HTTP-запросами, я бы не стал сильно беспокоиться, я просто заключил бы этот фрагмент кода в оператор try { ... } catch (SocketException e) { ... } и показал бы пользователю диалоговое окно, сообщающее им, что запрос не удалось, и они должны повторить попытку.

Что я, безусловно, сделал бы, так это попытался бы определить причину такого поведения: я бы попытался сопоставить время одного из этих исключений и попытаться покопаться в журналах HTTP-сервера почти до того времени, чтобы попытаться определить причину этого внезапного отключение (при условии, что у вас есть доступ к этим журналам и другим средствам диагностики). Как я уже говорил, это может быть глупо или немного сложнее в отладке, но держу пари, что это проблема.

Ответ 2

Исключение "java.net.SocketException: Socket closed" может возникать в различных ситуациях. Либо сторона сервера закрыла соединение, как предложено nKn, либо сторона клиента (ваше приложение) закрыла соединение. Даже если вы не знаете, как это сделать, может быть какой-то менее очевидный код, который может привести к закрытию сокета, например Thread.interrupt() или ExecutorService.shutdownNow().

Если, с другой стороны, это действительно происходит на стороне сервера, я бы посоветовал вам выполнить повторные попытки - 3 попытки являются обычной практикой и обычно достаточны.

Ответ 3

В настоящее время вы принимаете любые значения по умолчанию, с которыми клиентская библиотека настроена. Может быть, вы хотите получить больше контроля над своей библиотекой Httpclient, особенно в отношении настройки сокета TIMEOUT. Не ждите, пока сервер сделает что-то неожиданное. Установите более короткий тайм-аут, чем по умолчанию, и управляйте ошибками таким образом, чтобы ваши пользователи имели некоторый смысл: "Попробуйте позже, msg"....

Если вы используете httpclient для Android по умолчанию, возможно, вы захотите посмотреть на альтернативы, которые соответствуют новым версиям клиента Apache...

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

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

общий фоновый асинхронный клиент

и обратите внимание, что с любым из них вы можете принять во внимание (на Wi-Fi) ИЛИ (на 4G), что вы можете набрать подробные профили времени ожидания, где вы управляете временем ожидания с помощью кода, как показано ниже:

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(); 

используя обработчики и обратные вызовы для пользовательского интерфейса, чтобы вы могли показывать любой диалог оповещения, который вы хотите

в исполняемом файле, где у вас есть "..client.exec (запрос $ 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()));
}

вернувшись в поток пользовательского интерфейса, вы управляете оповещением для столько разностных обработчиков, сколько вам нужно....

           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;
                     }

При необходимости вы можете установить профили времени ожидания для разных доменов. Требуется время, чтобы изучить все компоненты сборки и настройки с этими двумя другими пакетами httpclient, но это может стоить времени, потому что вы можете настроить его так, чтобы делать все, что захотите, и у вас есть полный контроль над исключениями и тем, что вы хотите маршрут обратно в интерфейс.

Ответ 4

  • Что вы делаете с объектом HttpPost, возвращенным из методаpostData (...)? Это может быть одной из причин для рассмотрения. Обратите внимание, там два потока, один основной поток и другой, который вы порождаете выше.

  • В окончательном блоке вам нужно закрыть ресурсы, такие как httpClient и
    ответ, а также полностью опустошить входные данные ответа. Смотрите примеры для справки.

  • Явно установить кодировку на UrlEncodedFormEntity, может быть UTF-8

Ответ 5

попробуйте использовать это

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 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();
}