網絡流中 InputStream.available() = 0 問題探究


在處理文件輸入流時,通過調用available()方法來獲取還有多少字節可以讀取,根據該數值創建固定大小的byte數組,從而讀取輸入流的信息。

  FileInputStream fi = new FileInputStream("C:/Users/Administrator/Desktop/yy.txt");
    //1. read() 逐字節讀取
/*  int i = 0;
    byte[] bytes = new byte[fi.available()];
    while(fi.available() > 0){
        bytes[i] = (byte) fi.read();
        i++;
    }*/

    //2. read(byte b[]) 一次讀取
    byte[] bytes = new byte[fi.available()];
    fi.read(bytes);
fi.close(); System.out.println(Arrays.toString(bytes));

但是在處理網絡流(socket)時,通過available()方法對輸入流進行長度判斷,數值為0,這意味着對方發送的流中無數據,但實際上並非如此。

原因在於:

網絡通訊往往是間斷性的,一串字節往往分幾批進行發送。例如對方發來字節長度100的數據,本地程序調用available()方法有時得到0,有時得到50,有時能得到100,大多數情況下是0。這可能是對方還沒有響應,也可能是對方已經響應了,但是數據還沒有送達本地。也許分3批到達,也許分兩批,也許一次性到達。

詳細解釋參考OSI網絡7層結構:

我們進行的數據接收只是基於應用層,網絡傳輸的最上層,數據從一端到另一端傳輸的時候,會在傳輸層分解成適合的數據包。傳輸層(Transport Layer)是OSI 模型中最重要的一層。傳輸協議同時進行流量控制或是基於接收方可接收數據的快慢程度規定適當的發送速率。除此之外,傳輸層按照網絡能處理的最大尺寸將較長的數據包進行強制分割。例如,以太網無法接收大於1500 字節的數據包。發送方節點的傳輸層將數據分割成較小的數據片,同時對每一數據片安排一序列號,以便數據到達接收方節點的傳輸層時,能以正確的順序重組。該過程即被稱為排序。工作在傳輸層的一種服務是 TCP/IP 協議套中的TCP(傳輸控制協議),另一項傳輸層服務是 IPX/SPX 協議集的 SPX(序列包交換)。

InputStream的available()方法的作用是返回此輸入流在不受阻塞情況下能讀取的字節數。網絡流與文件流不同的關鍵就在於是否“受阻”二字,網絡socket流在讀取時如果沒有內容read()方法是會受阻的,所以從socket初始化的輸入流的available也是為零的,所以要read一字節后再使用,這樣可用的字節數就等於 available + 1。但文件讀取時read()一般是不會受阻的,因為文件流的可用字節數 available = file.length(),而文件的內容長度在創建File對象時就已知了。 

所以調用網絡流(socket)的available()方法前,一定記得要先調用read()方法,這樣才能避免獲取為0的不正確情況。

   //將接收到的數據存到字節數組bytes
    int firstByte = inputStream.read();
    int length = inputStream.available();
    byte[] bytes = new byte[length+1];
    bytes[0] = (byte)firstByte;
    inputStream.read(bytes,1,length);

另外,在文件上載和表單提交的過程中,可以使用 request.getContentLength()方法代替InputStream.available()方法,通過調用 request.getContentLength() 得到 Content-Length ,並定義一個與 Content-Length 大小相等的字節數組 buffer,從HttpServletRequest 的實例 request 中得到一個 InputStream, 並把它讀入 buffer 中。然后使用 FileOutputStream 將 buffer 寫入指定文件。

// ReceiveServlet.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
//示例程序:記錄下Form提交上來的數據,並存儲到Log文件中
public class  ReceiveServlet extends HttpServlet
{
    public void doPost(HttpServletRequest request,HttpServletResponse response)
    throws IOException, ServletException
    {
//1
        int len = request.getContentLength();
        byte buffer[] = new byte[len];
//2
        InputStream in = request.getInputStream();
        int total = 0;
        int once = 0;
        while ((total < len) && (once >=0)) {
            once = in.read(buffer,total,len);
            total += once;
        }
//3
        OutputStream out=new BufferedOutputStream(
            new FileOutputStream("Receive.log",true));
        byte[] breaker="\r\nNewLog: -------------------->\r\n".getBytes();
        System.out.println(request.getContentType());
        out.write(breaker,0,breaker.length);
        out.write(buffer);
        out.close();
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM