当前位置: 主页 > 日志 > 其它 >

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

拿到了盗版的 Web协议与实践(HTTP1.1、网络协议、缓存技术和流量检测)。这本书的正版现在已经买不到了,京东上有这本书,但是也是无货,标价竟然只有33.8元,说实话这个价格连成本都不够。

 
后来花了90块钱从淘宝买了盗版,书的印刷质量真的不咋样,很多地方都很模糊。不过的确是好书,这是我读过的第一本把HTTP协议讲的很透彻的书(这方面的书本身就不过,之前我看的最多的就是RFC2616)。
PDF版我也拿到了,下载地址:http://www.everbox.com/f/1sGIgc8fWlkblyVGB38pkGc5l9
 
进入正题。
 
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.jonasjohn.de/snippets/php/dl-speed-limit.htm获取的。
它读取服务器的一个文件,然后动态地输出该文件的数据(每隔1S输出一定长度的数据)。
<?php
set_time_limit(0);

// send the contents of the topmost output buffer (if any) and turn this output buffer off
ob_end_flush();


// local file that should be send to the client
$local_file = 'test.bin';
 
// filename that the user gets as default
$download_file = $local_file;
 
// set the download rate limit (=--> 10 KB/s)
$download_rate = 10;
 
if(file_exists($local_file) && is_file($local_file)) {
 
    // send headers
    header('Cache-control: private');
    header('Content-Type: application/octet-stream');
    header('Content-Length: '.filesize($local_file));
    header('Content-Disposition: filename='.$download_file);

   // flush headers first
   flush();
 
    // open file stream
    $file = fopen($local_file, "r");
 
    while (!feof($file)) {
 
        // send the current file part to the browser
        print fread($file, round($download_rate * 1024));
 
        // sleep one second
        sleep(1);
    }
 
    // close file stream
    fclose($file);
 
}
else {
    die('Error: The file '.$local_file.' does not exist!');
}
?> 
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的应答。
 
<?php
ob_end_flush();

for ($i=0; $i<5; $i++) {
   echo $i.'
'; sleep(1); } ?>

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

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

[日志信息]

该日志于 2011-12-23 20:17 由 redice 发表在 redice's Blog ,你除了可以发表评论外,还可以转载 “HTTP/1.1组块(chunked)传输编码实验” 日志到你的网站或博客,但是请保留源地址及作者信息,谢谢!!    (尊重他人劳动,你我共同努力)
   
验证(必填):   点击我更换验证码

redice's Blog  is powered by DedeCms |  Theme by Monkeii.Lee |  网站地图 |  本服务器由西安鲲之鹏网络信息技术有限公司友情提供

返回顶部