分享

HTTP/1.1组块(chunked)传输编码实验

 t涂鸦 2012-05-16
HTTP是如何确保参与各方(通常是浏览器和Web服务器)认识到它们已经接收到了完整的消息?
 
       实体的长度是一个重要的指示符,接收方据此可知道何时收到了完整的实体。HTTP/1.0可用来指定实体长度的唯一机制是通过Content-Length字段。静态资源的长度可以很容易地确定;但是对于动态生成的响应来说(比如在 phpMyAdmin中导出数据库的操作,数据的大小是事先不能确定的),为获取它的真实长度,只能等它完全生成之后,才能正确地填写Content-Length的值,这便要求缓存整个响应,从而增大了最终用户的延迟。在HTTP/1.0中,服务器可以通过关闭连接来指示动态内容的结尾,如果关闭连接是指示动态响应结尾的唯一办法,那么HTTP/1.1的持久连接便不可能实现了(TCP链接复用、持久化连接是HTTP/1.1的一个重大改进,它可以提高带宽的利用率)。
      为了解决这个问题,HTTP/1.1引入了被称为组块(chunked)的传输编码方法。该方法使发送方能将消息实体分割为任意大小的组块(chunk),并单独地发送他们。在每个组块前面,都加上了该组块的长度,使接收方可确保自己能够完整地接收到这个组块。更重要的是,在最末尾的地方,发送方生成了长度为零的组块。接收方可据此判断整条消息都已安全地传输完毕。这样也避免了在服务器端占用大量的缓存。Transfer-Encoding标头(值为chunked)向接收方指出:响应将被分组块,对响应分析时,应采取不同于非分组块的影响方式。
 
上面是我对,Web协议与实践7.6节“消息传输”的概括。
 
下面是两个实验:
测试环境: Apache + PHP。
测试脚本如下:
这段代码的功能是限速下载,它是从http://www./snippets/php/dl-speed-limit.htm获取的。
它读取服务器的一个文件,然后动态地输出该文件的数据(每隔1S输出一定长度的数据)。
  1. <?php  
  2. set_time_limit(0);  
  3.   
  4. // send the contents of the topmost output buffer (if any) and turn this output buffer off  
  5. ob_end_flush();  
  6.   
  7.   
  8. // local file that should be send to the client  
  9. $local_file = 'test.bin';  
  10.    
  11. // filename that the user gets as default  
  12. $download_file = $local_file;  
  13.    
  14. // set the download rate limit (=--> 10 KB/s)  
  15. $download_rate = 10;  
  16.    
  17. if(file_exists($local_file) && is_file($local_file)) {  
  18.    
  19.     // send headers  
  20.     header('Cache-control: private');  
  21.     header('Content-Type: application/octet-stream');  
  22.     header('Content-Length: '.filesize($local_file));  
  23.     header('Content-Disposition: filename='.$download_file);  
  24.   
  25.    // flush headers first  
  26.    flush();  
  27.    
  28.     // open file stream  
  29.     $file = fopen($local_file"r");  
  30.    
  31.     while (!feof($file)) {  
  32.    
  33.         // send the current file part to the browser  
  34.         print fread($fileround($download_rate * 1024));  
  35.    
  36.         // sleep one second  
  37.         sleep(1);  
  38.     }  
  39.    
  40.     // close file stream  
  41.     fclose($file);  
  42.    
  43. }  
  44. else {  
  45.     die('Error: The file '.$local_file.' does not exist!');  
  46. }  
  47. ?>   
ob_end_flush()将确保脚本执行过程中不使用缓存,这意味着任何输出的数据都将立即被发送到客户端。
 
1)带Content-Length的响应。
如上述代码,在应答头中我们手动添加了Content-Length字段(该值由文件的实际大小决定)。
在浏览器(Firefox)中访问该脚本,立即弹出下载对话框,应答头如下图所示。
 
 
我们可以看到应答头中包含了Content-Length标头字段。
下载进度如下图所示,FireFox下载管理器之所以能够准确地显示出下载进度就是因为Content-Length字段已经指示了文件的总大小。
 
 
   
 2)不带Content-Length的响应。
将上述代码中的header('Content-Length: '.filesize($local_file));语句注释掉。
在浏览器(Firefox)中访问该脚本,立即弹出下载对话框,应答头如下图所示。
 
 
 在应答头中我们可以看到Transfer-Encoding: chunked,说明这次服务器使用了组块式的应答。
看一下载管理器,如下图所示。由于此时不确定文件的总大小,因此它不能准确得显示出下载进度。
 
 
 
 特别说明:经过测试发现“IIS + ISAPI PHP”不支持关闭程序执行过程中的缓存(flush(), ob_implicit_flush(true), ob_end_flush()均不起作用)。注意本文所说的缓存都指的是PHP解释器在执行PHP代码过程中使用的内存缓存区(变量),而不是服务器或客户端的文件缓存。
所以如果在“IIS + ISAPI PHP”环境下做实验二,服务器总是会将数据先放到缓存中直到程序执行完毕,得到Content-Length值后,发送非chunked的应答。
 
  1. <?php  
  2. ob_end_flush();  
  3.   
  4. for ($i=0; $i<5; $i++) {  
  5.    echo $i.'<br>';  
  6.    sleep(1);  
  7. }  
  8. ?>   

上述代码在Apache服务器中部署,在浏览器中访问看到的情况会是0到4的数字每隔1S输出一个,但是在“IIS + ISAPI PHP”环境下始终是5个数字一起输出的。

同时我看到了有网友表示在"Nginx + Fast CGI PHP"环境下,PHP的程序缓存控制也会失败,看这里

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多