HTTP: 超文本传输协议(Hyper Text Transfer Protocol)。
- 是一个简单的请求响应,运行在 TCP 之上。规定客户端发送什么样的请求以及得到什么样的响应。
- 请求和相应消息的头是以 ASCII 形式给出;而消息类容则已 MIME 格式
HTTP/0.9 | HTTP/1.0 | HTTP/1.1 | HTTP/2.0 |
---|---|---|---|
1991 | 1991~1996 | 1997~1999 | 2012~2014 |
最新的 HTTP 3 版本也正在完善中, 目前 Google / Facebook 等公司的产品已经支持了.
HTTP 往往是基于传输层 TCP协议 实现的(HTTP3版本用的是UDP)目前我们主要使用的还是 HTTP1.1 和 HTTP2.0
当我们在浏览器输入“CSDN”关键字的时候浏览器就会给CSDN的服务器发送一个HTTP 请求,CSDN的服务器就会返回给浏览器客户端一个HTTP响应
这个响应结果被浏览器解析之后就展示成我们看到的页面内容(这个过程中浏览器可能发会发送多个HTTP请求,服务器就会返回多个响应对象,这些响应对象包含了HTML,CSS,JS,图片,字体等信息)
所谓 “超文本” 的含义, 就是传输的内容不仅仅是文本(比如 html, css 这
个就是文本), 还可以是一些 其他的资源, 比如图片, 视频, 音频等二进制的数据
我们已经知道数据能够通过客户端进程经过路径选择跨网络传送到服务端进程【IP+Port】
可是仅仅是把数据从A送到B就结束了吗?
就好比在淘宝上买一部手机,卖家【客户端】通过顺丰【传输+选择路径】送到买家手里【服务端】就完了吗?
当然不是,还要在手机在体验后对卖家进行评价
所以我们把数据从A端送到B端,TCP/IP解决的事顺丰的事情。而对两端数据进行的加工或使用还需要别一层协议,不关心通信细节,关心应用细节。
这层协议叫做应用层协议。而应用是有不同的场景的,所以应用层协议是有不同种类的,其中经典协议之一的HTTP就是其中的佼佼者.
刚才的例子中,TCP/IP相当于顺丰的功能,附带的说明书指导用户如何使用手机。此时的说明书可以理解为用户层协议。
当我们在浏览器中输入一个“网址”的时候,此时浏览器就会向对应的服务器发送一个HTTP,对方服务器收到这个请求之后,经过计算,就会返回一个HTTP响应
事实上, 当我们访问一个网站的时候, 可能涉及不止一次的 HTTP 请求/响应 的交互过程. 可以通过 chrome 的开发者工具观察到这个详细的过程.
通过 F12 打开 chrome 的开发者工具, 切换到 Network 标签页. 然后刷新
页面即可看到如下图效 果. 每一条记录都是一次 HTTP 请求/响应
注意
当前搜索结果是通过 https 来进行通信的. https 是在 http 基础之上做了一个加密解密的工作。发现传输不仅有文本,还有图片等二进制信息。
这里列举了常用的抓包工具,可以去下载
比如常用的抓包工具,charles,fiddler。
有了工具,如何使用呢?先从原理说起。
抓包相当于一个“代理”
代理就可以简单理解为开在大学宿舍的小卖部,大家半夜都不想出门大老远的跑去附近超市买东西【就近访问国内的网络】,偏远地方可能还没有超市【无法访问外网】,因此就诞生了小卖部。当你想买零食的时候,只需要在小卖部的群聊里发消息说自己需要一罐冰阔乐,小卖部看到了这个请求,立马安对你的冰阔乐请求有了响应,就给你带来了冰阔乐。如果小卖部生意庞大,跨境小电商的话就可以满足你海外物品的需求。
- 当客户端浏览器访问服务器的时候,先把请求数据转给fiddler,fiddler在把请求数据转给服务器
- 服务器响应经fiddler发送过来的浏览器请求,把响应结果发送到客户端,fiddler先获取这份数据,然后再转给浏览器本身。
因此,代理对于浏览器和服务器之间的数据交互非常清楚
利用Chrome查看的网站
摘要是对于网络数据流的大致信息描述
利用Fiddler查看HTTP请求结果
现在的http网站还是有,但是不多。这里用的是我们的政府网为例
发现它是如下结构:
- 首行:方法+URL+版本
- Header:请求的属性,冒号分割键值对,每个键值对用\n隔开
- Body:空行后面的内容是body。如果body存在,则在header中会有一个Content-Length属性来标记Body的长度
- 首行:版本号+状态码+状态码解释
- Header:同HTTP请求一致
- Body:通HTTP请求一致
这个空行会不会是多余的呢?
其实并不是的,虽然在网络传输协议中寸土寸金,仔细发现空行把报头和正文进行了区分。也可以看为报头的结束
HTTP在传输层依赖于 TCP协议 TCP是面向字节流的,如果没有这个空行分割,救护出现 “粘包问题”
打开开发者工具后发现还有信息是被隐藏的
- mp.csdn.net:方便记忆IP地址,因此域名就成了IP地址的别名。可通过DNS解析成为了一个IP地址【通过ping命令即可查看IP地址】
- mp_blog/manage/article:是服务器上的资源路径
- ?:后面跟随的是一些参数,有时候会有一些简单的用户名,查询参数等出现。这里出现的是查询参数
spm=1001.2014.3001.5448
通过ping命令查看ip地址
关于查询关键字query string
query string内容是
键值对
的形式出现的
URL中可以省略的部分
- 协议名称可以省略,省略后默认为:mp.csdn.net/mp_blog/manage/article?spm=1001.2014.3001.5448
- IP地址/域名:在 HTML 中可以省略(比如 img, link, script, a 标签的 src 或者 href 属性). 省略后表示服务器的 IP/域名 与当前 HTML 所属的 IP/域名 一致.
- 端口号:HTTP省略后默认为80;HTTPS省略后默认为443
- 带层次的文件路径: 可以省略. 省略后相当于 / . 有些服务器会在发现 / 路径的时候自动访问 /index.html【相当于绝对路径和相对路径】
- 查询字符串: 可以省略
- 片段标识: 可以省略
像 / ? : 等这样的字符,已经被url当做特殊意义理解了。因此这些字符不能随意出现。如果某个参数需要这些字符就需要进行转义。
一个中文字符经过GBK/UTF-8编码后,虽然放在URL中没有特殊含义,但是仍然需要转义。否则浏览器可能会把GBK/UTF-8编码中某个字节当作特殊字符处理
转换规则
需要将转码的字符转为 16进制,然后从右到左,每两位取【16进制,2位代表一个字节】,然后前面加上 %,形如%XY的格式
搜索C++,在浏览器中C没有被转义,’+’ 号被转义为 ‘%2B’
urldecode就是urlencode的逆过程
URL decode工具
方法 | 说明 | 支持的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 |
现在最常用的就是 1.1
1.0几乎已经看不到了,最新的版本是2.0和3.0但是都没有普及到
GET是最常用的方法,用于客户端从服务器上获取某个资源
网站:https://mp.csdn.net/mp_blog/manage/article?spm=1001.2014.3001.5448
我们是如何触发GET请求的
查看GET请求
我们可以按照上文中HTTP Request请求格式进行分析
发现 GET 请求特点
关于URL长度问题
网上有些资料上描述: get请求长度最多1024kb 这样的说法是错误的.
HTTP 协议由 RFC 2616 标准定义, 标准原文中明确说明:"Hypertext Transfer Protocol -- HTTP/1.1," does not specify any requirement for URL length.
没有对 URL 的长度有任何的限制
实际 URL 的长度取决于浏览器的实现和 HTTP 服务器端的实现. 在浏览器端, 不同的浏览器最大长度是不同的, 但是现代浏览器支持的长度一般都很长; 在服务器端, 一般这个长度是可以配置的.
POST方法也是常用的方法,经常用于用户向服务端提交数据。
最常见的场景就是登陆,输入用户名和密码之后点击登陆就会产生一个POST请求
如何构造POST请求?
观察CSDN的登陆过程中POST方法请求
这里应付上登陆过程中的post请求图片
结合HTTP响应来分析POST请求
GET | POST | |
---|---|---|
语义 | 从服务器获取数据 | 向客户端发送数据 |
body | 一般为空 | 一般不为空(需要的数据通过body传输) |
query string | 一般不为空(需要的数据通过query string传输) | 一般为空 |
请求 | 一般幂等 | 一般不幂等(如果多次请求是一样的就可以视为幂等) |
缓存 | 可以被缓存 | 不能被缓存(继承幂等特性) |
幂等概念
在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。
举例:在淘宝上购物,如果想抢购冰墩墩,用户一直提交下单,一共提交了3次。那么系统是判定用户下了3次单还是1次单,由于幂等,所以是1次单。
揭秘时刻
这些HTTP的请求也可以通过ajax 进行构造(也可以通过第三方工具[Postman])
任何一个能进行网络编程的语言都可以构造 HTTP 请求. 本质上就是通过TCP socket 写入一个符合 HTTP 协议规则的字符串.
以下是常用的报头种类简介
表示服务器主机的地址和端口
表示 body 中的数据长度,单位是字节
表示 body 中的数据格式
常见的有form表单,上传图片,json格式
表示浏览器/操作系统的属性
user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.83 Safari/537.36
早起的网站其实就是一张”报纸“,只有文字图片,没有音频和视频。浏览器也并不发达,有的不支持播放视频。能不能播放就是通过UA来判断当前浏览器支不支持。后来浏览器发展到现在,功能越来越多,所以几乎都支持这些功能。
现在UA的重要功能就是判断当前设备时PC还是移动设备
表示这个页面是从哪个页面跳转过来的,如果直接在浏览器中输入URL, 或者直接通过收藏夹访问页面时是没有 Referer 的。
Cokie中存储了一个字符串。这个数据可能是通过网页中的js写入,也可能通过服务区的HTTP响应的header中的Set-Cooki字段返回给浏览器。
往往通过这个字段实现“身份标示”功能。
一般用于登陆验证,比如登陆了账号后,当访问服务器其它页面的时候,客户端会带着当前的cookie去访问其它网页,就可以不用登陆账号。【刷哔哩哔哩的时候我们访问其它页面就可以不用重复登录账号】
理解登录过程
中途如果Cookie出现变化,HTTP通过Set-Cookie返回新的键值对
那能不能直接让这些令牌数据以文件的形式保存在本地呢?
答案是不可以的,因为浏览器为了保证安全性,禁止网页中的代码访问磁盘【可以把你的磁盘写满,也可以把你的磁盘格式化】。(无法在js中读写文件)也让前端开发失去了持久化能力
Cookie机制存在的目的
是为了能够让浏览器这一端存储程序猿自定义数据。按照键值对的形式进行存储,来代替直接访问文件。这个Cookie的内容是浏览器管理的(本质上也是保存在磁盘上)持久化的。
状态码 | 详解 |
---|---|
1xxx | 信息性状态码,接受的请求正在处理 |
2xxx | 成功状态码,请求处理成功 |
3xxx | 重定向状态码,需要进行额外操作才能完成处理 |
4xxx | 客户端错误状态码,服务器无法处理请求 |
5xxx | 服务器错误状态码,服务器请求处理出错 |
浏览器输入一个 URL, 目的就是为了访问对方服务器上的一个资源. 如果这个 URL 标识的资源不存在, 那么就会出现 404
访问被拒绝,如果用户没有登录就访问则会被拒绝。【gitee的私人仓库必须登陆后可查看,如果没有登录就会出现这种状况】
发送的请求方式对方服务器可能不支持
服务器内部运行出错
服务器负载大,处理单个请求就会消耗很长时间,可能会出现超时现象【12306,双十一等情况会发生】
重定向,当我们登录成功后就会自动跳转到主页,此时就是302
响应报头的基本格式和请求包头一致。类似于Content-Type和Content-Length也一致。
响应中的Content-Type常见取值一般就这几种
服务器返回的HTTP响应大多数都是经过压缩的结果,得解压缩才能看到
<form action="https://www.baidu.com" method="get">
用户名<input type="text" name="username">
密码<input type="password" name="password">
<input type="submit" value="提交">
form>
<form action="https://www.baidu.com" method="post">
用户名<input type="text" name="username">
密码<input type="password" name="password">
<input type="submit" value="提交">
form>
主要区别
在使用之前需要导入JQuery,并且开启服务器
<input type="text" placeholder="输入用户名">
<input type="password" placeholder="请输入密码">
<input type="submit" value="post" onclick="post()">
<input type="submit" value="put" onclick="put()">
<input type="submit" value="del" onclick="del()">
<script>
function post() {
$.ajax({
url: "test",
method: "post",
contentType: "text/html;charset=UTF-8",
success: function (data, status) {
document.write("postSuccess!");
}
})
}
function put() {
$.ajax({
url: "test",
method: "put",
contentType: "text/html;charset=UTF-8",
success: function (data, status) {
document.write("putSuccess!");
}
})
}
function del() {
$.ajax({
url: "test",
method: "del",
contentType: "text/html;charset=UTF-8",
success: function (data, status) {
document.write("delSuccess!");
}
})
}
script>