一、HTTP 协议是什么

每当我们打开浏览器,输入网址,点击链接,浏览网页时,背后都在发生着一场看不见的"数据接力"。这些数据是如何从服务器准确无误地传送到我们电脑上的?中间又经历了怎样的过程?今天,我们就来深入探讨互联网数据传输的基石——HTTP和HTTPS协议。

HTTP(HyperText Transfer Protocol,超文本传输协议)是一种应用非常广泛的应用层协议。是当前 web 开发中最核心的协议,但凡使用网站,都会用到HTTP协议。

那么,协议是什么意思?为了使数据能够完好无损地从源头发送到目的地,参与网络通信的收发方必须遵循相同的规则,这套规则就是协议,最终体现为网络上传输的数据包的格式。
简单来说,协议就类似一种暗号,只有懂得相同的暗号才能将数据传输过去。
在这里插入图片描述
HTTP诞生于1991年,目前已成为最主流的一种网络协议。

简单来说,当我们在浏览器中输入一个网址(URL),浏览器就会给服务器发送一个HTTP请求,服务器接收到请求后计算响应并返回HTTP响应。这个响应被浏览器解析之后,就展示成我们看到的内容了。

HTTP/1.0、HTTP/1.1和HTTP/2.0的底层基于TCP协议实现,而HTTP/3.0则是基于UDP协议实现。

二、HTTP 协议的工作过程

当我们访问一个网站,此时浏览器会给对应的服务器发送一个HTTP请求,服务器收到请求后经过计算得到一个HTTP响应,然后返回给浏览器,浏览器接收到响应我们就会看到网站内容了。
在这里插入图片描述
其实,当我们访问网站时,涉及到的HTTP请求/响应的交互可能不止一次。

我们当前学习的HTTP协议的特点就是一问一答,即有几条请求就会返回几条响应。
当然还有不同的收发模式:

  1. 多问一答:如上传一个大文件
  2. 一问多答:如下载一个大文件
  3. 多问多答:如远程控制程序

三、HTTP 协议格式

HTTP是一个文本格式的协议,我们可以通过两种方法查看协议细节:

  1. 浏览器自带的开发者工具,如Chrome按下F12可以开启开发者工具,然后点击Network就可以查看具体的通信交互细节了
  2. 使用抓包工具,我们使用Fiddler(专门抓包HTTP协议)来抓包HTTP协议以查看具体细节

3.1 Fiddler 抓包工具

什么是抓包工具?

抓包工具是一个能够获取到网络通信的数据包并且解析详细的格式的软件,电脑上的所有网络通信数据包都会先发给抓包工具,然后由抓包工具转发给服务器。

简单来说,抓包工具相当于一个代理,负责将请求/响应这些通信内容转发给服务器/浏览器,分为:

  • 正向代理:代替浏览器转发请求给对应服务器
  • 反向代理:代替服务器转发响应给浏览器

但是,未经授权的抓包操作可能涉及到信息安全,因此务必要注意抓包工具的使用!

目前较知名的抓包工具是 wireshark,可以抓包多个协议的数据包(HTTP、TCP、UDP、IP、以太网数据帧…),但是,使用的门槛较高,因此我们暂不使用。
Fiddler 是一个专门抓包HTTP协议的抓包工具,且使用起来简单、方便。

Fiddler官方下载地址
在这里插入图片描述
在这个页面下滑,找到 Fiddler Classic,我们要下载的就是这个版本(因为它免费~)
在这里插入图片描述
然后点击 “here” 跳转页面,点击 “Try For Free”,在这里插入图片描述
跳转到这个页面,输入信息就可以下载了(下载速度较慢的话可能需要魔法~)
在这里插入图片描述
下载完成后,点击 tool -> option 选项卡,然后点击 HTTPS在这里插入图片描述
第一次下载可能需要你信任Fiddler的根证书,这里一定要点击 YES!!否则就得卸载重装了~

然后页面就是这样(目前我的浏览器正在请求的是CSDN,因为我正在写文章):
在这里插入图片描述
可以看到,左边栏显示抓包到的数据包有很多,其实电脑上随时都会有很多程序在偷偷的运行。
这里的颜色也有不同:

  1. 红色表示报错,可能是请求失败了
  2. 蓝色表示请求得到了一个网页
  3. 绿色表示请求得到了一个JavaScript
  4. 灰色表示响应的数据已被缓存

右边上栏的就是请求的原始数据(要点击 raw),下栏的是响应的原始数据(原来是压缩了的,我这里是解压之后的了,点击中间的黄色的带有decode的那个条条就可以解压了)。

