音视频流媒体开发【五十四】HLS流媒体6-HTTP协议分析

音视频流媒体开发-目录
iOS知识点-目录
Android-目录
Flutter-目录
数据结构与算法-目录
uni-pp-目录

进⼀步参考⽂档:

《HTTP协议》
《图解HTTP》

报⽂结构

你也许对 TCP/UDP 的报⽂格式有所了解,拿 TCP 报⽂来举例,它在实际要传输的数据之前附加了⼀个20 字节的头部数据,存储 TCP 协议必须的额外信息,例如发送⽅的端⼝号、接收⽅的端⼝号、包序号、标志位等等。

有了这个附加的 TCP 头,数据包才能够正确传输,到了⽬的地后把头部去掉,就可以拿到真正的数据。

HTTP 协议也是与 TCP/UDP 类似,同样也需要在实际传输的数据前附加⼀些头数据,不过与 TCP/UDP不同的是,它是⼀个“纯⽂本”的协议,所以头数据都是 ASCII 码的⽂本,可以很容易地⽤⾁眼阅读,不⽤借助程序解析也能够看懂。

HTTP 协议的请求报⽂和响应报⽂的结构基本相同,由三⼤部分组成:

  • 起始⾏(start line):描述请求或响应的基本信息;
  • 头部字段集合(header):使⽤ key-value 形式更详细地说明报⽂;
  • 消息正⽂(entity):实际传输的数据,它不⼀定是纯⽂本,可以是图⽚、视频等⼆进制数据。

这其中前两部分起始⾏和头部字段经常⼜合称为“请求头”或“响应头”,消息正⽂⼜称为“实体”,但与“header”对应,很多时候就直接称为“body”。

HTTP 协议规定报⽂必须有 header,但可以没有 body,⽽且在 header 之后必须要有⼀个“空⾏”,也就是“CRLF”,⼗六进制的“0D0A”。

所以,⼀个完整的 HTTP 报⽂就像是下图的这个样⼦,注意在 header 和 body 之间有⼀个“空⾏”。

看⼀下我们之前⽤ Wireshark 抓的包。

在这个浏览器发出的请求报⽂⾥。

第⼀⾏“GET / HTTP/1.1”就是请求⾏,⽽后⾯的“Host”“Connection”等等都属于 header,报⽂的最后是⼀个空⽩⾏结束,没有 body。

在很多时候,特别是浏览器发送 GET 请求的时候都是这样,HTTP 报⽂经常是只有 header ⽽没 body。

请求⾏

了解了 HTTP 报⽂的基本结构后,我们来看看请求报⽂⾥的起始⾏也就是请求⾏(request line),它简要地描述了客户端想要如何操作服务器端的资源。

请求⾏由三部分构成:
1. 请求⽅法:是⼀个动词,如 GET/POST,表示对资源的操作;
2. 请求⽬标:通常是⼀个 URI,标记了请求⽅法要操作的资源;
3. 版本号:表示报⽂使⽤的 HTTP 协议版本。

这三个部分通常使⽤空格(space)来分隔,最后要⽤ CRLF 换⾏表示结束。(图示SP代表空格)

还是⽤ Wireshark 抓包的数据来举例:

GET / HTTP/1.1\r\n
GET /live/livestream.m3u8 HTTP/1.1\r\n

在这个请求⾏⾥,“GET”是请求⽅法,“/”是请求⽬标,“HTTP/1.1”是版本号,把这三部分连起来,意思就是“服务器你好,我想获取⽹站根⽬录下的默认⽂件,我⽤的协议版本号是 1.1,请不要⽤ 1.0 或者 2.0回复我。”

别看请求⾏就⼀⾏,貌似很简单,其实这⾥⾯的“讲究”是⾮常多的,尤其是前⾯的请求⽅法和请求⽬标,组合起来变化多端,后⾯我还会详细介绍。

状态⾏

看完了请求⾏,我们再看响应报⽂⾥的起始⾏,在这⾥它不叫“响应⾏”,⽽是叫“状态⾏”(status line),意思是服务器响应的状态。

⽐起请求⾏来说,状态⾏要简单⼀些,同样也是由三部分构成:

  • 版本号:表示报⽂使⽤的 HTTP 协议版本;
  • 状态码:⼀个三位数,⽤代码的形式表示处理的结果,⽐如 200 是成功,500 是服务器错误;
  • 原因:作为数字状态码补充,是更详细的解释⽂字,帮助⼈理解原因。

