HTTP/1.1组块(chunked)传输编码实验_redice's Blog
HTTP/1.1组块(chunked)传输编码实验
日期:2011-12-23 |
来源:未知 |
作者:redice |
274 人围观 |
0 人鼓掌了!
拿到了盗版的 Web协议与实践(HTTP1.1、网络协议、缓存技术和流量检测)。这本书的正版现在已经买不到了,京东上有这本书,但是也是无货,标价竟然只有33.8元,说实话这个价格连成本都不够。
后来花了90块钱从淘宝买了盗版,书的印刷质量真的不咋样,很多地方都很模糊。不过的确是好书,这是我读过的第一本把HTTP协议讲的很透彻的书(这方面的书本身就不过,之前我看的最多的就是RFC2616)。PDF版我也拿到了,想要的发邮件吧。进入正题。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.'<br>';
- sleep(1);
- }
- ?>
上述代码在Apache服务器中部署,在浏览器中访问看到的情况会是0到4的数字每隔1S输出一个,但是在“IIS + ISAPI PHP”环境下始终是5个数字一起输出的。
同时我看到了有网友表示在"Nginx + Fast CGI PHP"环境下,PHP的程序缓存控制也会失败,看这里。