那为什么响应要压缩传输呢?其实是为了节省网络带宽,因为响应的正文部分比较长,如果直接传送会很吃带宽,而带宽资源又贵的很~

3.2 请求格式结构

我们通过 Fiddler 抓包到请求的原始数据,点击 “View in Notepad” 可以在记事本中打开原始数据
在这里插入图片描述
在这里插入图片描述
这个就是请求的原始数据,接下来我们来认识一下它的格式结构。

请求结构包含四个部分:

  1. 首行:包含了请求方法、URL和版本号
  2. 请求头(header):是键值对结构
  3. 空行:请求头的结束标志
  4. 正文(body):有些请求有正文有些则没有
    在这里插入图片描述

3.3 响应格式结构

在 Fiddler 的右下栏就是响应的数据,点击中间的黄色长条可以解压,然后点击 “raw” 就可以显示响应的原始数据了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
接下来我们来看看响应的结构。

响应的结构包含:

  1. 首行:包含协议版本、响应状态码和状态码描述。其中的响应状态码表示请求的处理结果:1xx是信息性状态,表示临时响应,与客户端继续操作;2xx是成功状态,表示请求已处理成功;3xx是重定向状态,表示资源已移动,需客户端重定向;4xx是客户端错误状态,表示请求有误(如URL错误);5xx是服务器错误状态,表示服务器处理失败。而状态码描述就是为了方便理解响应(100对应 continue,200对应 OK,302对应 Found,404对应 Not Found,500对应 Internal Server Error)
  2. 响应头(header):也是键值对结构
  3. 空行:是响应头的结束标志
  4. 正文(body):返回浏览器的内容,若是网站则是HTML
    在这里插入图片描述

3.4 协议格式结构总结

在这里插入图片描述

四、HTTP 请求详解(Request)

4.1 认识 URL

URL(Uniform Resource Locator统一资源定位符)就是用来描述网络上唯一资源的位置。互联网上的每一个文件都有一个唯一的URL,它包含了文件的位置以及浏览器如何使用等信息。

  • 这是一条标准的URL:
    http://user:pass@www.example.jp:80/dir/index.htm?uid=1#ch1
    在这里插入图片描述
  • 一个具体的URL(我的CSDN主页):
    https://blog.csdn.net/REGARD712?spm=1011.2415.3001.5343
    在这里插入图片描述

接下来我们来分析:

  1. https是一个协议名称,本质是 http + s(保护层ssl)起到保密作用,这里我们使用https协议访问CSDN网站,若是访问数据库MySQL,则应写成“jdbc:mysql”
  2. user:pass是登录信息,可以看到在这里省略了,因为现在网站进行身份认证一般不再通过URL了。
  3. blog.csdn.net服务器地址,这里是一个域名,域名可以通过DNS域名解析系统来转换成一个具体的IP地址
  4. 端口号,这里省略了端口号,当省略端口号,浏览器就会自动根据协议类型决定使用哪个端口号(http协议默认使用80端口,https协议默认使用443端口)
  5. 带有层次结构的路径,每一层相当于一个目录,目录中还可以有子目录
  6. 查询字符串(query string),对要访问的资源进行补充说明,也是键值对结构,键值对之间使用 “&” 来分隔,键和值之间使用 “=” 来分隔,程序员可以自己定义这里的内容
  7. 片段标识符,对于含有不同文档的网站来说,使用不同的片段标识符可以跳转到不同的文档,这里是CSDN主页没有包含文档,因此此处省略了

4.2 认识 URL encode

当我们搜索的时候输入汉字,比如搜索马斯克,看到浏览器上方的URL中的查询字符串中显示着"埃隆·马斯克"中文汉字,
在这里插入图片描述
于是复制下来整个 URL,却发现粘贴得到的 URL 的查询字符串中,原本是中文汉字的地方变成了一堆看不懂的字符:
在这里插入图片描述
这是怎么一回事?

