Transfer-Encoding: chunked

有时候,Web服务器生成HTTP Response是无法在Header就确定消息大小的,这时一般来说服务器将不会提供Content-Length的头信息,而采用Chunked编码动态的提供body内容的长度。

进行Chunked编码传输的HTTP Response会在消息头部设置:

Transfer-Encoding: chunked

表示Content Body将用Chunked编码传输内容。

Chunked编码使用若干个Chunk串连而成,由一个标明长度为0的chunk标示结束。每个Chunk分为头部和正文两部分,头部内容指定下一段正文的字符总数(十六进制的数字)和数量单位(一般不写),正文部分就是指定长度的实际内容,两部分之间用回车换行(CRLF)隔开。在最后一个长度为0的Chunk中的内容是称为footer的内容,是一些附加的Header信息(通常可以直接忽略)。具体的Chunk编码格式如下:

  Chunked-Body = *chunk
         "0" CRLF
         footer
         CRLF
  chunk = chunk-size [ chunk-ext ] CRLF
       chunk-data CRLF

  hex-no-zero = <HEX excluding "0">

  chunk-size = hex-no-zero *HEX
  chunk-ext = *( ";" chunk-ext-name [ "=" chunk-ext-value ] )
  chunk-ext-name = token
  chunk-ext-val = token | quoted-string
  chunk-data = chunk-size(OCTET)

  footer = *entity-header

RFC文档中的Chunked解码过程如下:
  length := 0
  read chunk-size, chunk-ext (if any) and CRLF
  while (chunk-size > 0) {
  read chunk-data and CRLF
  append chunk-data to entity-body
  length := length + chunk-size
  read chunk-size and CRLF
  }
  read entity-header
  while (entity-header not empty) {
  append entity-header to existing header fields
  read entity-header
  }
  Content-Length := length
  Remove "chunked" from Transfer-Encoding

最后提供一段PHP版本的chunked解码代码:

$chunk_size = (integer)hexdec(fgets( $socket_fd, 4096 ) );
while(!feof($socket_fd) && $chunk_size > 0) {
    $bodyContent .= fread( $socket_fd, $chunk_size );
    fread( $socket_fd, 2 ); // skip /r/n
    $chunk_size = (integer)hexdec(fgets( $socket_fd, 4096
) );
}

==========================================================

Server Push技术

服务器推送(Server Push)的思想是由服务器主动发送信息,并与客户端保持连接,直至服务器或客户端有一方自行中断连接为止。
  Server Push的优点在于减少了建立、销毁连接的时间,去除了无用的页面刷新,缺点是占用了大量端口和相关系统资源,单纯的Server Push无法支持大用户量的服务。
  Server Push使用了multipart/x-mixed-replace这种MIME类型,报文范例格式如下:

Content-type:multipart/x-mixed-replace;boundary=ThisRandomString
–ThisRandomString
Content-type:text/plain
第一个对象的数据
–ThisRandomString
Content-type:text/plain
第二个(最后一个)对象的数据
–ThisRandomString–

  每个数据块由三部分组成:一是Content-type之类的头标,二是数据正文,三是报文边界,每当客户端接收到新的头标时,原有文档将被清除,并被新的数据块填充。
  Apache和IIS均支持Server Push技术,笔者推荐Linux/Unix下的Apache软件,它可以自由的增删相应模块,以满足较多连接状态下的高性能需求。

===========================================

PHP-Push技术实现刷新功能

Server push 前一段时间炒得很热的“推”技术,不过网上大部分都是cgi的资料,偶尔看到一个法国的网站上有这么个介绍,可惜法语看不懂,只能从他的程序中看懂点东西,现整理个例子出来大家学习一下。可以用于聊天室的数据传输、网站上的新闻更新、等等各类更新频繁的页面。

以前做刷新主要通过页面上加标签。

 < META HTTP-EQUIV=REFRESH CONTENT="time;URL=url" >


或者使用javascript的timeout+reload,不过这种刷新的方法取决于时间的设定,无法连续的数据传输且时间不好确定。采用了Server push的服务器在客户机做出一个请求后,和客户机建立一个永久的连接,然后服务器会根据客户机的请求不断地把数据包推向服务器。那些你觉察不到的延迟会让你觉得服务器的响应和你的请求已经达到了同步的程度。

先来看一下例子再解释。

 img.php
  < ?php
    set_time_limit(0);
    $file = "./1.jpg";
    $sep = "gIrLsKiCkAsSiTsAySsOoNaTsHiRt";
  if(ereg(".*MSIE.*",$HTTP_SERVER_VARS["HTTP_USER_AGENT"])){
  //如果是ie浏览器,直接输出就退出,IE的不支持哦,我没试出来过
    header("Cache-Control: no-cache");
    header("Pragma: no-cache");
    header("Content-type: image/jpeg");
    header("Content-size: " . filesize($file));
    readfile($file);
  }else{
    header("Content-Type: multipart/x-mixed-replace; boundary=$sep");
  //这里是关键哦,看看MIME类型说明

  //你会明白
  print "--$sep
";
  do{
    print "Content-Type: image/jpeg
";
    readfile($file);
    print "
--$sep
";
    flush();
    $mt = filemtime($file);
    do{
      sleep (1);
      clearstatcache();
     }while($mt == filemtime($file));
  }while(1);
}
? >


