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属性值。最后的 这种方式没有尝试过。