其实,由于程序员可以自行定义查询字符串,如果定义的符号和原本 URL 指定的带有特殊含义的符号(如. / ? # &等等)冲突了,那就会将该符号转义。转义的规则是:将需要转码的字符由二进制转成十六进制,然后从右到左依次取4位,每2位做一位,在前面加上 “%”,最终编码成 “%XY” 的形式。

在这里,中文字符是由 UTF-8 或者 GBK 编码构成的,虽然在 URL 中没有特殊含义,但是仍然需要进行转义,否则浏览器可能把 UTF-8/GBK 编码中的某个字节当做 URL 中的特殊符号。

4.3 认识 “方法” (method)

方法 说明 支持的HTTP协议版本
GET 获取资源 1.0、1.1
POST 传输实体主体 1.0、1.1
PUT 传输文件 1.0、1.1
HEAD 获得报文首部 1.0、1.1
DELETE 删除文件 1.0、1.1
OPTIONS 询问支持的方法 1.1
TRACE 追踪路径 1.1
CONNECT 要求用隧道协议连接代理 1.1
LINK 建立和资源之间的联系 1.0
UNLINE 断开连接 1.0

这里有很多的方法,使用最多的方法是 GET 和 POST,因此我们主要讲一下这两个方法。

4.3.1 GET 方法

GET 方法是HTTP协议中最常用的方法,常用于获取服务器上的某个资源。

向浏览器网址栏输入一个 URL,此时浏览器就会发送一个 GET 请求给对应的服务器。

并且,HTML的 link、img 和 script 等标签也可以触发 GET 请求;JavaScript 中的 ajax 也可以构造出 GET 请求。

接下来,我访问一下我的 CSDN 主页并使用 Fiddler 抓包 GET 请求:
在这里插入图片描述

GET 请求中的特点:

  1. 首行的第一个是 GET
  2. URL 的查询字符串可以为空也可以不为空,这里不为空
  3. 请求报头header部分有若干个键值对
  4. 正文body部分为空
    在这里插入图片描述
  • 关于 GET 请求的 URL 的长度问题:
  1. 网上有些资料上描述,GET 请求长度最多1024KB,这样的说法其实是不正确的,HTTP 协议由 RFC 2616 标准定义, 标准原⽂中明确说明: "Hypertext Transfer Protocol – HTTP/1.1,"does not specify any requirement for URL length. 也就是说,标准定义中并没有限制 URL 的长度。
  2. 实际 URL 的长度取决于浏览器的实现和HTTP服务器端的实现。在浏览器端,不同的浏览器最大长度是不同的,但现代浏览器支持的长度一般都很长;在服务器端,长度一般都可以配置的。

4.3.2 POST 方法

POST 方法多用于登录界面,将用户信息提交给服务器,可以通过HTML中的 form 标签构造请求,也可以使用 JavaScript 的 ajax 构造构造请求。

通过 Fiddler 抓包我登录力扣时候的 POST 请求:
在这里插入图片描述
POST 请求中的特点:

  1. 首行的第一个是 POST
  2. URL 的查询字符串一般为空(也可以不为空)
  3. 请求包头header部分有若干对键值对
  4. 正文body部分一般不为空,body 中的数据格式是通过 header 中的 Content-Type 来指定,body 的长度由 header 中的 Content-Length 来指定
    在这里插入图片描述

面试题:谈谈 GET 和 POST 的区别


  • 语义上,并没有本质的区别,在一些场景可以混着用。GET 也可以用于提交数据,POST 也可以用以获取数据。只是在使用是有点细微差别:GET 习惯将数据放在 查询字符串(query string)中传输,而正文(body)部分则为空;而 POST 则习惯将数据放在正文(body)中传输,而查询字符串(query string)部分是空的。GET 请求一般是幂等的,而 POST 请求一般没有要求幂等。由于 GET 被要求是幂等的,就可以将数据缓存下来,而 POST 一般没要求幂等,一般也不允许将数据缓存。
  • 补充:幂等是什么意思?
    简单来说就是“执行一次和执行多次的结果是一致的”,比如双十一、抢票等这类抢购的场景,如果一个用户抢到了,并且进入支付页面,但是此时刚好卡了,用户就又点了两三次支付按钮,等到页面恢复正常,支付的金额并不会因为用户多点了两三次而多扣费,而是和用户点一次支付按钮的结果是一样的。
  • 关于 POST 比 GET 更安全的误解:
  1. 误解:网上有些人说,在登录的场景下,GET 比 POST 更加安全。因为若是 GET 请求,登录的时候用户输入的数据就会被放到 query string 中,而 query string 中的键值对是可以通过浏览器的 URL 栏看到的,但若是 POST 请求,数据就会被放到 body 中,在 URL 栏就无法看到了。
  2. 正解:安全与否取决于是否有加密以及加密的强度,与放在请求中的位置无关。因为 GET 和 POST 请求都可以通过抓包工具获取到,重要的是加密了没有。
  • 关于 GET 只能传输文本数据、POST 可以传输二进制的误解
  1. 误解:网上有人说 GET 只能传输文本数据;POST 既可以传输文本数据,也可以传输二进制数据
  2. 正解: GET 也可以传输二进制数据,虽然不能直接在 query string 中传输二进制数据,但是可以针对二进制数据通过 URL encode 转码,然后就可以放到 URL 中;GET 还可以直接将二进制数据放到 body 中(少部分情况)。

4.3.3 其他方法

PUT 方法

PUT 方法一般用于更新,相当于 SQL 中的 UPDATE 操作。

DELETE 方法

DELETE 方法一般用于删除服务器资源的,类似 SQL 中的 DELETE 操作。

HEAD 方法

HEAD 方法类似于 GET 方法,只不过返回的只有报头(header)部分,而不含正文(body)部分。

OPTIONS 方法

返回的是服务器所支持的请求方法。

TRACE 方法

显示服务器端的收到的请求,测试的时候用到

CONNECT 方法

预留,暂无使用。

这些方法的 HTTP 请求可以通过 ajax 来构造,也可以通过一些第三方工具来构造。

4.4 认识请求 “报头”(header)

报头(header)的整体格式是键值对结构,其中的键和值都是有标准规定的(RFC 标准文档),每一个键值对占一行,键值对之间用 “ ; ”分隔。

由于报头中的种类较多,这里只选择几个常见的:

  1. Host
  2. Content-Length
  3. Content-Type
  4. User-Agent
  5. Referer
  6. Cookie

4.4.1 Host

表示服务器主机的地址和端口号。里面的地址和端口号是当前请求所要访问的服务器的地址和端口号。

比如此时我正在写文章,使用 Fiddler 抓到当前页面的 GET 请求中,header 的 Host :
在这里插入图片描述
绝大部分情况下,Host 是和 “IP地址 :端口号” 这个格式一致的,即使是使用了代理,也可以通过 Host 获取到原本的目标。

4.4.2 Content-Length

Content-Length 指定 body 中的数据长度,单位是 “ 字节 ”。

由于 HTTP 请求是经过一定的格式将数据写入 TCP socket 中,然后发送给服务器的,TCP 在一次连接过程中可以进行多次通信(即发送多个请求),若请求中不包含 body ,服务器只需要读到空行就认为这个请求结束了;若请求中包含 body,就没办法区分该次请求到底什么时候结束,这时候返回去解析 header 中的 Content-Length ,然后根据这个值按字节数读取 body 内容。

4.4.3 Content-Type

Content-Type 指定 body 中的数据格式,提示服务器该如何解析(按照不同的数据格式使用对应的解析方法)body 中的数据。这是因为 HTTP 可以携带多种类型的数据(如 HTML、CSS、JavaScript、JSON 和图片),指定数据格式能帮助服务器准确区分并解析这些数据。

这些类型的数据的 Content-Type 如下:

  1. HTML:text/html
  2. CSS:text/css
  3. JavaScript:application/javascript
  4. JSON:application/json
  5. 图片:image/png 或 image/jpg

在这里插入图片描述

4.4.4 User-Agent(简称 UA)

user-agent 显示了用户所使用的设备的情况(客户端应用程序、操作系统、设备型号及浏览器等信息)。

在早期,由于不同浏览器和设备对网页功能的支持程度差异较大,服务器会通过 User-Agent 判断客户端能力,对旧设备或旧浏览器返回简化版页面,对新设备返回功能丰富的页面。

目前,用户的设备都大差不差,因此 UA 主要用于区分用户的设备是 PC端(Windows 或 MAC)还是移动端(Android 或 IOS),然后根据不同的设备返回不同版本的页面。

除了区分 PC端和移动端,UA 还可以:

  1. 统计分析,了解用户使用的设备、操作系统和浏览器的分布情况;
  2. 特定优化,针对某些浏览器(如 Chrome、Safari)的特殊功能或已知问题进行调整;
  3. 安全与反爬,辅助识别异常请求。

需要注意的是,User-Agent 可以被伪造或修改,因此不能完全依赖它做关键判断。

4.4.5 Referer

referer 记录了当前请求的来源页面的地址。

对于从某个网页点击超链接后跳转到另一页面或者提交表单,或者页面加载外部资源(如图片、样式表、脚本)这些操作,在跳转后页面的 HTTP 的请求报头中的 Referer 就会记录下跳转前页面的 URL;但是如果是直接在 URL 栏输入网址的 URL 或者通过书签打开,或者在浏览器中刷新页面这些操作,就不会显示 referer。

4.4.6 Cookie

cookie 是浏览器允许网页在客户端本地存储数据的一种机制,提供键值对存储机制。

cookie 是可以由后端程序员自行定义的,当客户端访问页面,发送了请求,服务器可以在响应中通过 Set-Cookie 头将 Cookie 发送给浏览器,浏览器收到后保存。在浏览器中可以直接查看当前页面的 cookie 内容。浏览器保存了当前页面的 cookie 后,就可以在后续给服务器发送请求的时候把这些 cookie 键值对放到请求 cookie header 中。

cookie 的一个业务场景是“登陆和用户认证”,当用户登录是发送了请求,其中包含了一些用户名密码等信息,当服务器收到请求,就会解析并查询数据库,服务器验证登录成功后,会生成一个唯一的 sessionId,并在服务器端创建对应的 session 对象(存储用户信息),然后将 sessionId 通过 Set-Cookie 返回给浏览器。浏览器后续请求会自动携带该 Cookie,服务器通过 sessionId 找到对应的 session 对象,从而识别用户身份。

Cookie 可以通过 Max-Age 或 Expires 属性设置有效期,未设置时默认为会话 Cookie(浏览器关闭即失效)。后端程序员可根据业务需求调整过期时间。
在这里插入图片描述

五、HTTP 响应详解(Response)

5.1 认识 “状态码”(status code)

状态码是由 RFC标准文档定义的、用于表示此次请求的结果是正常还是出错的三位数字代码。
在这里插入图片描述
由于状态码较多,这里只选取几个较常见的状态码。

5.1.1 200 OK

表示此次请求已成功,出现的大多数是这个响应码。

5.1.2 301 Moved Permanently

表示被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URL 之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。

新的永久性的URL 应当在响应的 Location 域中返回。

如果这不是一个 GET 或者 HEAD 请求,浏览器就会禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。

注意:对于某些使用 HTTP/1.0 协议的浏览器,当它们发送的 POST 请求得到了一个301响应的话,接下来的重定向请求将会变成 GET 方式。

5.1.3 302 Move temporarily

请求的资源临时从不同的 URL 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。

简单来说就是当我们请求了一个资源,但是这个资源暂时移动到新位置,那么就会重定向到新位置(跳转)。

5.1.4 403 Forbidden

表示服务器已经理解请求,但是拒绝执行它。可能是客户端的请求的某个地方出现错误(网址输入错误等)。

5.1.5 404 Not Found

表示请求失败,请求所希望得到的资源未被在服务器上发现。出现这个错误的最有可能的原因是服务器端没有这个页面。

5.1.6 405 Method Not Allowed

表示请求行中指定的请求方法不能被用于请求相应的资源。

5.1.7 500 Internal Server Error

表示服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器端的源代码出现错误时出现。也就是服务器的代码中出现了 bug。

5.1.8 504 Gateway Timeout

表示作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTP、FTP、LDAP)或者辅助服务器(例如DNS)收到响应。可以理解为超时了~