看⼀下上⼀讲⾥ Wireshark 抓包⾥的响应报⽂,状态⾏是:

HTTP/1.1 200 OK\r\n

意思就是:“浏览器你好,我已经处理完了你的请求,这个报⽂使⽤的协议版本号是 1.1,状态码是 200,⼀切 OK。”

⽽另⼀个“GET /favicon.ico HTTP/1.1”的响应报⽂状态⾏是:

HTTP/1.1 404 Not Found

翻译成⼈话就是:“抱歉啊浏览器,刚才你的请求收到了,但我没找到你要的资源,错误代码是 404,接下来的事情你就看着办吧。”

头部字段

请求⾏或状态⾏再加上头部字段集合就构成了 HTTP 报⽂⾥完整的请求头或响应头,我画了两个示意图,你可以看⼀下。

请求头和响应头的结构是基本⼀样的,唯⼀的区别是起始⾏,所以我把请求头和响应头⾥的字段放在⼀起介绍。

头部字段是 key-value 的形式,key 和 value 之间⽤“:”分隔,最后⽤ CRLF 换⾏表示字段结束。⽐如在“Host: 127.0.0.1”这⼀⾏⾥ key 就是“Host”,value 就是“127.0.0.1”。

HTTP 头字段⾮常灵活,不仅可以使⽤标准⾥的 Host、Connection 等已有头,也可以任意添加⾃定义头,这就给 HTTP 协议带来了⽆限的扩展可能。

不过使⽤头字段需要注意下⾯⼏点:

  • 字段名不区分⼤⼩写,例如“Host”也可以写成“host”,但⾸字⺟⼤写的可读性更好;
  • 字段名⾥不允许出现空格,可以使⽤连字符“-”,但不能使⽤下划线“_”。例如,“test-name”是合法的字段名,⽽“test name”“test_name”是不正确的字段名;
  • 字段名后⾯必须紧接着“:”,不能有空格,⽽“:”后的字段值前可以有多个空格;
  • 字段的顺序是没有意义的,可以任意排列不影响语义;
  • 字段原则上不能重复,除⾮这个字段本身的语义允许,例如 Set-Cookie。
⽤wireshark抓包的分析
请求
image.png
响应

常⽤头字段

HTTP 协议规定了⾮常多的头部字段,实现各种各样的功能,但基本上可以分为四⼤类:

  • 通⽤字段:在请求头和响应头⾥都可以出现;
  • 请求字段:仅能出现在请求头⾥,进⼀步说明请求信息或者额外的附加条件;
  • 响应字段:仅能出现在响应头⾥,补充说明响应报⽂的信息;
  • 实体字段:它实际上属于通⽤字段,但专⻔描述 body 的额外信息。

对 HTTP 报⽂的解析和处理实际上主要就是对头字段的处理,理解了头字段也就理解了 HTTP 报⽂。

这⾥主要讲⼏个最基本的头,看完了它们你就应该能够读懂⼤多数 HTTP 报⽂了。

User-Agent

User-Agent是请求字段,只出现在请求头⾥。它使⽤⼀个字符串来描述发起 HTTP 请求的客户端,服务器可以依据它来返回最合适此浏览器显示的⻚⾯。

但由于历史的原因,User-Agent ⾮常混乱,每个浏览器都⾃称是“Mozilla”“Chrome”“Safari”,企图使⽤这个字段来互相“伪装”,导致 User-Agent 变得越来越⻓,最终变得毫⽆意义。

不过有的⽐较“诚实”的爬⾍会在 User-Agent ⾥⽤“spider”标明⾃⼰是爬⾍,所以可以利⽤这个字段实现简单的反爬⾍策略。

Accept

Accept是请求字段,代表客户端希望接受的数据类型。⽐如Accept:text/xml(application/json);代表客户端希望接受的数据类型是xml(json )类型;⽐如Accept: /则说明客户端接收所有类型的数据。

Host

⾸先要说的是Host字段,它属于请求字段,只能出现在请求头⾥,它同时也是唯⼀⼀个 HTTP/1.1 规范⾥要求必须出现的字段,也就是说,如果请求头⾥没有 Host,那这就是⼀个错误的报⽂。

Host 字段告诉服务器这个请求应该由哪个主机来处理,当⼀台计算机上托管了多个虚拟主机的时候,服务器端就需要⽤ Host 字段来选择,有点像是⼀个简单的“路由重定向”。

