2012-01-19 40 views
1

我爲Java中的HTTP消息編寫了一個標記器。它有一個方法nextToken(),它應該返回一個包含收到的整個HTTP消息的字符串。問題是消息在預期的主體大小被讀取之前結束。爲什麼我的HTTP消息在正文大小達到Content-Length頭中規定的大小之前結束?

我將輸入流一直讀到主體的開頭。然後,我嘗試從流中讀取n字節,其中n是在Content-Length報頭中聲明的主體的字節大小。問題在於在while循環中,行charsRead = in.read(buffer)因爲輸入流中沒有更多輸入而被阻塞。但它發生在讀取之前字節。

示例:對於大小爲12,493的主體,當需要讀取更多675個字節時會阻塞。

輸入流使用UTF-8,因此每個字節都編碼爲一個char

/* Somewhere else in the code: 
InputStreamReader _isr = 
    new InputStreamReader(clientSocket.getInputStream(), "UTF-8") 
*/ 
BufferedReader in = new BufferedReader(_isr); 
StringBuilder tmp = new StringBuilder(); 
String line = ""; 
boolean body = false; 
int bodylen = -1; 

for (;;) { 
    line = in.readLine(); 

    if (line == null) 
     break; 
    if (line.equals("")) { /* We've reached the body */ 
     body = true; 
     break; 
    } 

    tmp.append(line + "\r\n"); 

    if ((bodylen == -1) && (line.contains("Content-Length:"))) { 
     /* Make `bodylen` hold the length of the body */ 
     String[] splitted = line.split("Content-Length:"); 
     bodylen = Integer.parseInt(splitted[1].trim()); 
    } 
} 

if (body == true) { 
    int charsRead; 
    char[] buffer = new char[1024]; 

    while (bodylen > 0) { 
     charsRead = in.read(buffer); 
     if (charsRead == -1) 
      break; 
     bodylen -= charsRead; 
     tmp.append(buffer); 
    } 
} 

爲什麼會發生,以及如何解決它?

回答

3

看來你是混淆字符與字節。內容長度是以字節爲單位的,但是您要計數字符。

+0

因爲它是用UTF-8編碼的,所以每個字節都被編碼爲一個「char」。我也用調試器檢查過它。 – 2012-01-19 00:48:20

+0

此外,如果不是這種情況,那麼緩衝區讀數將會完全停止。 – 2012-01-19 00:50:19

+0

Julian正確地注意到UTF-8字符可能需要多達6個字節。 – EricLaw 2012-01-19 01:44:42

2

您正在使用錯誤的read()方法。您應該使用read(byte[], int start, int len)方法。

下面是你應該如何讀書的樣本幫手:

private void readAll(InputStream is, byte[] buffer){ 
    int read = 0; 
    while (read != buffer.length){ 
     int ret = in.read(buffer, read, buffer.length - read); 
     if (ret == -1) return; 
     read += ret; 
    } 
} 

你在你的代碼做什麼你問的API來讀取每次調用讀取時間爲1024字節。發生什麼事是基礎InputStream只能讀取675個字節(這是一個網絡調用,所以這是可以預期的),在您通過循環的下一次迭代中,您要求API再次讀取1024個字節。 API讀取剩餘的(1024-675字節)和塊,直到它填滿整個緩衝區,它永遠不會發生這種情況,導致您將讀取操作拆分爲2次調用(您的代碼也會覆蓋之前的讀取,因爲它們都從0開始)。

這是處理網絡資料時非常正常的行爲,人們很喜歡用它來處理文件,當他們無法完全準備好緩衝區長度時,他們發現很奇怪。

+0

你錯了。你寫道:「...並阻塞,直到它填充整個緩衝區」 - 這是不正確的。根據Java,read方法阻塞,直到輸入流上出現一些輸入,直到緩衝區滿。 – 2012-01-19 09:31:23

+0

Leif,重新讀取API。 read(buffer)== read(buffer,0,buffer.length)== while(int char = read()!= -1)。如果可以的話,api會嘗試讀取整個緩衝區,或者在流結束時停止。 「直到輸入出現塊」是read()API的繼承。 – user931366 2012-01-19 19:32:09

相關問題