这里填写标题
1. HTTP 协议之 Chunked 解析
在网上找了好一会, 始终没发现有解析 Chunked 编码的文章, 那就自己写一个吧, 呵呵。
网上使用 Chunked 编码的网站似乎并不是很多, 除了那些使用 GZip 压缩的网站, 例: google.com, 还有就是大部分打开 GZip 压缩的 PHP 论坛。
根据本人的理解, 使用 Chunked 编码的主要好处就在于一些程序的运算出过程中, 可以动态的输出内容。
例如, 要在后台处理一个小时的运算, 但又不希望用户等一个小时才能看到结果。这时就可采用 Chunked 编码将内容分块输出, 用户随时都可以接收到最新的处理结果。
ASP 关闭了缓存的输出模式, 就是 Chunked 编码的。(Response.Buffer = false)
而每一次的 Response.Write
, 都是一个 Chunked, 所以不要使用的太频繁哦, 否则 Chunk 数量太多, 额外的数据太浪费空间了。
分块传输编码(Chunked transfer encoding)是超文本传输协议(HTTP)中的一种数据传输机制, 它允许 HTTP 由网页服务器发送给客户端应用的数据可以分成多个部分。
- 使用限制: 分块传输编码只在 HTTP 协议 1.1 版本(HTTP/1.1)中提供。
通常情况下, HTTP 应答消息中发送的数据是整个发送的, Content-Length 消息头字段表示数据的长度。数据的长度很重要, 因为客户端需要知道哪里是应答消息的结束, 以及后续应答消息的开始
然而, 使用分块传输编码, 数据分解成一系列数据块, 并以一个或多个块发送, 这样服务器可以发送数据而不需要预先知道发送内容的总大小。
- 分块传输编码的使用场景:
当客户端向服务器请求一个静态页面或者一张图片时, 服务器可以很清楚的知道内容大小, 然后通过 Content-Length 消息首部字段告诉客户端需要接收多少数据。但是如果是动态页面等时, 服务器是不可能预先知道内容大小, 这时就可以使用 Transfer-Encoding: chunk 模式来传输数据了。即如果要一边产生数据, 一边发给客户端, 服务器就需要使用"Transfer-Encoding: chunked"这样的方式来代替 Content-Length。
在进行 chunked 编码传输时, 在回复消息的头部有 Transfer-Encoding: chunked
- 分块传输编码的编码格式:
编码使用若干个 chunk 组成, 由一个标明长度为 0 的 chunk 结束。每个 chunk 有两部分组成, 第一部分是该 chunk 的长度, 第二部分就是指定长度的内容, 每个部分用 CRLF 隔开。在最后一个长度为 0 的 chunk 中的内容是称为 footer 的内容, 是一些没有写的头部内容。
chunk 编码格式如下:
[chunk size][\r\n][chunk data][\r\n][chunk size][\r\n][chunk data][\r\n][chunk size = 0][\r\n][\r\n]
chunk size 是以十六进制的 ASCII 码表示, 比如: 头部是 3134 这两个字节, 表示的是 1 和 4 这两个 ascii 字符, 被 http 协议解释为十六进制数 14, 也就是十进制的 20, 后面紧跟 [\r\n](0d 0a)
, 再接着是连续的 20 个字节的 chunk 正文。chunk 数据以 0 长度的 chunk 块结束, 也就是(30 0d 0a 0d 0a)。
分块编码传输的译码过程:
最最开始的地方: 你需要先确认你收到的数据时使用的 chunked 编码, 也就是找到 Transfer-Encoding: chunked, 如果你找到了, 好的, 那接下来就可以按照下面的步骤开始进行解析了。
- 你需要找到数据开始地方, 也就是第一个 chunk size 开始的地方, 这个地方的标识符为、
r\n\r\n
- 你要成功获取到这一块的数据长度, 这个地方需要注意了, 十六进制的 ASCII 码表示。是的, 字符串转成数字, 你想起来函数 atoi() 了吗? 恭喜你, 你函数选择错了, atoi() 只能识别数字 0-9, 遇到 A-F 就截断了, 不过不用担心, 还有一个函数叫做 strtol(), 它可以随意的转换 2-36 进制。
- 获取到数据块长度了, 就可以 copy 数据了是吧, 是不是很简单, 但是如果你使用了 str ***相关的函数, 那么恭喜你, 你又错了, 他们遇到、0 就会停止, 而你需要 copy 的数据可能会遇到很多歌、0, 还好有 memcpy() 这一类的函数可以使用。如果数据时写到指针里面, 多次 copy 数据时记得指针的偏移
- 接下就跳过、r\n 去获取下一个数据块长度, 然后 copy 数据吧, 直到你遇到一个数据块的长度为 0, 到这里数据就完全获取成功了。
看到了这里就会发现里面数据获取的方式是拼接, 就是舍弃数据块长度, \r\n
, 只要数据块。是不是很简单?
下面就说说我之前的解析想法以及错误的原因吧。
我之前没有想到用到长度这个数据, 而是看到了 “\r\n 数据块、r\n”, 于是我就想到通过查找"\r\n"来提取数据, 如果你认真的看了上面过程中我的解析, 你就知道原因了。是的, 数据块中有、0, 所以我没有找到我想要的 “\r\n”, 就解析失败了。
所以一定要用好字符串函数。一定。