例如我们的试验环境,在 127.0.0.1 上有三个虚拟主机:“www.chrono.com”“www.metroid.net”和“origin.io”。那么当使⽤域名的⽅式访问时,就必须要⽤Host 字段来区分这三个 IP 相同但域名不同的⽹站,否则服务器就会找不到合适的虚拟主机,⽆法处理。

Range

Range是请求字段。

⽐如Range: bytes=5001-10000 对于只需获资源的范围请求,包含⾸部字段 Range 即可告知服务器资源的指定范围。上⾯的示例表示请求获取从第 5001 字节到第 10000 字节的资源。

⽐如Range: bytes=0- 则是请求所有的数据。

接收到附带 Range ⾸部字段请求的服务器,会在处理请求之后返回状态码为 206 Partial Content 的响应。⽆法处理该范围请求时,则会返回状态码 200 OK 的响应及全部资源。

Connection

管理持久连接

①: close 断开连接

Connection: close

HTTP/1.1版本的默认连接都是持久连接。为此,客户端会在持久连接上连续发送请求。当服务器端想明确断开连接时,则指定 Connection ⾸部字段的值为 close 。

②: Keep-Alive 保持连接

Connection: keep-alive

HTTP/1.1 之前的版本的默认连接都是⾮持久连接。为此,如果想在旧版本的HTTP协议上维持持续连接,则需要指定 Connection ⾸部字段的值为 keep-alive 。

在客户单发送请求给服务器时,携带此参数和值,服务器也会加上字段和值进⾏返回响应。

http是⼀个⽆状态的⾯向连接的协议。

http⽆状态:⽆状态协议是指http协议本身对于事务处理没有记忆功能,服务器不知道浏览器的状态。通俗的即使你登录了,去访问同⼀个⽹站的不同⽹⻚,服务器都不会知道你是谁,如果需要记录登录⽤户的信息,⽤户操作,⽤户⾏为等数据需要使⽤cookie或session来存储。

keep-alive:从HTTP/1.1起,浏览器默认都开启了Keep-Alive,保持连接特性,客户端和服务器都能选择随时关闭连接,则请求头中为connection:close。简单地说,当⼀个⽹⻚打开完成后,客户端和服务器之间⽤于传输HTTP数据的TCP连接不会关闭,如果客户端再次访问这个服务器上的⽹⻚,会继续使⽤这⼀条已经建⽴的TCP连接。但是Keep-Alive不会永久保持连接,它有⼀个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。

误解:⽆状态不代表HTTP不能保持TCP连接,更不能代表HTTP使⽤的是UDP协议(⽆连接)。即使http在⽆状态下,只要客户端和服务器的头部信息connection:keep-alive,则在有效期内他们使⽤同⼀条TCP连接。

Date

Date字段是⼀个通⽤字段,但通常出现在响应头⾥,表示 HTTP 报⽂创建的时间,客户端可以使⽤这个时间再搭配其他字段决定缓存策略。

Server

Server字段是响应字段,只能出现在响应头⾥。它告诉客户端当前正在提供 Web 服务的软件名称和版本号, Server 字段也不是必须要出现的,因为这会把服务器的⼀部分信息暴露给外界,如果这个版本恰好存在 bug,那么⿊客就有可能利⽤ bug 攻陷服务器。所以,有的⽹站响应头⾥要么没有这个字段,要么就给出⼀个完全⽆关的描述信息。

⽐如 GitHub,它的 Server 字段⾥就看不出是使⽤了 Apache 还是 Nginx,只是显示为“GitHub.com”。

再⽐如srs流媒体服务器的响应 Server:SRS/3.0.141(OuXuli)\r\n

Content-Type

Content-Type是实体字段,表发送端(客户端|服务器)发送的实体数据的数据类型。⽐如:ContentType:text/html(application/json) ; 代表发送端发送的数据格式是html(json)。

Content-Length

实体字段⾥要说的⼀个是Content-Length,它表示报⽂⾥ body 的⻓度,也就是请求头或响应头空⾏后⾯数据的⻓度。服务器看到这个字段,就知道了后续有多少数据,可以直接接收。如果没有这个字段,那么 body 就是不定⻓的,需要使⽤ chunked ⽅式分段传输。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,366评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,521评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,689评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,925评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,942评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,727评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,447评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,349评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,820评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,990评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,127评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,812评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,471评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,017评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,142评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,388评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,066评论 2 355

推荐阅读更多精彩内容