zoukankan      html  css  js  c++  java
  • 网络流中 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="
    NewLog: -------------------->
    ".getBytes();
            System.out.println(request.getContentType());
            out.write(breaker,0,breaker.length);
            out.write(buffer);
            out.close();
        }
    }
  • 相关阅读:
    自执行函数的几种不同写法的比较
    Textarea与懒惰渲染
    备忘:递归callee.caller导致死循环
    围观STK
    某台机器上IE8抛“Invalid procedure call or argument”异常
    QWrap Selector之W3C版
    onclick与listeners的执行先后问题
    随机问题之洗牌算法
    selector4 之 巧妙的主体定义符
    神奇的"javascript:"
  • 原文地址:https://www.cnblogs.com/zjfjava/p/10829241.html
Copyright © 2011-2022 走看看