聊一聊HTTP的Range, Content-Range



前言

早上8点左右的时候看到了微信群里有一条分享,点开进去后是一个视频,是网易新闻的一个关于圣诞节的一个推广视频,刚好就坐在电脑旁边,便用 Charles 抓包看了下。

因为以前对视频加载这块接触的不多,便想了解下获取视频的请求和普通的请求有什么不通,如何播放就不谈了,主要从HTTP协议的角度研究学习下。



刚打开网页的时候只有前3个请求,此时视频是暂停的,通过视频上方中的播放按钮开始了播放,这时候开始发出了第4个请求。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
HTTP-Trace-Version: 1.0  // Charles的http trace版本
Generator: Charles/4.0.1 // Charles版本

// 请求
Method: GET
Protocol-Version: HTTP/1.1
Protocol: http
Host: file.ws.126.net
File: /3g/activities/xmas2016.mp4
Remote-Address: 58.59.19.125
Client-Address: 192.168.1.104
Start-Time: 2016-12-24T08:20:44.528+08:00
DNS-Duration: 9
Connect-Duration: 22
Request-Begin-Time: 2016-12-24T08:20:44.560+08:00
Request-Time: 2016-12-24T08:20:44.561+08:00
Response-Time: 2016-12-24T08:20:44.586+08:00
End-Time: 2016-12-24T08:20:44.588+08:00
Request-Header-Size: 504
Response-Header-Size: 424
Request-Body-Size: 0
Response-Body-Size: 2
Request-Body-Decoded: false
Response-Body-Decoded: false
Request-Header:<<--EOF-1482539973400-
GET /3g/activities/xmas2016.mp4 HTTP/1.1
Host: file.ws.126.net
Accept-Language: zh-cn
X-Playback-Session-Id: 843D959A-32AD-49C4-A2D7-32065A6292D9
Range: bytes=0-1
Accept: */*
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 10_2 like Mac OS X) AppleWebKit/602.3.12 (KHTML, like Gecko) Mobile/14C92 MicroMessenger/6.5.2 NetType/WIFI Language/zh_CN
Referer: http://c.m.163.com/nc/qa/activity/20161202/index.html?from=groupmessage&isappinstalled=1
Accept-Encoding: identity
Connection: keep-alive


--EOF-1482539973400-
// 响应
Response-Header:<<--EOF-1482539973400-
HTTP/1.1 206 Partial Content
Date: Sat, 24 Dec 2016 00:20:44 GMT
Expires: Sun, 25 Dec 2016 00:20:44 GMT
Content-Length: 2
Accept-Ranges: bytes
Content-Range: bytes 0-1/4300047
Content-Type: text/plain; charset=GBK
Last-Modified: Thu, 22 Dec 2016 02:04:16 GMT
Cache-Control: max-age=86400
Server: nginx
Fw-Cache-Status: hit
Fw-Via: DISK HIT from 61.130.28.99, DISK HIT from 58.59.19.104
Proxy-Connection: Keep-alive

这是第一个请求的一些详细信息,其余3个请求我就不贴了,比较占篇幅,值得注意的是,这4个请求中请求头的range每一个都不同,依次如下:

  • bytes=0-1
  • bytes=0-4300046
  • bytes=4276224-4300046
  • bytes=8640-4276223

Range的诞生

Range,是在 HTTP/1.1里新增的一个请求头字段域。我们使用的迅雷等支持多线程下载以及断点下载的核心也是基于此重要特性。

HTTP/1.1规范的 Range 的约定

如果Server支持 Range,首先就要告诉客户端,服务器会在响应头中添加Accept-Ranges: bytes来表示支持 Range 的请求,之后客户端才可能发起带 Range 的请求。不支持的话,用Accept-Ranges: none告知客户端,对不起,我不支持。

Server通过请求头中的Range: bytes=0-xxx来判断是否是做 Range 请求,如果这个值存在而且有效,则只发回请求的那部分文件内容,响应的状态码变成206,表示Partial Content,并设置Content-Range。如果无效,则返回416状态码,表明Request Range Not Satisfiable。如果请求头中不带 Range,那么 Server则正常响应,也不会设置 Content-Range 等。

Value Description
206 Partial Content
416 Range Not Satisfiable

Range的格式为:

Range:(unit=first byte pos)-[last byte pos],即Range: 单位(如bytes)= 开始字节位置-结束字节位置

我们再来看个例子。假设我们要开启多线程下载,需要把一个大文件分割成多个部分进行下载,比如4个部分,然后创建4个线程,每个线程负责下载一个部分,如果文件大小为 5000 个 byte(随意的数字),那么我们可以划分为

  • Range: bytes=0-1199 头1200个字节
  • Range: bytes=1200-2399 第二个1200字节
  • Range: bytes=2400-3599 第三个1200字节
  • Range: bytes=3600-5000 最后的1400字节

服务器给出响应:

// 第1个响应

  • Content-Length:1200
  • Content-Range:bytes 0-1199/5000

// 第2个响应

  • Content-Length:1200
  • Content-Range:bytes 1200-2399/5000

// 第3个响应

  • Content-Length:1200
  • Content-Range:bytes 2400-3599/5000

// 第4个响应

  • Content-Length:1400
  • Content-Range:bytes 3600-5000/5000

如果每个请求都成功了,服务器返回的response头中有一个 Content-Range 的字段域,Content-Range 用于响应头,告诉了客户端发送了多少数据,它描述了响应覆盖的范围和整个实体长度。一般格式:

Content-Range: bytes (unit first byte pos) - [last byte pos]/[entity length]Content-Range:字节 开始字节位置-结束字节位置/文件大小

小结

HTTP协议博大精深,设计有很多巧妙的地方,Range也许就是一处吧。

更多阅读

坚持原创技术分享,您的支持将鼓励我继续创作!