web-dev-qa-db-ja.com

Android Error:Java.net.SocketException:Socket closed

このエラーがクラッシュログで週に何百回も発生するのを目にしていますが、この時点で何週間も費やしてエラーを追跡しようとしましたが、成功しませんでした。どのデバイスでも再現できません。スタックトレースは次のとおりです。

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

これを最終的に理解する上での助けは大歓迎です!

13
D-Nice

私の意見では、この問題の原因はアプリではなく、リモート側(つまり、HTTPサーバー)です。最も可能性が高いのは、HTTPサーバーが突然接続をリセットしているため、アプリにSocketExceptionが発生していることです。本番環境では、これらのことが頻繁に発生します。これは、HTTPサーバーの過負荷、サーバーを閉じる可能性のある例外的な状況(HTTPリクエストのフラッド、またはリモートサーバーのリソースが不足しているときにリクエスト数が増加すること)によって引き起こされる可能性があります。サーバーも不足する可能性があります。そのローカルソケットプール...理由は数十かもしれません)。

成功したHTTPリクエストと比較してこれらのエラーの割合が低い場合、私は心配する必要はありません。そのコードをtry { ... } catch (SocketException e) { ... }ステートメントにラップして、ユーザーに次のようなダイアログを表示します。リクエストが失敗したため、再試行する必要があります。

私が間違いなく行うことは、この動作の理由を特定することです。これらの例外のいずれかの時間を一致させ、HTTPサーバーのログをその時間に近い時間で調べて、この突然の原因を特定しようとします切断(そのログやその他の診断ツールにアクセスできる場合)。前に言ったように、ばかげたことか、デバッグが少し複雑かもしれませんが、これが問題だと思います。

12
nKn

「Java.net.SocketException:Socket closed」例外は、さまざまな状況で発生する可能性があります。サーバー側が推奨されるnKnのように接続を閉じたか、クライアント側(アプリ)が接続を閉じました。意識していなくても、Thread.interrupt()やExecutorService.shutdownNow()のように、ソケットのクローズにつながる可能性のある明らかでないコードが存在する場合があります。

一方、実際にサーバー側で発生する場合は、再試行を実装することをお勧めします。3回の試行が一般的な方法であり、通常は十分です。

5
Christian Esken
  • メソッドpostData(...)から返されたHttpPostオブジェクトをどのように処理していますか?それは考慮すべき原因の1つかもしれません。 2つのスレッドがあることに注意してください。1つはメインスレッド、もう1つは上で生成したスレッドです。

  • 最後のブロックでは、httpClientや
    応答および応答入力ストリームを完全に空にします。参考例を参照してください。

  • UrlEncodedFormEntityの文字セットを明示的に設定し、UTF-8にすることができます

1
Gladwin Burboz

現在、クライアントライブラリが構成されているデフォルトを受け入れています。特にソケットのTIMEOUT設定に関して、Httpclient libをより詳細に制御したいと思うかもしれません。サーバーが予期しない何かを行うのを待たないでください。デフォルトよりも短いタイムアウトを設定し、ユーザーにとって意味のある方法でエラーを制御します。「後でメッセージを試す」...

あなたがデフォルトを使用している場合Android httpclientあなたはより新しいApacheクライアントリリースに追いつく代替案を見たいと思うかもしれません...

https://hc.Apache.org/httpcomponents-client-4.3.x/Android-port.html

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

一般的なバックグラウンド非同期クライアント

これらのいずれかを考慮に入れることができることに注意してください(Wifiの場合)OR(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(); 

ハンドラとUIへのコールバックを使用して、必要なアラートダイアログを表示できます。

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

uIスレッドに戻って、必要な数のdiffハンドラーのアラートを制御します。

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

必要に応じて、ドメインごとに差分タイムアウトプロファイルを設定できます。これらの他の2つのhttpclientパッケージですべてのビルダーのものと構成のものを学ぶには時間がかかりますが、あなたが望むことを何でもできるように設定でき、例外とあなたが望むものを完全に制御できるので、それは時間の価値があります。 UIに戻ります。

1
Robert Rowntree

これを使ってみてください

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