5.1.9 状态码小结

状态码 类别 原因
1xx Informational(信息性状态码) 接收的请求正在处理
2xx Success(成功状态码) 请求正常处理完毕
3xx Redirection(重定向状态码) 需要进行附加操作以完成请求
4xx Client Error(客户端错误码) 服务器无法处理请求
5xx Server Error(服务器错误码) 服务器处理请求出错

由于响应报头(header)和正文(body)部分都与请求部分一致,因此不再过多赘述。

六、通过表单构造 HTTP 请求

6.1 构造 GET 请求

我们这里写一个 HTML 程序:

<form action="http://localhost:8080/hello" method="GET">
    <input type="text" name="userId" placeholder="请输入用户ID">
    <input type="text" name="content" placeholder="请输入内容">
    <input type="submit" value="提交">
</form>

保存后点开该页面:
在这里插入图片描述
输入相应信息之后点击 “提交” 按钮。

由于我们写的 URL是一个不存在的 URL,因此不会出现有效页面,也不会有响应返回。
在这里插入图片描述
使用 Fiddler 抓包得到请求:
在这里插入图片描述

6.2 构造 POST 请求

将代码中的 GET 改为 POST,再一次保存后通过抓包得到请求:
在这里插入图片描述
可以发现原本在 URL 部分的 query string 现在在 body 中了,其他部分没有什么改变。


今天就到这里吧,若有错误请尽管指出~

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