✨HTTP协议详解+经典面试题
作者介绍:
作者:偷偷敲代码的青花瓷
作者的Gitee:代码仓库
系列文章推荐:
✨计算机网络—网络原理之TCP/IP协议(一)
✨✨我和大家一样都是热爱编程✨,很高兴能在此和大家分享知识,希望在分享知识的同时,能和大家一起共同进步,取得好成绩,今天和大家分享的章节是HTTP协议,如果有错误❌,欢迎指正哟,咋们废话不多说,跟紧步伐,开始学习吧~
文章目录
- HTTP协议
- 理解"应用层协议"
- 理解HTTP协议的工作过程
- HTTP协议格式
- 抓包工具的使用
- 抓包工具的原理
- 抓包结果
- 协议格式总结
- HTTP 请求(Request)
- 认识 URL
- URL 基本格式
- 关于 URL encode/decode
- 认识"方法"(method)
- 1.GET 方法
- 2.POST 方法
- 3.其他方法
- 经典面试题:谈谈 GET 和 POST 的区别
- 认识请求"报头"(header)
- Host
- Content-Length/ Content-Type
- 关于 Content-Length 的补充
- User-Agent(简称 UA)
- Referer
- Cookie
- HTTP 响应详解
- 认识"状态码"(status code)
- 200 OK
- 404 Not Found
- 403 Forbidden
- 405 Method Not Allowed
- 500 Internal Server Error
- 504 Gateway Timeout
- 302 Move temporarily
- 总结:
- 通过 form 表单构造 HTTP 请求
- form发送GET请求
- form发送POST请求
- 通过 ajax构造HTTP请求
- 发送 GET 请求
- 发送 POST 请求
- 通过 Java socket 构造 HTTP 请求
- 总结
HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写。HTTP 协议和 TCP/IP 协议族内的其他众多的协议相同, 用于客户端和服务器之间的通信。请求访问文本或图像等资源的一端称为客户端, 而提供资源响应的一端称为服务器端。
HTTP是一种应用非常广泛
的 应用层协议
HTTP 具体应用场景
我们平时打开一个网站,就是通过 HTTP 协议来传输数据的
当我们在浏览器中输入一个搜狗搜索的"网址"(URL)时,浏览器就给搜狗的服务器发送一个HTTP请求,搜狗的服务器返回一个HTTP响应.
这个响应结果被浏览器解析之后
,就展示成我们看到的页面内容.(这个过程中浏览器可能会给服务器发送多个HTTP请求,服务器会对应返回多个响应,这些响应就包含了页面HTML,CCS,JavaScript,图片文字等信息)
所谓 “超文本” 的含义, 就是传输的内容
不仅仅是文本
(比如 html, css 这个就是文本), 还可以是一些
其他的资源
, 比如图片, 视频, 音频等二进制的数据
我们已经学过 TCP/IP , 已经知道目前数据能从客户端进程经过路径选择跨网络传送到服务器端进程[ IP+Port ].
可是,仅仅把数据从 A 点 传送到 B点就完了吗?
这就好比,在淘宝上买了一部手机,卖家[ 客户端 ]把手机通过顺丰[ 传送+路径选择 ] 送到买家 [
服务器 ] 手里就完了吗?
当然不是,买家还要使用这款产品,还要在使用之后,给卖家打分评论。
所以,我们把数据从A端传送到B端, TCP/IP 解决的是顺丰的功能,而两端还要对数据进行加工处理或者使用,所以我们还需要一层协议,不关心通信细节,关心应用细节!
这层协议叫做应用层协议。而应用是有不同的场景的,所以应用层协议是有不同种类的,其中经典协议之一的HTTP就是其中的佼佼者.
再回到我们刚刚说的买手机的例子,顺丰相当于 TCP/IP 的功能,那么买回来的手机都附带了说
明书【产品介绍,使用介绍,注意事项等】,而该说明书指导用户该如何使用手机【虽然我们都
不看,但是父母辈有部分是有看说明书的习惯的:)】,此时的说明书可以理解为用户层协议
当我们在浏览器中输入一个 “网址”, 此时浏览器就会给对应的服务器发送一个 HTTP 请求. 对方服务器收到这个请求之后, 经过计算处理, 就会返回一个 HTTP 响应.
事实上当我们访问一个网站的时候,可能涉及不止一次的HTTP 请求/响应 的交互过程.
可以通过 chrome 的开发者工具观察到这个详细的过程.
通过F12 打开 chrome 的开发者工具,切换到 Network 标签页,然后刷新页面即可看到如下图效果,每一条记录都是一次 HTTP 请求/响应
当前 搜狗主页 是通过 https 来进行通信的. https 是在 http 基础之上做了一个加密解密的工作, 后
面再介绍.
HTTP 是一个文本格式的协议. 可以通过 Chrome 开发者工具或者 Fiddler 抓包, 分析 HTTP 请求/响应的细节.
view in Notepad
通过记事本打开Fiddler 使用小技巧
可以使用
ctrl + a
全选左侧的抓包结果,按delete
键清除所有被选中的结果
Fiddler 相当于一个 “代理”
浏览器访问 sogou.com 时, 就会把 HTTP 请求先发给 Fiddler, Fiddler 再把请求转发给 sogou 的服务器. 当 sogou 服务器返回数据时, Fiddler 拿到返回数据, 再把数据交给浏览器.
因此 Fiddler 对于浏览器和 sogou 服务器之间交互的数据细节, 都是非常清楚的.
代理就可以简单理解为一个跑腿小弟. 你想买罐冰阔落, 又不想自己下楼去超市, 那么就可以把钱给
你的跑腿小弟, 跑腿小弟来到超市把钱给超市老板, 再把冰阔落拿回来交到你手上. 这个过程中, 这
个跑腿小弟对于 “你” 和 “超市老板” 之间的交易细节, 是非常清楚的^^
以下是一个 HTTP 请求/响应 的抓包结果.
因为
HTTP 协议并没有规定报头部分的键值对有多少个. 空行就相当于是 "报头的结束标记"
, 或者
是"报头和正文之间的分隔符".
HTTP 在传输层依赖 TCP 协议, TCP 是面向字节流的.如果没有这个空行, 就会出现 "粘包问题"
前言:
Internet上的每一个网页都具有一个唯一的名称标识,通常称之为URL(Uniform Resource Locator, 统一资源定位器)。它是www的统一资源定位标志,简单地说URL就是web地址,俗称“网址”。
URL
URL是对互联网上得到的资源的位置和访问方法的一种简洁表示,是互联网上标准资源的地址。URL它具有全球唯一性,正确的URL应该是可以通过浏览器打开此网页的,但如果您访问外网,会提示网页无法打开,这并不能说明这个URL是错误的。只不过在国内不能访问而已。
https://blog.csdn.net/Biteht?spm=1010.2135.3001.5421
以上是我CSDN博客的URL地址。访问百度网站时,你的浏览器上就会显示:http//www.baidu.com,如下图
URL小结:
对于URL 来说,里面的结构看起来复杂,其实最重要的,和开发关系紧密的,就四个部分
:
- ip 地址/域名
- 端口号(经常是小透明)
- 带层次结构的路径
- query string 查询字符串
当 query string 中如果包含了特殊字符,就需要对特殊字符进行转义(url encode)
转义过程
叫做 url encode
转义后的内容还回来
url decode
url 里面是有很多特殊含义符号的
/ : & = ...
这些符号都是在 URL 中具有特定含义的~
注意:如果,query string 里也包含了这类特殊符号,就可能导致 URL 被解析失败
比如:当我们在sogou 中搜索 C++ 的时候,就可以看到,URL中的 query string 里面有一个键值对,就表示了查询词内容
这里的%2B是在骂人吗?显然不是!!!%2B 其实就是 通过 url encode 转义得到的结果
HTTP协议的方法非常多!但是最常用的,也就是 GET 和 POST
HTTP 中引入这些方法的初衷是为了表示不同的"语义"(语义:是否有特定的含义)
比如在 HTML 里,h3,p,a,img...
语义化标签div span
无语义标签
GET 是最常用的 HTTP 方法. 常用于获取服务器上的某个资源
使用 Fiddler 观察 GET 请求:
打开 Fiddler,访问 搜狗主页,观察抓包结果
在上面的结果中可以看到:最上面的
是通过浏览器地址栏发送的 GET 请求.选中它并观察请求的详细结果:
GET 请求的特点:
- 首行的第一部分为 GET
- URL 的 query string 可以为空,也可以不为空
- header 部分有若干个键值对结构
- body 部分为空
POST 方法也是一种常见的方法. 多用于提交用户输入的数据给服务器(例如登陆页面).
使用 Fiddler 观察 POST 方法
在Gitee登陆页面, 输入用户名, 密码, 验证码之后, 点击登陆, 就可以看到 POST 请求.
查看它的具体请求:
POST 请求的特点:
- 首行第一部分为 POST
- URL 的 query string 一般为空(也可以不为空)
- header 部分有若干个键值对结构
body 部分一般不为空,body 内的数据格式通过 header 中的 content -Type 指定 body 的长度由 header 中的 content-Length 指定
当面试官问到 GET 和 POST 的区别时:
第一句话,先盖棺定论!!GET 和 POST 没有本质区别!!(具体来说,相当于是 GET 能使用的场景,也能天成POST,POST 使用的场景,也能替换成 GET),但是在细节上还是有一些区别的
- 语义上的区别:
GET 通常用来读取数据 POST 通常用来上传数据
现状是 GET 也经常用来上传数据,POST用来读取数据- 通常情况下,GET 是有没有 body,GET 通过 query string 向服务器传递数据
通常情况下, POST 是有 body,POST 通过 body 向服务器传递数据,但是 POST 没有 query string
注意
:如果我就想让 GET 有 body(自己构造一个带 body的 GET请求)或者就想让 POST 带有 query sting 是完全可以的- GET 请求一般是幂等的,POST 请求一般是不幂等的(也不是强制要求,而是建议)
幂等
:每次你相同的输入,得到的输出结果是确定的
不幂等
:每次你相同的输`,得到的结果是不确定的- GET 可以被缓存,POST 不能被缓存(提前把结果记住,如果是幂等的,记住结果是很有用的,节省了下次访问的开销,如果不是幂等的,就不应该去记)
header 的整体的格式也是
"键值对"结构
,每个键值对占一行,看个值之间使用分号
分割
表示服务器主机的地址和端口
Content-Length
:表示 body中的数据长度
Content-Type
:表示请求
的 body 中的 数据格式
注意:这两个属性是在描述 body,如果你的请求里就没有 body(GET),也就不需要这两个字段了
一般POST都是带body,一般登录系统都是基于 POST 来实现的,我们抓个包具体来观察一下:
点击:
我们可以看到:
这里注意有两个问题:
1.为什么登录系统是使用POST实现?使用GET能不能实现登录呢
使用 GET 是完全可以实现登录的
2.为什么还主要使用 POST 呢?
登录肯定就会给服务器 传递用户和密码,如果是 GET,用户名密码习惯上就会
放到 URL 的 query string 中来传递
(此时浏览器的地址栏里的路径,就可能变的很长一串
)这个时候用户体验可能就不太好
网上有种说法:使用 POST 实现登录,是因为 POST 比 GET 更安全
对于这个说法是大错特错的,
安不安全取决于你的数据是否明文传输,是否是加密过的
第三种最常见请求中的 body 的格式:json
body格式如下:
HTTP 也是基于 TCP 的协议,TCP 是一个面向字节流的协议,粘包问题 ==> 合理设计应用层协议,来明确 包和包 之间的边界:
- 使用分隔符
- 使用长度
表示的是,当前用户是在拿一个啥样的东西来上网
表示了当前的页面,是从哪个页面,跳转过来的
注意
:Referer 不一定是有的,如果你是通过浏览器地址栏直接输入 地址,或者直接点收藏夹,这个时候是没 referer 的
Cookie 就是浏览器给页面提供的一种
能够持久化存储数据的机制
持久化指的是:数据不会因为程序重启或者主机重启,而丢失(写到磁盘里)
Cookie 具体的组织形式:
- 先按照域名来组织,针对每个域名,分别分配一个小房间
就好比如:我访问搜狗,浏览器就会给 sogou 这个域名记录一组cookie,我访问码云,浏览器也就会给 码云一组 cookie- 一个小房间里面,又会按照 键值对 的方式来组织数据
Cookie 数据从哪里来的?
其实是服务器返回给客户端的
下图为抓包实例:
再举个通俗易懂的例子方便我们对 Cookie 的理解:
虽然就诊卡上面可以存储一些信息,但是保存的数量是有限的,真正保存我的这心信息,并不是这个卡,而是放到医院的服务器上,而卡上,只需要存储我的一个身份标识(存储一个用户id),这些关键信息,存储在服务器上,管这个东西称为 "session"会话
状态码表示访问一个页面的结果(是访问成功,还是失败,还是其他的一些情况)
这是一个最常见的状态码,表示访问成功
要访问的资源不存在
虽然资源有,但是没有权限使用
这个情况,你去外面的网站上抓包,很难遇到…
但是如果后面自己写网站后台,这个就很容易出现
例如:尝试使用 GET 来访问人家的服务器,但是可能人家只支持 POST,于是就返回405
服务器自己出问题了,意味着出现 bug,外面的服务器上看到这种情况的概率也是比较低的
但是我们后面自己写代码,也是容易出现这种情况
服务器太忙了
在登录过程中,非常典型的情况
重定向:这个词在计算机的很多常见中都会涉及到,不仅仅是 HTTP,但是虽然在不同场景中细节有差异,但是表示的核心含义,都是呼叫转移
比如呼叫转移–>中国移动,运营商这里可以办理一个业务,有人拨打我的旧号码,就会自动的 转接到 我的新号码上
上面就介绍了 最常见的一些状态码,但是实际上 HTTP 状态码,种类非常多!
form(表单)是HTML中的一个常用标签,可以用于给服务器发送 GET 或者 POST 请求
注意
:切勿把form
拼写成from
!
form 的重要参数
:
- 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 就对应了按钮上显示的文本
下列为具体代码实现以及图解:
当我们在输入框输入内容点击提交的时候页面会跳转到我们之前 action 指定的 URL
使用 form 发送 POST 请求 和 使用 form 发送 GET 请求步骤一样,我们只需要将 method = "GET" 换成 method = "POST"
在学习 ajax 之前,我们先了解一下,为什么要使用它?
form表单这种方式,是一个更加原始的构造方法,使用 form 一定会涉及到"
页面跳转
",浏览器需要加载出一个全新页面,这个事情就是非常不科学的了,尤其是页面非常复杂的时候~
随着前端页面越来越复杂,就希望,能够让页面不去整个全部加载,而是只加载其中需要变化的某个小部分
,这个情况,就可以使用 ajax了
ajax 全称 Asynchronous Javascript And XML
ajxa 属于 基于异步等待的方式来进行的,下面给大家 弄清楚几个 概念
在发送 GET 请求之前 先引入 jquery
- 现在搜索引擎中搜索 jquery cdn 查询词
- 在结果中,找到一个 合适的 cdn 的
url
- 打开对应的 url,加载出 jquery 本体
- 复制粘贴内容到本地文件
下面为具体代码以及图解:
通过刚才 ajax 的请求,我们抓包看到,响应里面 是200OK,并且 body也是 html数据
但是浏览器仍然认为这是一个"出错"的请求
出现这个报错的原因,是浏览器禁止 ajax 进行 跨域访问(跨越多个域名/多个服务器)
当前 页面所处在的服务器,是本地文件
页面中 ajax 请求的URL 域名是 www.sougou.com
这样就触发了 跨域操作
什么样才不算是跨域呢?
当前页面所在的服务器,就是在 www.sogou.com中
页面中再通过ajax,请求 URL,域名为 www.sogou.com 这种就不算跨域
发送 POST 请求方法和代码同上,只是将 type:'get' 改为 'post'
即可,这里不再具体演示
java 构造一个 HTTP 请求,主要就是基于 TCP socket,按照 HTTP 请求的报文格式,构造出一个匹配的字符串,再写入 socket 即可
在实际研发过程中,确实也会有一些基于 java构造 http请求的情况,可以直接使用第三方库来实现,不一定非得要直接使用scoket
具体就演示了~
代码如下:
public class HttpClient {
private Socket socket;
private String ip;
private int port;
public HttpClient(String ip, int port) throws IOException {
this.ip = ip;
this.port = port;
socket = new Socket(ip, port);
}
public String get(String url) throws IOException {
StringBuilder request = new StringBuilder();
// 构造首行
request.append("GET " + url + " HTTP/1.1\n");
// 构造 header
request.append("Host: " + ip + ":" + port + "\n");
// 构造 空行
request.append("\n");
// 发送数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write(request.toString().getBytes());
// 读取响应数据
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[1024 * 1024];
int n = inputStream.read(buffer);
return new String(buffer, 0, n, "utf-8");
}
public String post(String url, String body) throws IOException {
StringBuilder request = new StringBuilder();
// 构造首行
request.append("POST " + url + " HTTP/1.1\n");
// 构造 header
request.append("Host: " + ip + ":" + port + "\n");
request.append("Content-Length: " + body.getBytes().length + "\n");
request.append("Content-Type: text/plain\n");
// 构造 空行
request.append("\n");
// 构造 body
request.append(body);
// 发送数据
OutputStream outputStream = socket.getOutputStream();
outputStream.write(request.toString().getBytes());
// 读取响应数据
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[1024 * 1024];
int n = inputStream.read(buffer);
return new String(buffer, 0, n, "utf-8");
}
public static void main(String[] args) throws IOException {
HttpClient httpClient = new HttpClient("42.192.83.143", 8080);
String getResp = httpClient.get("/AjaxMockServer/info");
System.out.println(getResp);
String postResp = httpClient.post("/AjaxMockServer/info", "this is
body");
System.out.println(postResp);
}
}
“种一颗树最好的是十年前,其次就是现在”
所以,
“让我们一起努力吧,去奔赴更高更远的山海”
如果有错误❌,欢迎指正哟