Ошибка 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;
    }

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


person D-Nice    schedule 07.07.2014    source источник
comment
Отладка невидимого кода сложна.   -  person takendarkk    schedule 08.07.2014
comment
Извините, добавил код для просмотра.   -  person D-Nice    schedule 08.07.2014
comment
Другой поток закрывает сокет, в то время как этот заблокирован в read(). Вы случайно не используете HTTPClient повторно?   -  person user207421    schedule 08.07.2014
comment
Нет, другого потока, закрывающего сокет, нет. Мы используем функцию postData каждый раз, когда хотим сделать запрос в приложении.   -  person D-Nice    schedule 08.07.2014
comment
Я предполагаю, что ваше описание означает, что пользователи вашего приложения видят этот сбой, и вы не можете его воспроизвести. Если это так, возможно, у них просто плохое подключение к Интернету, и вам нужно более изящно обрабатывать эти ошибки. При тестировании вашего приложения попробуйте отключить интернет-соединение во время работы вашего приложения и посмотреть, воспроизводит ли оно что-то подобное. Если это проблема синхронизации, поместите спящий режим перед неисправной линией, чтобы вы могли быть уверены, что разорвете соединение для передачи данных, прежде чем двигаться дальше.   -  person blh83    schedule 11.07.2014


Ответы (5)


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

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

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

person nKn    schedule 10.07.2014

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

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

person Christian Esken    schedule 14.07.2014
comment
как бы вы реализовали повторную попытку? - person jesses.co.tt; 17.07.2014
comment
Вы можете реализовать повторы, выполнив простой цикл for или while. Это можно сделать со всеми HTTP-клиентами. Но проще всего использовать класс HTTPClient, который делает это автоматически. Документация Apache HttpClient на http://hc.apache.org/httpclient-3.x/tutorial.html сообщает: По умолчанию HttpClient будет автоматически пытаться восстановиться после нефатальных ошибок, то есть при возникновении обычного исключения IOException. HttpClient трижды повторит попытку метода при условии, что запрос никогда не был полностью передан на целевой сервер. - person Christian Esken; 01.08.2014

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

Если вы используете Android httpclient по умолчанию, вы можете рассмотреть альтернативы, которые соответствуют более новым версиям клиента 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(); 

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

в runnable, где у вас есть '..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()));
}

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

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

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

person Robert Rowntree    schedule 10.07.2014

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

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

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

person Gladwin Burboz    schedule 14.07.2014

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

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();
}
person Vaishali Sutariya    schedule 17.07.2014