2011-05-31 48 views
2

我的Android应用程序使用HttpURLConnection对象从我们的服务器请求信息。服务器返回一些文本 - 根据请求可能会有一些(〜50个字符)或很多(12 MB)。 MIME类型是文本/纯文本。HttpURLConnection getInputStream()有时包含响应标头

在我最初的实现中,我很失望地发现getInputStream()从服务器返回整个HTTP响应 - 包括头文件 - 即使文档说它只包含响应主体。哦,好吧......我实现了一个小状态机来读取和存储头文件,并在我的代码中提供接口以允许调用者访问头字段。从AsyncTask中调用它来关闭UI线程并获得简单的进度跟踪方法。一切都好。

我最近添加了另一个功能,要求从服务器的信息,所以我用我的同一个“http连接包装”对象,它希望看到正文流中的标题。这一次,使用Thread对象将下载从UI线程中推出很方便。 (我并不需要进度跟踪,具有讽刺意味的是,Thread比“便利对象”AsyncTask更容易实现。)有趣的是,当从一个Thread对象调用getInputStream()流时,我没有看到头。

唯一的区别是,在一种情况下,我从AsyncTask派生的对象中发出请求,另一种情况是我从Thread派生的对象中发出请求。

当我使用来自AsyncTask的HttpUrlConnection时,我在输入流中看到标题。当我使用线程中的HttpUrlConnection时,我只看到响应主体。根据文件,后者是“正确的”行为。

我试过切换方法。也就是说,我在一个线程而不是一个AsyncTask中创建我的第一个请求。如果我这样做,我没有看到标题。当我在AsyncTask而不是一个线程中发出第二个请求时,我会在输入流中看到只有正文需要的头文件。所以这个问题似乎与我是在一个AsyncTask还是在一个线程中有关。

很明显,我可以选择一种或其他方法让我的HTTP活动脱离UI线程,但我想了解发生了什么,所以我可以使其行为相同,无论它如何调用。我喜欢使用AsyncTask,因为它具有简单的内置进度追踪机制,但这是不正确的方法。

想法?建议?

更多信息

我已经封装在一个小功能,看起来像这样(实际的代码必须将其传送接收到的字符串委托我HttpURLConnection的访问,但我已经简化它的测试):

/** 
* Reads the data at the URL. 
* @param urlString The URL to read 
*/ 
public void Get(
    String urlString) 
    { 
    URL url; 
    BufferedInputStream in; 
    try 
     { 
     url = new URL(urlString); 
     conn = (HttpURLConnection) url.openConnection(); 
     conn.setRequestMethod("GET"); 
     conn.setRequestProperty("Accept", "text/*"); 
     in = new BufferedInputStream(conn.getInputStream(), 8192); 

     byte [] buffer = new byte[8192]; 
     StringBuilder stringBuffer = new StringBuilder(16384); 

     int read; 
     while ((read = in.read(buffer)) != -1) 
      { 
      String tempstr = new String(buffer, 0, read, "ISO-8859-1"); 
      System.out.println(tempstr); 
      } 
     return true; 
     } 
    catch (IOException e) 
     { 
     return false; 
     } 

我从两个地方调用这个函数。为了测试,我有两个地方要求从我们的服务器的同一页。首先是一个扩展AsyncTask的对象。在doInBackground()中,我实例化包含Get()的对象,然后调用Get()。其次是扩展Thread的对象,其中我实例化包含Get()的对象,然后在run()中调用Get()。在这两种情况下,我都请求相同的页面。

结果是当我从我的AsyncTask调用时,我看到所有的响应标题。我实际看到的是原始HTTP - 包括传输编码“分块”时的块长度。当我从我的线程调用时,我只能得到HTML的正文,这正是我期望的。

为了测试,我在服务器上写了一个脚本,它返回原始请求。这是我得到(注意 “awebsite” 是不是我的服务器的名称):

在一个线程在运行时:

Connection: Keep-Alive 
Accept: text/* 
Host: www.awebsite.com 
User-Agent: Java0 

当在的AsyncTask运行:

[CRLF] 
HTTP/1.1 200 OK 
Date: Tue, 31 May 2011 23:29:20 GMT 
Server: Microsoft-IIS/6.0 
X-Powered-By: ASP.NET 
Content-Type: text/html 
Set-Cookie: ASPSESSIONIDCQSTTQAQ=OHBIFGJCPEAAHGNHICPKOKFO; path=/ 
Cache-control: private 
Transfer-Encoding: chunked 
[CRLF] 
53 
Connection: Keep-Alive 
Accept: text/* 
Host: www.awebsite.com 
User-Agent: Java0 
[CRLF] 
0 
[CRLF] 

注意这两种情况似乎都发出了同样的要求。然而,在我从AsyncTask中调用Get()的情况下,某些东西会欺骗HttpURLConnection,使其与正文一起给出响应标头。

若要进一步混淆的东西,访问www.google.com按预期工作 - 我从来没有看到头与身体混合。所以这似乎表明服务器端有问题(这让我想知道服务器如何知道失败),或者有一些关于服务器响应的问题,它会在AsyncTask中运行时混淆HttpURLConnection。

+0

我面对同样的情况。我发送Thread和SOMETIMES的请求,所有头文件都在getInputStream()中。也在寻找解决方法。 – Fedor 2011-06-29 01:41:28

+0

@Chirag,你能告诉我为什么要使用标题。我必须从android验证一些凭据我在xammp上使用php。我该如何去做。因为我不知道如何编写带有标题的php代码 – 2015-02-17 08:04:41

回答

3

我希望在你的服务器端生成一个错误:也许有一个响应头有几个嵌入的换行符,并且http解析器将它作为头的结尾。

+0

服务器如何知道我是从一个线程还是一个AsyncTask请求页面?无论哪种情况,数据都是一样的(好,坏,畸形,不管)。 – Craig 2011-05-31 15:02:16

+0

我觉得费米是对的。在这两种情况下,你确定要求服务器提供同样的东西吗?我从来没有遇到过这样的问题,即响应包含您所描述的标题。尝试请求公共领域的东西,看看你是否也从他们那里得到标题(使用微软,谷歌,雅虎,苹果......尝试所有:)) – WarrenFaith 2011-05-31 15:33:57

+0

嗯,服务器无法知道你是否在一个线程或一个AsyncTask中,所以你必须要求不同的东西,或者不正确地使用流水线或Keepalive之类的东西。粘贴您正在使用的2个代码块。 – Femi 2011-05-31 15:58:32

0
  1. 检查conn.getResponseCode()。如果它是-1,你不应该尝试读取流。
  2. 如果您在2.1或更低版本,请执行以下操作:System.setProperty(“http.keepAlive”,“false”);
    此处更多详细信息HttpURLConnection.getResponseCode() returns -1 on second invocation
+0

的PHP代码,请告诉我为什么要使用标题。我必须从android验证一些凭据我在xammp上使用php。我该如何去做。因为我不知道如何编写带有头文件的php代码 – 2015-02-17 08:05:17