
什么是HTTP?
HTTP全称为“超文本传输协议”,是一种应用非常广泛的应用层协议。
HTTP往往是基于传输层的 TCP协议实现的。(HTTP1.0 / HTTP1.1 / HTTP2.0 均为TCP,HTTP3基于UDP实现)
目前我们主要使用的还是HTPP1.1和HTTP2.0。
本文讨论的HTTP以1.1版本为主。
浏览器与服务器之间的交互就是通过HTTP协议来完成的。
“超文本”的含义:
指传输的内容不仅仅是文本,还可以是一些图片、视频、音频等二进制数据。
HTTP协议包含HTTP请求和HTTP相应。
当我们在浏览器中输入一个“网址”,此时浏览器就会给对应的服务器发送一个HTTP请求,服务器收到这个请求之后,就会做出对应的反应,形成一个HTTP响应,发送回给客户端,客户端就可以通过这个响应来获取内容。

在这里给大家推荐一个简单易用的抓包工具Fiddler,这是一款专门针对HTTP协议的抓包工具。下载网址:https://www.telerik.com/fiddler
安装过程简单,一直next即可。
Fiddler 界面,左侧显示了所有抓取到的包,选中其中一条双击即可在右侧查看。

注意切换到Raw标签,看原始的内容。
使用Ctrl+a全选左侧全部抓包结果,再按delete键即可清除结果。
Fiddler 相当于一个 “代理”。
当我们要访问某个主页时,就会先把请求发给这个代理,再由代理将请求转发给相应的服务器,响应时也是一样经由代理进行转交。
因此,这个代理对于客户端和服务器之间交互的数据细节,就可以非常清楚。
这个角色有点类似于购物中的代购~,代购就对客户要买的东西和商家的价格等等情况搞得非常清楚噢!
下面我们分别从请求和响应来认识HTTP的格式:
请求格式主要分为四大部分:请求行、请求报头、空行、请求正文。
主要格式如下:

问题:为什么需要使用空行对报头和正文进行分割?
因为HTTP在传输层依赖的是TCP协议。而TCP是面向字节流的,如果没有这个空行,就会出现粘包问题。(就分不清报头和正文啦!)
继续提问:那这样的话包和包之间如何分割呢?
如果有正文的话,那么报头里面会有表示正文长度这个属性噢!~

