一个实时反馈上传速度的小程序的bug


点击(此处)折叠或打开

  1. protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  2.         // TODO Auto-generated method stub
  3.         int LENGTH=10240;
  4.         InputStream is=request.getInputStream();
  5.         String fn=UUID.randomUUID().toString();
  6.         File a=new File(HlsConfigure.getPath()+fn);
  7.         byte []content=new byte[LENGTH];
  8.         int length;
  9.         OutputStream os =new FileOutputStream(a);
  10.          PrintWriter out = response.getWriter();
  11.          out.write("<html><body><table><tr><td>bytes</td><td>speed KB/s</td></tr>");
  12.          long total=0;
  13.          long vb=System.currentTimeMillis();
  14. again:
  15.         while(true){
  16.             int total_length=0;
  17.             long begin=System.currentTimeMillis();            
  18.             while((length=is.read(content))!=-1){
  19.                 os.write(content, 0, length);
  20.                 System.out.println("get "+length);
  21.                 total_length+=length;
  22.                 if(total_length>=200*1024){
  23.                     break;
  24.                 }                
  25.             }
  26.             System.out.println("will write speed");
  27.             long time=System.currentTimeMillis()-begin;
  28.             out.write("<tr><td>");
  29.             out.write(Integer.toString(total_length));
  30.             out.write("</td><td>");
  31.             out.write(Double.toString(total_length/(1.0240)/(time)));                
  32.             out.write("</td></tr>");
  33.             total+=total_length;
  34.             out.flush();
  35.             if(length==-1)
  36.                 break;
  37.         }
  38.          os.close();    
  39.          long time=System.currentTimeMillis()-vb;
  40.             out.write("<tr><td>");
  41.             out.write(Long.toString(total));
  42.             out.write("</td><td>");
  43.             String speed=Double.toString(total/(1.0240)/(time));
  44.             out.write(speed);                
  45.             out.write("</td></tr>");
  46.             out.flush();
  47.          out.write("</table></body></html>");
  48.          System.out.println("from "+request.getRemoteAddr()+ " speed is "+speed);
  49.         // a.delete();
  50.     }

代码很简单:
1. 打开inputstream,读流,并写到文件中去;
2. 每200k反馈上传速度;
3. 客户段用最简单的input type=‘file‘上传文件;

但是当客户上传一个很大的文件时,不能传完就hang了。
最终分析到原因为:
1. 浏览器在没有完成发送请求时,是不会读取服务器输出的。所以这个实时显示上传速度的想法不对; 
2. 从代码执行逻辑看,客户段上传,服务段收数据,不会导致服务器缓冲区满;
3. 但是由于1的原因,服务器返回给客户段的数据不会被从缓冲区读取。所以客户段的缓冲去会满;
4. 从而导致28行的write写不出去;
5. 由于此时服务器堵在了write处,所以服务器也不会再读取数据了。
6. 服务器的缓冲区满了,所以客户的上传就hang住了。

注意:
ACK报文是tcp协议栈实现的。所以无论程序是否读取缓冲区的数据,ACK是不会受到影响的。如果不都数据window值会变小; 这个标记可以用来指示程序是不是没有读数据;

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。