我在崩溃日志中看到这个错误每周数百次,但我已经花了几个星期的时间试图追赶错误而没有任何成功。我无法在任何设备上重现它。这是堆栈跟踪:
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;
}
非常感谢任何有关最终解决这个问题的帮助!
答案 0 :(得分:10)
在我看来,这个问题的罪魁祸首不是你的应用程序,而是远程端(即HTTP服务器)。最可能的事情是HTTP服务器突然重置连接,这会导致应用程序中出现SocketException
。在生产环境中,这些事情经常发生。它可能是由HTTP服务器的过载引起的,一些特殊情况可能导致服务器关闭(HTTP请求泛滥,甚至在远程服务器资源耗尽时增加的请求数量;服务器也可能耗尽它的本地套接字池...原因可能是几十个。
如果这些错误的比例与成功的HTTP请求相比较低,我不会太担心,我只是将这段代码包装成try { ... } catch (SocketException e) { ... }
语句并显示给用户一个对话框告诉他们请求失败,他们应该重试。
我当然会尝试确定这种行为的原因:我尝试匹配其中一个异常的时间,并尝试深入了解HTTP服务器日志,以便尝试确定这种突然断开的原因(假设您可以访问该日志和其他诊断工具)。正如我之前所说的那样,调试可能是一件愚蠢的事情或者更复杂,但我敢打赌这就是问题。
答案 1 :(得分:5)
“java.net.SocketException:Socket closed”异常可能在各种情况下发生。服务器端关闭连接,如nKn建议,或客户端(您的应用程序)关闭连接。即使您不知道这样做,也可能会有一些不太明显的代码可能导致关闭套接字,如Thread.interrupt()或ExecutorService.shutdownNow()。
另一方面,如果它实际上发生在服务器端,我会建议你实施重试 - 通常做3次尝试并且通常就足够了。
答案 2 :(得分:1)
您目前正在接受客户端lib配置的默认值。也许您想要更多地控制您的Httpclient lib,特别是关于套接字TIMEOUT设置。不要等待服务器做出意想不到的事情。设置比默认值更短的超时,并以对用户有意义的方式控制错误&#34;稍后再尝试msg&#34; ....
如果您正在使用默认的android httpclient,您可能需要查看与新的Apache客户端版本保持同步的替代方案......
https://hc.apache.org/httpcomponents-client-4.3.x/android-port.html
https://code.google.com/p/httpclientandroidlib/
general background async client
并注意,使用其中任何一个,您都可以考虑(在Wifi上)或(在4G上)您可以拨打detailed timeout profiles,您可以使用以下代码控制超时:
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回调,以便您可以显示所需的任何警告对话框
在runnable中您拥有&#39; .. client.exec(请求$ Type)&#39;
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。
答案 3 :(得分:1)
您正在使用方法返回的HttpPost对象做什么 发布数据(...) ?这可能是需要考虑的原因之一。请注意那里 是两个线程,一个主线程和另一个产生的线程 上方。
在finally块中,您需要关闭httpClient和
之类的资源
响应以及完全清空响应输入流。参见示例以供参考。
在UrlEncodedFormEntity上明确设置charset,可能是UTF-8
答案 4 :(得分:0)
尝试使用此
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();
}