Android 错误:java.net.SocketException:套接字已关闭

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24620953/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 13:30:58  来源:igfitidea点击:

Android Error: java.net.SocketException: Socket closed

javaandroidandroid-asynctaskandroid-studioandroidhttpclient

提问by D-Nice

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
 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;
    }
0" 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.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);:

这是错误来自的代码块......崩溃发生的确切位置是HttpResponse response = httpclient.execute(httppost);

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

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

非常感谢最终解决这个问题的任何帮助!

回答by nKn

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 SocketExceptionin 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).

在我看来,这个问题的罪魁祸首不是你的应用程序,而是远程端(即 HTTP 服务器)。最有可能的事情是 HTTP 服务器突然重置连接,这会导致SocketException您的应用程序出现错误。在生产环境中,这些事情经常发生。这可能是由于 HTTP 服务器过载,一些可能使服务器关闭的异常情况(HTTP 请求泛滥,甚至当远程服务器资源耗尽时请求数量增加;服务器也可能耗尽它的本地套接字池......原因可能有几十个)。

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.

如果这些错误的比例与成功的 HTTP 请求相比较低,我不会太担心,我只是将那段代码包装成一个try { ... } catch (SocketException e) { ... }语句并向用户显示一个对话框,告诉他们请求已失败,他们应该重试。

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.

我当然会做的是尝试确定这种行为的原因:我会尝试匹配这些异常之一的时间,并尝试深入研究几乎到那个时间的 HTTP 服务器日志,以确定这种突然发生的原因断开连接(假设您可以访问该日志和其他诊断工具)。正如我之前所说,调试可能是一件愚蠢的事情或者更复杂一些,但我敢打赌这就是问题所在。

回答by Robert Rowntree

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"....

您当前正在接受客户端库配置的任何默认值。也许你想更多地控制你的 Httpclient 库,特别是关于套接字超时设置。不要等待服务器做一些意想不到的事情。设置一个比默认值更短的超时时间,并以一种对您的用户有意义的方式控制错误“稍后再试消息”....

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

如果您使用的是默认的 android httpclient,您可能需要查看与较新的 Apache 客户端版本保持同步的替代方案...

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

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

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

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 profileswhere you control the timeouts with code like below:

并注意,使用这些中的任何一个,您都可以考虑(在 Wifi 上)或(在 4G 上)您可以拨入详细的超时配置文件,您可以使用如下代码控制超时:

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

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

使用处理程序和回调到 UI,以便您可以显示任何您想要的警报对话框

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

在 runnable 中,您拥有 '..client.exec(request$Type)'

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

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

回到 UI 线程,您可以根据需要控制尽可能多的差异处理程序的警报....

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

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.

如有必要,您可以按域建立差异超时配置文件。学习其他 2 个 httpclient 包的所有构建器内容和配置内容需要时间,但是,这可能是值得的,因为您可以将其设置为做任何您想做的事情,并且您可以完全控制异常和您想要做的事情返回到 UI。

回答by Gladwin Burboz

  • 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

  • 你对从方法 postData(...) 返回的 HttpPost 对象做了什么?这可能是要考虑的原因之一。请注意,有两个线程,一个主线程和另一个在上面生成的线程。

  • 在 finally 块中,您需要关闭 httpClient 和
    响应等资源,并完全清空响应输入流。请参阅示例以供参考。

  • 在 UrlEncodedFormEntity 上显式设置字符集,可能是 UTF-8

回答by Christian Esken

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

“java.net.SocketException: Socket closed”异常可能在各种情况下发生。服务器端像 nKn 建议的那样关闭了连接,或者客户端(您的应用程序)关闭了连接。即使您不知道这样做,也可能有一些不太明显的代码可能会导致关闭套接字,例如 Thread.interrupt() 或 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.

另一方面,如果它确实发生在服务器端,我建议您实现重试 - 3 次尝试是常见的做法,通常就足够了。

回答by Vaishali Sutariya

try to use this

尝试使用这个

##代码##