这就是一个永久执行的页面(网络不断的情况下),不断输出图片的内容,下面是调用的页面。<img src=img.php>,然后打开你的netscape或其他非ie浏览器查看调用页面,好象没什么变化啊,别急,接着就是怎样变动1.jpg这个图片了,写个另外的php页面来测试吧,比如弄2张图片按时间来覆盖1.jpg(这个方法自己想,用拷贝覆盖也行,只要1.jpg有变化)。这时你就看到调用页面的图片自动更新了。

使用中你会发现个问题:怎么图片不自动更新了。这是由于客户机在一段时间内没有对服务器发生请求,也就是某一段时间内没有新的内容向浏览器输入,可能发生连接超时现象。什么办法解决呢?可以在执行页面中加个向浏览器发送一个空信号,类似ftp连接方式,上面页面中在do...while(1)间加个print("");

看完这个例子,应该知道怎么处理不断更新的内容了,关键在img.php页面中的处理(可以是检查新的内容并输出),调用的时候可以有img、script、frame等等。详细的其他使用方面请查看:http://www.npds.org/

http://www.zeali.net/entry/129

http://www.rainway.org/2004/10/03/server-push/

http://tech.ccidnet.com/art/294/20030212/37827_1.html

阅读更多

跪求Transfer-Encoding: chunked的解析代码!!

08-18

这是小弟写的一个代理服务器的方法.clientSock监听localhost的请求(其实就是用localhost作代理,有什么就传回什么,很简单没什么实际意义的代理),serverSock获取服务器的字节.然后这个DoRequest就是总的方法rnrn如果网页是一口气把byte都传送回来的话,网页显示没问题,但问题是,如果网页的Transfer-Encoding是chunked的话,这个方法就不行了.POST也貌似不能用...rnrn那位大大能够提供一个解析chunked的C#代码呢?最好是能够直接用在我最下面外边的else里面的(因为是草稿,所以会有些没什么意义的代码,抱歉了)rn[code=C#]rn public void DoRequest()rn rn System.Text.Encoding utf8 = System.Text.Encoding.UTF8;rn //const int BUFSZ = 1024;rnrn tryrn rn // IPAddress and IPEndPoint represent the endpoint that willrn // receive the request.rn // Get the first IPAddress in the list using DNS.rnrn IPAddress hostadd = Dns.GetHostEntry(Host).AddressList[0];rn if (Host == "localhost")rn rn hostadd = IPAddress.Loopback;rn rn IPEndPoint remoteEndPoint = new IPEndPoint(hostadd, Port);rnrn //Console.WriteLine(remoteEndPoint.ToString());rnrn //Creates the Socket for sending data over TCP.rn Socket serverSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);rn serverSock.ReceiveTimeout = 20 * 1000; // Set time out (milliseconds)rnrn // Connects to the host using IPEndPoint.rn serverSock.Connect(remoteEndPoint);rn serverSock.Send(RequestBytes, RequestBytes.Length, SocketFlags.None);rnrn // Get the HTTP headers firstrn List ResponseBytesList = new List();rn string header = "";rn while (true)rn rn byte[] bytes = new byte[1];rn int bytesRec = serverSock.Receive(bytes, SocketFlags.None);rn rn header += System.Text.Encoding.UTF8.GetString(bytes, 0, bytesRec);rn for (int i = 0; i < bytesRec; i++)rn rn ResponseBytesList.Add(bytes[i]);rn rnrn if (header.IndexOf("\r\n\r\n") > -1 || header.IndexOf("\n\n") > -1)rn rn //Blank line found, header endsrn break;rn rn rn rn byte[] HeaderBytes = ResponseBytesList.ToArray();rn //Console.WriteLine(header.Equals(utf8.GetString(ResponseBytesList.ToArray())));rnrn // Break up the headersrn string[] headers = header.Split(new char[] '\n' );rnrn string statusLine = headers[0];rn Console.WriteLine(header);rn bool isChunked = checkIfChunked(headers);rnrn //List ContentBytesList = new List();rnrn if (isChunked == false)rn rn //Console.WriteLine("Not chunked");rn int contentLength = GetContentLength(headers);rn clientSock.Send(HeaderBytes, HeaderBytes.Length, SocketFlags.None);rn byte[] contentBytesArray = new byte[contentLength];rn int index = 0;rn while (index < contentLength)rn rn byte[] bytes = new byte[1];rn int bytesNowGot = serverSock.Receive(bytes, SocketFlags.None);rn if (bytesNowGot != 0)rn rn contentBytesArray[index] = bytes[0];rn index++;rn rn rn clientSock.Send(contentBytesArray, SocketFlags.None); rn //If not chunked, it worksrn else//If chunked, it doesnt'rn rn int chunkSize = 0;rn string content = "";rn clientSock.Send(HeaderBytes, HeaderBytes.Length, SocketFlags.None);rn dorn rn byte[] bytes = new byte[1];rn serverSock.Receive(bytes);rn content += utf8.GetString(bytes);rn if (content.IndexOf("\r\n") > -1)rn rn chunkSize = Int32.Parse(content.Substring(0, content.Length - 4), System.Globalization.NumberStyles.HexNumber);rn Console.WriteLine("Chunk size: " + chunkSize);rn bytes = new byte[chunkSize];rn serverSock.Receive(bytes);rn clientSock.Send(bytes);rn content = "";rn rnrn while (chunkSize != 0);rn rn rn serverSock.Shutdown(SocketShutdown.Both);rn serverSock.Close();rn clientSock.Shutdown(SocketShutdown.Both);rn clientSock.Close();rn catch (Exception ex)rn rn Console.WriteLine(ex.ToString()); rn rn // DoRequestrn[/code]

没有更多推荐了,返回首页