- GET方法是最常用的HTTP方法。在浏览器中直接输入URL,浏览器就会发送一个GET请求,此外link、img、script等标签也会触发GET请求。特点:GET请求的正文部分为空。
- POST方法多用于提交用户输入的数据给服务器(例如登录页面)。特点:URL的查询字符串一般为空,正文部分一般不为空。
幂等:相同的输入,相同的输出。
平时我们常称的“网址”其实就是URL!全称为统一资源定位符((Uniform Resource Locator )。
它指出了文件的位置以及浏览器应该怎么处理它。
互联网上的每一个文件都有一个唯一的URL噢~
URL的基本格式为:
协议名+“😕/”+登录信息(认证)+服务器地址+服务器端口号+带层次的文件路径+查询字符串+片段标识符
一个具体的URL:(仅供参考,打不开的哈~)

可以看到,这个URL中的信息并不是完全按照上面的基本格式来写的,其中有些信息被省略了。
如:
登录信息、端口号和片段标识。
- 现在的网站一般不通过URL进行身份认证,因此一般都会省略。
- 当端口号被省略的时候,浏览器会根据协议类型自动决定使用哪个端口,如http协议默认使用80端口,https协议默认使用443端口。
- 片段标识主要用于页面内跳转。
关于查询字符串(query string)
这部分内容完全由设计这个页面的程序员来定,我们可以通过这样的方式来自定义传输我们需要的信息给服务器。
URL中可以省略的部分
协议名、ip地址/域名(当服务器与当前HTML所属ip/域名一致时)、端口号、带层次的文件路径、查询字符串、片段标识。
像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现。
如果某个参数中需要带有这些特殊字符,就需要先对特殊字符进行转义。
一个中文字符由 UTF-8 或者 GBK 这样的编码方式构成, 虽然在 URL 中没有特殊含义, 但是仍然需要进行转义. 否则浏览器可能把 UTF-8/GBK 编码中的某个字节当做 URL 中的特殊符号.
转义的规则:转义的规则如下: 将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式
例如我们百度c++时:

c++变成了c%2B%2B。
其中+的ASCII码的十六进制即为2B。
urldecode 就是 urlencode 的逆过程。
报头中是一些键值对,不同的键值对表示了不同的含义。
下面介绍一些常见的键值对。
Host:表示服务器主机的地址和端口.
Content-Length:表示 body 中的数据长度.
Content-Type:表示请求的 body 中的数据格式.
常见选项:
- application/x-www-form-urlencoded: form 表单提交的数据格式。 此时 body 的格式形如键值对,格式同URL中的键值对。
title=test&content=hello
- multipart/form-data: form 表单提交的数据格式(在 form 标签中加上enctyped=“multipart/form-data” . 通常用于提交图片/文件。
Content-Type:multipart/form-data; boundary=----
WebKitFormBoundaryrGKCBY7qhFd3TrwA
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="text"
title
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png
PNG ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--
- application/json: 数据为 json 格式。bode格式也是键值对的形式,键值对之间用逗号分割,键和值之间用冒号分割。
{"username":"123456789","password":"xxxx","code":"jw7l","uuid":"d110a05ccde64b16a861fa2bddfdcd15"}
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36
其中 Windows NT 10.0; Win64; x64 表示操作系统信息;
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36 表示浏览器信息
Referer 表示这个页面是从哪个页面跳转过来的
Cookie 给浏览器提供持久化存储数据的机制
类似看病时使用就诊卡,通过就诊卡,医生就可以看到你之前的就诊信息。类似一个身份标识。
组织形式:键值对
数据来源&数据储存者:服务器(就诊信息是保存在医院的系统中的,只是就诊卡是保存在患者自己手中)
我们可以在浏览器URL前面的小锁中找到cookie。


我们也可以自己把cookie删除。

状态码表示访问一个页面的结果。(访问成功/失败/其他)
下面介绍一些常见的状态码:
200 OK
这是一个最常见的状态码, 表示访问成功。
404 Not Found
没有找到资源。
403 Forbidden
表示访问被拒绝/没有权限访问。
405 Method Not Allowed
HTTP 中所支持的方法, 有 GET, POST, PUT, DELETE 等。但是并非所有的服务器都支持所有的方法(或者有些服务器不允许用户使用其他方法)
500 Internal Server Error
服务器出现内部错误。一般是服务器的代码执行过程中遇到了一些特殊情况(服务器异常崩溃)。
504 Gateway Timeout
当服务器负载比较大的时候,服务器处理单条请求的时候消耗的时间就会很长, 可能会出现超时的情况。
302 Move temporarily
临时重定向。在登陆页面中经常会见到 302,用于实现登陆成功后自动跳转到主页。响应报文的 header 部分会包含一个 Location 字段,表示要跳转到哪个页面。
301 Moved Permanently
永久重定向。当浏览器收到这种响应时,后续的请求都会被自动改成新的地址。
301 Moved Permanently
永久重定向. 当浏览器收到这种响应时, 后续的请求都会被自动改成新的地址
状态码小结

响应报头格式和请求报头格式基本一致。
这里主要介绍Content-Type。
text/html : body 数据格式是 HTML
text/css : body 数据格式是 CSS
application/javascript : body 数据格式是 JavaScript
application/json : body 数据格式是 JSON
form (表单) 是 HTML 中的一个常用标签. 可以用于给服务器发送 GET 或者 POST 请求。
重要参数:
action:构造的 HTTP 请求的URL是什么。
method:构造的 HTTP 请求的方法是GET还是POST(form 只支持 GET 和 POST)。
input 的重要参数:
type:表示输入框的类型。text表示文本,password 表示密码,submit 表示提交按钮。
name:表示构造出的 HTTP 请求的 query string 的 key。query string 的 value 就是输入框的用户输入的内容。
value:input 标签的值。对于type为submit类型来说,value 就对应了按钮上显示的文本。
示例:
Document

随便怎么输入,只要我们点击提交,此时就会构造出 HTTP 请求并发送出去,浏览器就会跳转到百度页面了~

在跳转后的URL中,我们可以看到此处的query string正是页面提交给服务器的数据~
在这里我们只是将请求直接提交给百度主页了,但是百度并没有处理我们提交的参数。后续如果我们自己写服务器,我们就可以针对前端提交的参数进行处理,以实现不同的功能啦!~
form 表单构造的请求一般会涉及到“页面跳转”。
从前端角度,另一种构造HTTP请求并且功能强大的方法还有ajax的方式。
ajax 全称 Asynchronous Javascript And XML,是 2005 年提出的一种 JavaScript 给服务器发送HTTP 请求的方式。
特点是可以不需要 刷新页面/页面跳转 就能进行数据传输。
构造示例:
$ (这是一个变量名)是jQuery中最核心的对象,jQuery中的各种api,都是通过$来触发的。这里我们通过没原来调用ajax函数,参数只有一个,但这个参数是一个“对象”
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<!-- 引入jQuery -->
<script src="../博客系统/js/jquery.min.js"></script>
</head>
<body>
<!-- <form action="http://baidu.com" method="get"> -->
<!-- 搭配form中的其他标签,可以提交数据 -->
<!-- <input type="text" name="userID">
<input type="password" name="password"> -->
<!-- 使用提交按钮来触发请求 -->
<!-- <input type="submit" value="提交">
</form> -->
<script>
$.ajax({
type:'get',
url:'http://www.baidu.com',
success:function(body) {
// success对应一个回调函数
// 这个函数就会在正确获取到HTTP相应之后,调用这个回调函数
// 回调函数的参数,就是HTTP响应的部分
console.log("获取到响应数据!" + body);
},
error:function() {
// error也是一个回调函数
// 在请求失败后触发
console.log("获取响应失败!");
}
});
</script>
</body>
</html>
保存代码,查看结果。

可以看到获取相应失败了,我们抓包看一下相应结果。

从抓包结果中,可以看到刚才的ajax请求的响应是200,并且body中也是html的数据,但浏览器仍然认为只会一个“出错”的请求。
这是因为浏览器禁止ajax进行跨域访问。
跨域访问:跨越多个域名/多个服务器
当前页面出在的服务器,是本地文件,页面中ajax请求的url域名是www.baidu.com,因此触发了跨域操作。
如何绕过浏览器的限制?
在服务器返回的响应中带有相关的响应头,则浏览器就可以正常显示啦!(当然这就需要我们拥有操作服务器的权限啦!如果我们拥有一个服务器,就能让页面和ajax的地址都是这一个服务器,这样就可以正常显示了)
发送 HTTP 请求本质上是按照 HTTP 的格式往 TCP Socket 中写入一个字符串。
而接受 HTTP 响应本质上就是从 TCP Socket 中读取一个字符串,再按照 HTTP 的格式来解析。
我们基于 Socket 的知识,也可以构造出一个简单的 HTTP 客户端程序,用来发送各种类型的 HTTP 请求。
在实际开发中,也有使用java来构造http请求的情况,但是可以直接基于第三方库来实现,不一定非得直接使用socket,因此这里就不做介绍了~
HTTPS是HTTP协议的孪生兄弟,它在HTTP的基础上,引入了加密层。
为什么要引入加密?
大家有听过所谓的“运营商劫持”吗?比如我们想下载某款软件,结果点击下载链接后,弹出的下载框中下载的内容却变成了某个浏览器的下载链接。在这个故事中,运营商属于客户端和服务器中间的设备,当我们想下载软件时,就要想服务器发出HTTP请求,然后获得的响应中就包含了我们想要的内容,而营运商发现之后,就可能把响应中的内容进行更改,于是出现了上述的结果。
因此,引入HTTPS,对数据进行加密,是为了更好地保护数据安全,防止数据被随意篡改。
加密就是把明文(要传输的信息)进行一系列变换,生成密文。
解密就是把密文再进行一系列变换,还原成 明文。
在这个加密和解密的过程中,往往需要一个或者多个中间的数据,辅助进行这个过程,这样的数据称为密钥。

加密解密到如今已经发展成一个独立的学科:密码学。
其奠基人, 是计算机科学的祖师爷之一:艾伦·麦席森·图灵。
加密的方式有很多,但是整体可以分成两大类:对称加密和非对称加密。
对称加密其实就是通过同一个“密钥”,把明文加密成密文,并且也能把密文解密成明文。

引入对称加密之后,即使数据被截获,由于黑客不知道密钥是啥,因此就无法进行解密,也就不知道请求的真实内容是啥了。
但这并不能保证绝对的安全!
因为密钥也需要通过网络传输,也有可能被窃取到!
并且不同的客户需要使用不同的密钥,这样就需要保存很多串钥匙,试想想,学校的保安大哥每个楼都有一大串钥匙,带着是不是很不方便,并且还要妥善保管好钥匙,更何况服务器面对的庞大的客户端群体呢?
因此还需要引入非对称加密!
非对称加密需要使用两个密钥,一个为“公钥”,一个为“私钥”。
公钥和私钥是配对的,最大的缺点就是运算速度非常慢,比对称加密要慢很多。
通过公钥对明文加密, 变成密文
通过私钥对密文解密, 变成明文
也可以反着用
通过私钥对明文加密, 变成密文
通过公钥对密文解密, 变成明文
如何理解非对称加密?这里涉及到一些数论相关的知识。
举个例子,我们可以很容易得到324*256=82944,但是却很难通过2944得到324和256这两个数,因此就形成了一种“非对称”的状态。此时82944就可以作为一个公开的公钥、324和256作为一个私钥来使用,我们将公钥交给对方对消息进行加密,我们得到对方传递过来的密文之后再用自己的私钥来对密文进行解密。

下面举个例子来帮助理解~
小明和小红通过一个盒子传递东西,为了保证安全,小红和小明进行了约定:
小红说:我这里有一把锁和对应的钥匙,我把锁给你,你把你要传递的东西放到盒子里后,用这个锁上锁,然后传给我,我再用我手中的钥匙把锁解开。
这里小红给小明的锁就相当于公钥,而小红自己手中的钥匙就相当于私钥~
理解了以上的流程之后,我们就需要提出两个关键的问题:
客户端如何获取到公钥呢?
客户端又如何确保这个公钥不是黑客伪造的呢?
这两个问题可以导致一个典型的“中间人攻击”:
小红需要先把锁给到小明,如果在给小明的过程中,被中间人偷偷换成了自己的锁,再传给小明。
而小明并不知道啊这是被中间人换了的锁,下一次小明要给小红传纸条的时候,使用的是这把被替换了的锁。
中间人再用自己的钥匙把锁解开,看到了盒子里面的纸条内容,然后再把盒子上的锁换成小红给小明的,再传递给小红。
这样中间人既可以获取到盒子中的内容,又能很好的伪装自己!
为了防止上述的“中间人攻击”,于是人们又引入了证书的机制。
通过证书来保证这把“锁”确确实实是小红给的那把,而不是中间人偷换的。
客户端和服务器在最初建立连接的时候,服务器就给客户端返回一个证书,这个证书包含刚才的公钥,也包含了网站的身份信息。
这个证书就类似一个身份证,作为一个身份的保证,而这个身份的保证需要有一个权威机构作为背书(比如公安局办的身份证才是得到认可的)
当客户端获取到这个证书之后,就会对证书进行校验(防止证书是假的)
判定证书是否过期
判定证书的发布机构是否受信任(操作系统已经内置了受信任的证书发布机构)
验证证书是否被篡改(这个过程就好像我们验证一个人的签名是否是别人仿造的,如果是仿的,则总会有不一样的端倪)

下面简要介绍一下HTTPS的工作过程。
总的来说:HTTPS 工作过程中涉及到的密钥有三组:
第一组(非对称加密):用于校验证书是否被篡改。
服务器持有私钥(私钥在注册证书时获得),客户端持有公钥(操作系统包含了可信任的 CA 认证机构有哪些,同时持有对应的公钥)。服务器使用这个私钥对证书的签名进行加密。客户端通过这个公钥解密获取到证书的签名,从而校验证书内容是否是篡改过。
第二组(非对称加密):用于协商生成对称加密的密钥。
服务器生成这组私钥-公钥对,然后通过证书把公钥传递给客户端。然后客户端用这个公钥给生成的对称加密的密钥加密,传输给服务器,服务器通过私钥解密获取到对称加密密钥。
第三组(对称加密):客户端和服务器后续传输的数据都通过这个对称密钥加密解密。
以上就是本文关于HTTP协议的全部内容啦~
希望对你有帮助噢~~
另外,如果你觉得本文有用,记得给个赞呀~(一键三连更直接哟!)
您的支持是对我最大的鼓励哟!~
