🌎应用层协议Http
文章目录:
应用层协议Http
认识HTTP协议
认识URL
Http协议请求与响应格式
HTTP Request
HTTP Response
HTTP常见Header
URI资源以及网页跳转原因
HTTP其他属性字段
HTTP状态码
HTTP 的常见方法
🚀认识HTTP协议
虽然我们说, 应用层协议是我们程序猿自己定的. 但实际上, 已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我们直接参考使用。HTTP(超文本传输协议) 就是其中之一。
在互联网世界中,HTTP(HyperText Transfer Protocol,超文本传输协议)是一个至关重要的协议。听起来好像是那么回事,实际上超文本传输协议指的是不仅仅可以传输文本,还可以传输图片、音频、视频等文件。它定义了客户端(如浏览器)与服务器之间如何通信,以交换或传输超文本(如 HTML 文档)。
HTTP 协议是客户端与服务器之间通信的基础。客户端通过 HTTP 协议向服务器发送请求,服务器收到请求后处理并返回响应。HTTP 协议是一个 无连接、无状态 的协议,即 每次请求都需要建立新的连接,且 服务器不会保存客户端的状态信息。
🚩认识URL
我们常说的“网址”,其实就是 URL(Uniform Resource Locator),一个网址通常包含如下部分:
我们从网络上获取的文字,图片,音视频等等,这些信息本质上都是资源。那么在我们获取这些信息之前,这些文件存储在哪个位置?大多数在Linux服务器中,而在Linux当中,一切皆文件,所以 网络上获取的所有资源本质上都是文件!
而我们从网络中获取数据本质上就是从 Linux服务器当中获取文件,而每个文件都是有路径的,所以找到一个文件直接通过文件路径即可访问资源。而我们能够找到对应文件的 前提是我们能够找到对应的服务器。 而要想 找到一个服务器就必须要知道该服务器的 IP[ + PORT] ,而IP[PORT] + 服务器中的文件路径 也就在网络中标识了唯一的文件(资源)。
这里IP后面为什么要给PORT打上括号呢?原因其实在前面文章中也说过,客户端在像服务器端发起请求时,虽然需要绑定IP和端口号,但是不需要用户来绑定,因为这一步操作系统会给你自动绑定,所以在客户端看来,我只需要知道服务器的IP即可。不同于自定义应用层协议,可以随便绑定端口号,这些成熟的上层协议的端口号一般都是固定的,比如http协议的端口号就是 80。
在最上方那张图当中,有一点过时了,实际上现在登录信息时,是不需要用户身份信息验证的。并且片段标识符早期可以将图片进行轮播所用,但是现在运用的同样少了,所以这张图实际上应该如下所示:
当我们在某度的搜索框内搜索东西的时候,比如我们搜索C++,浏览器将在资源路径后面出现一个 wd=关键字信息
(不同的浏览器解释可能不同,有些浏览器就不会使用wd),如果你仔细观察,其实wd后面跟着的就是我们想要索引的关键字。而这些关键字有时候会发现跟我们在搜索框内搜索的不同:
我们在随便搜索一个问题时,我们会在资源路径后面看到一大堆的字符串,其中包含不少的特殊字符,比如 ‘?’,‘/’, ‘:’ 等字符,而 这些字符实际上有特殊含义的,已经被 url 当做特殊意义理解了。因此这些字符不能随意出现. 比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义。
由上面的例子我们可以看出,其实浏览器对特殊字符的转换是有规则的,而这个规则就是:将需要转码的字符转为 16 进制,然后从右到左,取 4 位(不足 4 位直接处理),每 2 位做一位,前面加上%,编码成%XY 格式。
如果你对我们输入中文出来的关键字也是被转换为16进制数而感到疑惑,那么你就不懂ASCII表的含义,所谓的语言本质就是符号,所以语言,ASCII本质就是一个数据数字和符号映射关系的表。我们知道计算机无法对语言进行处理,所以必须要将文字进行转换才能让计算机识别。
🚀Http协议请求与响应格式
我们构建一个Tcp服务器,上层调用服务内部仅仅将将来从客户端发来的请求字符串打印出来,其他什么事情也不做,启动tcp服务器,我们以浏览器为客户端,如果今天浏览器作为客户端,向我们的服务器发起请求,端口号使用自定义端口号。由于客户端是浏览器,所以我们不需要序列化和反序列化工作,因为浏览器已经将这个工作给做了。
如果我们使用浏览器发送了请求,并且服务器端收到了请求,那么我们就可以看到以下返回的客户端请求格式,Tcp上层调用的服务代码如下,我们只对请求进行打印:
#include <string>
#include <iostream>class HttpServer
{
public:HttpServer(){}std::string HandlerHttpRequest(std::string req){std::cout << "-------------------------------" << std::endl;std::cout << req;return std::string();}~HttpServer(){}
private:
};
我们可以发现,客户端发来的请求报头一共有11行,其中最后一行为空行,但实际上它们就只有一行,只不过每一行的末尾都有 \r\n
将它们连接到一起。我们之前说过,无论是什么协议,都要做到 将报头 和 有效载荷进行分离,同样,http协议也是如此。
在上图中,空行以上实际上全部都是有效载荷的部分,空行之后就是正文,只不过这里我们没有做任何处理,所以空行下面就啥也没有。
🚩HTTP Request
上面我们已经展示了什么是http的请求报文的报头部分,不过上面的仅仅是具体的一些字符串,这HTTP的请求的格式如下图所示:
除此之外,http响应格式不同于其他的响应,我们可以看到第一行的请求行当中实际包含了 请求方法,URL,HTTP版本(客户端版本) 这些信息,我们具体来看一看一个Request的报头第一行内容:
- 首行: [方法] + [url] + [版本]
- Header : 请求的属性, 冒号分割的键值对;每组属性之间使用\r\n 分隔;遇到空行表示 Header 部分结束。
- Body : 空行后面的内容都是 Body. Body 允许为空字符串. 如果 Body 存在, 则在Header 中会有一个 Content-Length 属性来标识 Body 的长度。
为什么这里我们只有协议的处理方法与协议的版本信息呢?因为我们写的程序并没有向服务器发起任何的资源访问,所以这里 访问的资源路径默认为 ‘/’ 。而剩下的所有行,也都符合上图中的 Key: Value
的形式,而我们要学习的就是HTTP请求报文中的请求行以及请求报头信息。
我们知道,任何协议都需要有将 报头 和 有效载荷进行分离的能力,那么HTTP协议是根据什么对报头和载荷进行分离的呢?我们发现http请求格式中,报头部分与正文部分实际上是有一个空行作为分隔的,所以 HTTP协议就可以通过空行(“\n”)来分离报头和有效载荷部分。
在http请求报头当中,存在一个 key为 Content-Length
value为 正文长度
的key: value请求行,如果发送的请求当中没有正文,那么这个字段也就不会存在。
🚩HTTP Response
除了发送请求的报文有格式以外,服务器对客户端返回的报头应该也要有响应,那么也就会存在与请求格式大差不差的响应格式:
在状态行当中,包含了HTTP版本(服务端版本),状态码,状态码描述三个字段,其中这里的版本与发送请求的版本不同,这里使用的是服务端的http版本,而状态码我们是非常常见的,比如 404,403… 而状态描述就是指对状态码的描述,比如:404,状态描述就是 Not Found。下图是一个简单的http应答报文:
- 首行: [版本号] + [状态码] + [状态码解释]
- Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\r\n 分隔;遇到空行表示 Header 部分结束。
- Body: 空行后面的内容都是 Body. Body 允许为空字符串. 如果 Body 存在, 则在Header 中会有一个 Content-Length 属性来标识 Body 的长度; 如果服务器返回了一个 html 页面, 那么 html 页面内容就是在 body 中。
为什么http请求和应答当中都必须要有http版本这个字段信息呢?这是因为双方需要确定对方的http的版本信息,以便于使用相同或者近似的协议处理方法去做请求处理与响应。
微信就是一个很好的例子,我们常用手机都会知道,微信每隔一段时间就会更新一次。其实微信每次更新都是小范围的更新,如果没什么问题就逐渐扩大范围,直到达到一定的基数才会实现全部用户进行更新,如果已经更新的用户和没有更新的用户发消息,如果因为版本不同而发不了消息了,这是客户最不愿看到的情况。
所以微信服务器必须有对应老版本信息的处理方法,如果一个老版本的用户向新版本用户发送了信息,就会先检测用户版本信息,如果为老版本,就选择来版本的处理方法来相互通信,这样就能保证不同版本之间可行的通信。
🚩HTTP常见Header
- Content-Type: 数据类型(text/html 等) 。
Content-Type表示资源的数据类型,资源类型必须得提前在代码当中定义好,如果你拿着一个js文件返回给浏览器,但是却告诉浏览器这个文件是html文件,这就可能会导致一些无法预测的问题,不过浏览器还是很聪明的,如果你发来的是js文件,那么浏览器不会解释为html文件的,但是文件类型有非常多,浏览器不能保证每次都能帮你纠正错误,在服务器的代码中,我们有必要将文件后缀做解析,发送正确的文件后缀,至于如何做,我们可以使用 Content-Type对照表 来定位文件类型:
- Content-Length: Body 的长度。
- Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上。
- User-Agent: 声明 用户的操作系统 和 浏览器版本信息。
- referer: 当前页面是从哪个页面跳转过来的。
- Location: 搭配 3xx 状态码使用, 告诉客户端接下来要去哪里访问。
- Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能。
再发送报文或者相应报文的报头当中,都在一个叫做 Connection
的字段,HTTP 中的 Connection 字段是 HTTP 报文头的一部分,它主要 用于控制和管理客户端与服务器之间的连接状态。
核心作用:
管理持久连接:Connection 字段还用于管理持久连接(也称为长连接)。持久连接允许客户端和服务器在请求/响应完成后不立即关闭 TCP 连接,以便在同一个连接上发送多个请求和接收多个响应。
持久连接(长连接):
HTTP/1.1:在 HTTP/1.1 协议中,默认使用持久连接。当客户端和服务器都不明确指定关闭连接时,连接将保持打开状态,以便后续的请求和响应可以复用同一个连接。
HTTP/1.0:在 HTTP/1.0 协议中,默认连接是非持久的。如果希望在 HTTP/1.0上实现持久连接,需要在请求头中显式设置 Connection: keep-alive。
语法格式:
- Connection: keep-alive:表示希望保持连接以复用 TCP 连接。
- Connection: close:表示请求/响应完成后,应该关闭 TCP 连接。
HTTP常见Header表:
字段名 | 含义 |
---|---|
Accept | 客户端可接受的响应内容类型 |
Accept- Encoding | 客户端支持的数据压缩格式 |
Accept- Language | 客户端可接受的语言类型 |
Host | 请求的主机名和端口号 |
User-Agent | 客户端的软件环境信息 |
Cookie | 客户端发送给服务器的 HTTPcookie 信息 |
Referer | 请求的来源 URL |
Content-Type | 实体主体的媒体类型 |
Content-Length | 实体主体的字节大小 |
Authorization | 认证信息,如用户名和密码 |
Cache-Control | 缓存控制指令 |
Connection | 请求完后是关闭还是保持连接 |
Date | 请求或响应的日期和时间 |
Location | 重定向的目标URL(与 3xx 状态码配合使用) |
Server | 服务器类型 |
Last-Modified | 资源的最后修改时间 |
ETag | 资源的唯一标识符,用于缓存 |
Expires | 响应过期的日期和时间 |
🚩URI资源以及网页跳转原因
URI是客户端访问服务器的资源路径,当服务器端收到客户端发来的请求时,如果没有访问任何资源路径,那么默认就是 ‘/’ ,而默认的’/’ 需要有一个默认的首页,以后只要对服务器进行访问,如果没有访问具体的服务器资源,就会默认跳转到给出的默认首页,通常默认首页是一个 index.html/index.htm
文件。而这些文件,通常存在于一个名为 wwwroot
的目录结构下,服务器默认访问资源路径实际上就是wwwroot目录下的 index.html文件。
当然,wwwroot是一个主目录,其中将来可以存在多个子目录,而每个目录都应该有一个默认的首页,比如我们使用 telnet 具体网址 访问协议
,命令来请求百度的默认首页内容:
网页页面之间跳转的功能,是通过链接来进行切换页面的,如下图:
当然不仅仅如此,我们在页面中访问网页,进行页面跳转,就是每一次http请求:
什么是网站?站在我们程序员的角度来说,网站其实就是一堆特定的目录和文件所构成的目录结构!前端程序员并不需要管后端的各种服务是怎么实现的,只需要将wwwroot里的内容做好,进行页面之间的跳转即可。
🚀HTTP其他属性字段
🚩HTTP状态码
我们通过HTTP访问服务器上的资源时,并不是每一都会访问成功,往往面临着多种特殊情况,对于这些特殊情况,被分类为了一下五类状态描述:
最常见的状态码, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)。以下是常见状态码表:
状态码 | 含义 | 应用样例 |
---|---|---|
100 | Continue | 上传大文件时,服务器告诉客户端可以继续上传 |
200 | OK | 访问网站首页,服务器返回网页内容 |
201 | Created | 发布新文章,服务器返回文章创建成功的信息 |
204 | No Content | 删除文章后,服务器返回“无内容”表示操作成功 |
301 | Moved Permanently | 网站换域名后,自动跳转到新域名;搜索引擎更新网站链接时使用 |
302 | Found or See Other | 用户登录成功后,重定向到用户首页 |
304 | Not Modified | 浏览器缓存机制,对未修改的资源返回304 状态码 |
400 | Bad Request | 填写表单时,格式不正确导致提交失败 |
401 | Unauthorized | 访问需要登录的页面时,未登录或认证失败 |
403 | Forbidden | 尝试访问你没有权限查看的页面 |
404 | Not Found | 访问不存在的网页链接 |
500 | Internal Server Error | 服务器崩溃或数据库错误导致页面无法加载 |
502 | Bad GateWay | 使用代理服务器时,代理服务器无法从上游服务器获取有效响应 |
503 | Service Unavaiable | 服务器维护或过载,暂时无法处理请求 |
有关重定向,很多人对此感到陌生,实际上,在我们日常生活当中重定向也是非常常见的,比如我们在观看某讯视频时,看了几分钟之后,就会自动弹出一个页面,让你充值会员。这其实就是一种重定向,以及301永久重定向,有些网站的域名可能换了,但是很多用户还是习惯使用老域名,为了避免这部分用户的流失,所以会对原域名做一个重定向,到新的域名访问资源。
5开头的状态码大家很少见,因为5开头的状态码表示服务器端出现了问题,实际上,就算是服务器端出现了错误,很可能也会被设置为4xx的状态码,也就是转化为客户端错误,如果用户每次访问服务器端都弹出5xx,服务器哪哪发生错误,这绝对会让部分用户不满意,所以很多时候我们并不能看到5xx的状态码,不过这也就能骗一骗外行人了。
以下是仅仅包含重定向相关状态的表格:
状态码 | 含义 | 是否为临时重定向 | 应用样例 |
---|---|---|---|
301 | Moved Permanently | 否(永久重定向) | 网站换域名后,自动跳转到新域名;搜索引擎更新网站链接时使用 |
302 | Found 或 SeeOther | 是(临时重定向) | 用户登录成功后,重定向到用户首页 |
307 | Temporary Redirect | 是(临时重定向) | 临时重定向资源到新的位置(较少使用) |
308 | Permanent Redirect | 否(永久重定向) | 永久重定向资源到新的位置(较少使用) |
HTTP 状态码 301(永久重定向)和 302(临时重定向)都依赖 Location 选项。以下是关于两者依赖 Location 选项的详细说明:
HTTP 状态码 301(永久重定向):
- 当服务器返回 HTTP 301 状态码时,表示请求的资源已经被永久移动到新的位置。
- 在这种情况下,服务器会在响应中添加一个 Location 头部,用于指定资源的新位置。这个 Location头部包含了新的 URL 地址,浏览器会自动重定向到该地址。
- 例如,在 HTTP 响应中,可能会看到类似于以下的头部信息:
HTTP/1.1 301 Moved Permanently\r\n Location: https://www.new-url.com\r\n
HTTP 状态码 302(临时重定向):
- 当服务器返回 HTTP 302 状态码时,表示请求的资源临时被移动到新的位置。
- 同样地,服务器也会在响应中添加一个 Location 头部来指定资源的新位置。浏览器会暂时使用新的 URL 进行后续的请求,但不会缓存这个重定向。
- 例如,在 HTTP 响应中,可能会看到类似于以下的头部信息:
HTTP/1.1 302 Found\r\n Location: https://www.new-url.com\r\n
总结:
无论是 HTTP 301 还是 HTTP 302 重定向,都需要依赖 Location 选项来指定资源的新位置。这个 Location 选项是一个标准的 HTTP 响应头部,用于告诉浏览器应该将请求重定向到哪个新的 URL 地址。
🚩HTTP 的常见方法
HTTP协议存在许多方法,比如获取服务器资源使用GET方法,向服务器发送数据使用POST方法,实际上GET方法也可以提交数据给服务器,只不过跟POST方法有些差别,我们来具体了解一下这些方法:
GET 方法:
- 用途:用于请求 URL 指定的资源。
- 示例:
GET /index.html HTTP/1.1
- 特性:指定资源经服务器端解析后返回响应内容。
POST 方法:
- 用途:用于传输实体的主体,通常用于提交表单数据。
- 示例:
POST /submit.cgi HTTP/1.1
- 特性:可以发送大量的数据给服务器,并且数据包含在请求体中。
PUT 方法(不常用):
- 用途:用于传输文件,将请求报文主体中的文件保存到请求 URL 指定的位置。
- 示例:
PUT /example.html HTTP/1.1
- 特性:不太常用,但在某些情况下,如 RESTful API 中,用于更新资源。
HEAD 方法:
- 用途:与 GET 方法类似,但不返回报文主体部分,仅返回响应头。
- 示例:
HEAD /index.html HTTP/1.1
- 特性:用于确认 URL 的有效性及资源更新的日期时间等。
DELETE 方法(不常用):
- 用途:用于删除文件,是 PUT 的相反方法。
- 示例:
DELETE /example.html HTTP/1.1
- 特性:按请求 URL 删除指定的资源。
OPTIONS 方法:
- 用途:用于查询针对请求 URL 指定的资源支持的方法。
- 示例:
OPTIONS * HTTP/1.1
- 特性:返回允许的方法,如 GET、POST 等。
以上这些方法一般都不是由后端代码来完成的,不过如果想要处理这些请求后端也可以处理,一般这些都属于前端页面的请求方法,我们可以通过 HTML表单 来获取简单的前段页面,而以上方法中最重要的莫过于 GET 和 POST方法了。
其中GET方法我们如何理解呢?就拿简单的登录页面来说,我们登录页面实际上在前端代码中,就是一个form表单:
我们可以看到在搜索框内出现的网址,在前端页面上我们指定了要上传资源的路径,以及使用方法,在网址中,我们看到除了前端页面以外,后面紧跟着的字符是 “?” ,问号后面跟着的起始就是用户的用户名信息,以及用户的密码信息。所以由此我们就可以看到,使用HTTP协议实际上是不安全的,所以催生出了HTTPS协议(下期再谈),更加注重用户的隐私性。
以上是GET上传资源的方式,是通过url来上传资源的。而POST方法上传参数,是以正文形式进行参数上传的, content-length + request body,有人会说,POST比GET更安全,更加保护用户的信息,因为我们在浏览器的网址上看不到,我要说的是,GET和POST都不安全!因为POST方法虽然在浏览器层面看不到用户信息,但是如果你会抓包,实际上所有的用户信息都会被暴漏出去,所以这两种方法都不能称为安全方法。