inputstream.available()与HttpURLConnection.getContentLength()
问题描述
先看看下面这段代码(有删节)
public static String send(String sendurl, String sendText) throws Exception {
URL url = null;
URLConnection uc = null;
//建立连接、输出数据等;
String strReceive = "";
try {
InputStream is = uc.getInputStream();
DataInputStream in = new DataInputStream(is);
// 从这里开始
int blockLen = in.available();
byte block[] = new byte[blockLen];
for (int readLen = -9999; readLen != -1;) {
readLen = in.read(block);
if (readLen != -1){
strReceive = strReceive + new String(block);
}
}
// 到这里结束
is.close();
in.close();
} catch (IOException e) {
logger.info("httpl接收错误1:" + e.getMessage());
}
return strReceive;
}
注意“从这里开始”到“到这里结束”那几行代码。这几段代码在线上环境形成了死循环。
问题分析
上面的代码中,blockLen被用来创建一个字节数组block,block作为数据缓冲来读取inputstream里的数据。然后循环 从inputstream中读取数据,写入block中。
考虑一种情况。如果网络阻塞了,inputstream已经打开,但是数据却还没有传输过来,会发生什么?
inputstream.available()方法返回的值是该inputstream在不被阻塞的情况下一次可以读取到的数据长度。如果数据还没有传输过来,那么这个inputstream势必会被阻塞,从而导致inputstream.available返回0。而对 inputstream.read(byte[] b)而言,如果b的长度等于0,该方法将返回0。
回头看看这个循环体的结束条件,是readLen == -1时跳出。显然,上面提到的网络阻塞情况发生之后,代码将陷入这个死循环当中。
这是我们在工程应用中遇到的一个问题。由外包商提供的工具jar包中的这段代码,直接将我们的服务器 拉进了死循环。
解决方案
我们的解决方法,是将整个接收与发送的方法进行改写,使用了下面的这段代码:
HttpClient client = new HttpClient();
PostMethod method = new PostMethod(prpUrl);
method.setRequestBody(sendstr);
method.getParams().setParameter(
HttpMethodParams.HTTP_CONTENT_CHARSET, "GBK");
client.executeMethod(method);
// rtnXml = method.getResponseBodyAsString();
InputStream txtis = method.getResponseBodyAsStream();
BufferedReader br = new BufferedReader(new InputStreamReader(txtis));
String tempbf;
StringBuffer html = new StringBuffer(100);
while ((tempbf = br.readLine()) != null) {
html.append(tempbf);
}
rtnXml = html.toString();
method.releaseConnection();
确确实实的,解决了问题。
如果仍然要采用原方法中手动打开输入流、建立缓冲区、循环读取数据的方法,那么不应该用 available这个字段来作为缓冲区的初始长度。可以考虑手工设定一个固定值;或者读取http报文头的content-length属性值。最后的 这种方式没有尝试过。