【JavaEE】 HTTP协议(1.0)

HTTP协议介绍

HTTP (全称为 "超文本传输协议") 是一种应用非常广泛的 应用层协议,我们在生活中经常用到。
举个HTTP在日常生活中的用例:(打开一个网站)

我们通过浏览器来访问学校的教务系统时候,我们输入一串网址的时候,此时就是通过HTTP的方式和学校教务系统的服务器进行交互,浏览器给学校教务系统的服务器发送一个HTTP请求,然后学校服务器给浏览器回一个HTTP响应。当这个响应被浏览器解析后,浏览器上就会显示页面内容(这个过程中浏览器可能会给服务器发送多个 HTTP 请求, 服务器会对应返回多个响应, 这些响应里就包含了页面 HTML, CSS, JavaScript, 图片,字体等信息
所谓 "超文本" 的含义, 就是传输的内容不仅仅是文本(比如 html, css 这个就是文本), 还可以是一些其他的资源, 比如图片, 视频, 音频等二进制的数据
虽然HTTP的作用很大,应用很广,但是实际开发中,不一定是真的直接使用HTTP,更大的概率是使用HTTPS(本质上是HTTP引入的加密层,s指安全,即叫做"超文本安全传输协议")。很少直接使用HTTP

HTTP 往往是基于传输层的 TCP 协议实现的. (HTTP1.0, HTTP1.1, HTTP2.0 均为TCP, HTTP3 基于 UDP实现)

目前我们主要使用的还是 HTTP1.1 和 HTTP2.0 . 该文章主要讲述HTTP1.1

Fiddler抓包

当我们在浏览器中输入一个 "网址",此时浏览器就会给对应的服务器发送一个HTTP 请求。 对方服务器收到这个请求之后,经过计算处理, 就会返回一个 HTTP 响应。也就是典型的"一问一答"模式,请求和响应是一一对应的。

事实上, 当我们访问一个网站的时候, 可能涉及不止一次的 HTTP 请求/响应的交互过程,会有超多的请求。那么要想进一步的理解HTTP协议工作过程,以及理解HTTP的报文格式,就需要用到"抓包工具",这样才能更好了解HTTP协议

HTTP抓包工具种类有很多,比如 wireshark,它功能非常强大,使Fi用起来也比较复杂的,用这个也能抓 HTTP, 但是不太方便。

这里我们推荐使用Fiddler,它是专门抓HTTP的工具
打开Fiddler

页面左侧包含你主机上所有进程http请求/响应数据,右侧是对左侧某个数据包双击之后的一个详细信息

一个网页打开的时候,往往不只是和服务器进行一次操作,是很多次操作,比如我们对csdn来进行抓包,这些结果都是浏览器打开csdn的时候,给csdn发送的http/https的请求数据。这里很多数据我们主要关注的是蓝色的数据,其他可以不用关心

现在我们就可以观察蓝色数据的报文格式,右侧上方的是请求报文,我们点击raw则可以观察到原始的请求数据,如果觉得字体小了还可以点击view in notepad(右下角)

右侧下方则是响应报文,这里有个疑问,为什么看不懂该报文,这是因为此时它是二进制协议报文,所以看不懂数据。

但是HTTP 协议是文本格式的协议,TCP, UDP, IP才是二进制格式的协议,这里为什么好端端的文本格式协议变为二进制格式的协议(文本格式协议和二进制格式协议区别在于看不看得懂内容),这是因为HTTP 响应通常会被压缩为二进制格式的协议,压缩之后体积变小,传输的时候节省网络带宽(一台服务器最贵的就是网络带宽)

此时想要看清楚原本的内容我们只需点击黄色的内容即可解压,变为文本格式协议内容,之后的操作就跟请求报文时一样,不多说了。

除此以外还有一个小技巧:可以使用 ctrl + a 全选左侧的抓包结果, delete 键清除所有被选中的结果(快捷键)
但这里要注意一点,现在用的大多都是hhtps,而目前的fidder只能抓取http,我们要设置一下才能捕获https。

点击Options里面的HTTPS选项,然后将里面的选项全部打勾

这里值得注意的是,它会出现一个提示框,提示你是否安装一个证书,这里一定要选择是,如果不小心选择否,重装Fiddler

然后OK就可以开始抓HTTPS的包了。

对于Fiddler其实相当于一个 "代理服务器".

浏览器访问 csdn.com 时, 会把 HTTP 请求先发给 Fiddler, Fiddler 再把请求转发给 csdn 的服务器.当 csdn 服务器返回数据时, Fiddler 拿到返回数据, 再把数据交给浏览器.
因此 Fiddler 对于浏览器和 sogou 服务器之间交互的数据细节, 都是非常清楚的,所以才能抓取包。

代理也有正向代理和反向代理,可以简单的理解为帮助客户端"跑腿"的为正向代理,帮助服务器"跑腿"的为反向代理。(反向代理我们这里先不说)

除了 fiddler 之外, 有的程序也是代理,如加速器和vpn。这些代理程序之间,可能是冲突的。
所以使用 fiddler抓包前, 一定要检査关闭之前的代理软件,否则可能会抓包不成功。

HTTP协议格式

学习一个协议我们肯定要知道它的格式,HTTP协议格式跟其他协议不同,它的请求报文格式和响应报文格式是不同的,我们可以通过刚学的Fiddler去抓包看下请求和响应的内容。

请求报文格式

请求报文格式由首行,请求头(header),空行,正文(body) 组成

首行------URL

HTTP 请求的第一行.有三个部分信息,三个部分使用 空格 分割,下图是一个经典例子,我们来分析下:

1.GET, HTTP 请求的"方法"(method)
2.URL 唯一资源定位符,描述了一个资源在网络上的位置。
3.版本号 HTTP/1.1

这里要重点讲述的由第一个信息:方法 和 第二个信息:URL ,我们先讲URL
平时我们俗称的 "网址" 其实就是说的 URL (Uniform Resource Locator 统一资源定位符)

URL的详细规则由因特网标准RFC1738 进行了约定,其详细格式如下:

1.协议方案名: 常见的有 http 和 https,告诉你是哪个协议 也有其他的类型.(例如访问 mysql 时用的jdbc:mysql )

2.登陆信息: 现在的网站进行身份认证一般不再通过 URL 进行了, 一般都会省略,属于上古时期的方式,现在都没有人这么进行登录了

3.服务器地址: 要访问的服务器是哪个,此处是一个 "IP地址/域名", 域名会通过 DNS 系统解析成一个具体的 IP 地址

4.端口号: 上面的 URL 中端口号可以被省略, 当端口号省略的时候, 浏览器会根据协议类型自动决定使用哪个端口, (例如 http 协议默认使用 80 端口, https 协议默认使用 443 端口)

5.带层次的文件路径: 描述了要访问服务器的哪个资源,一个服务器可提供很多资源供外界访问,比如web服务器(网站)就可能会包含很多不同的网页,就可以通过这里的路径区分不同的网页。

6.查询字符串(query string): 本质是一个键值对结构,它以?开头 , 键值对之间使用 & 分隔, 键和值之间使用 = 分隔,1个 url 中的 query string 里可以包含 N 个键值对,甚至可能更长。查询字符串是程序猿自定义的,不像 header 中的键值对是标准规定的。我们可以通过这样的方式来自定制传输我们需要的信息给服务器。(请求报文中的body作用也是如此,自定制传输我们需要的信息,之后在讲方法时我们会讲)
这里还要提一点,对于 query string 来说,如果 value 部分要包含特殊符号或者中文字符的话,往往需要进行 urlencode 操作,urlencode 本质上是一种转义字符,如++进行urlencode操作后就是%2B%2B

为什么要urlencode操作呢?

因为像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现.query string里面的value部分,一旦也包含这些符号不就乱套了吗,就会使url的解析出现问题,如果不处理好,有些浏览器就可能会解析失败,导致请求无法正常进行.

另外我们在游览器网址上可能看到该现象:有中文汉字出现,那这是什么情况?

实际上,如果把这串url复制粘贴到记事本,那么就又回到了urlencode编码状态,这是因为浏览器为了让用户看起来更加直观,所以就按照转码之前的方式进行显示的,实际上网络传输的请求依旧是转码之后的状态。


7.片段标识符:有的网页内容比较长,就可以分成多个"片段",通过片段标识符,就可以完成页面内部的跳转,这个一般应用于技术文档,普通网站都不会存在片段标识符

首行------方法

对于这些方法,最常用的是Get和Post方法,其他方法很少使用。

Get方法

GET 是最常用的 HTTP 方法,常用于获取服务器上的某个资源.网络上大部分请求都是GET

方法。

GET请求中程序员自定制想要传输的信息都存放在query string中,里面不包含body(body也是用于自定制传输信息)

Post方法

POST 方法也是一种常见的方法. 多用于提交用户输入的数据给服务器(例如登陆,上传文件)

Post请求中程序员自定制想要传输的信息都存放在body中,url中不包含query string

Get与Post的区别

这是一个经典的面试题:
开篇, 我们先盖棺定论, GET 和 POST 没有本质区别(双方甚至可以替换对方的场景) ,这是因为这些 HTTP 请求,最初的初心就是为了表示不同的"语义",对于语法规定并没规定其有什么本质不同,所以实际上程序员如何使用,可以更加随意,因为语法上没有严格规定不同,所以就算乱用也不会报错。(GET和POST没有本质区别其实也可以说上述所有方法就没有本质区别,可以乱着用)

虽然GET和POST没有本质区别,但使用习惯上我们程序员还是普遍达成了一个共识,使它们存在一些差异:
1.GET 经常是把自定制传递给服务器的数据放到 query string 中; POST 则是经常放到 body 中.(使用习惯上最大的差别)

2.语义上的差异:GET用来获取数据,POST 用来提交数据

上述这些习惯虽然我们可以不遵循,但还是建议大家遵守上述的约定俗称的习惯,因为大家都用该习惯。

除此以外,还有一些网上说的差异,实际上是错误的:

1.GET 请求能传递的数据量有上限,POST 传递的数据量没有上限.

这个说法是一个"历史遗留"早期版本的浏览器(硬件资源非常匮乏),针对 GET 请求的 URL 的长度做出了限制,实际上,RFC 标准文档中并没有明确规定 URL 能有多长.....目前的浏览器和服务器的实现过程中, URL 可以非常长的.(甚至说可以使用 URL 传递一些图片这样的数据)
2.GET 请求传递数据不安全,POST 请求传递数据更安全

依据是: 如果使用 GET 请求来实现登录,点击登录的时候,就会把用户名和密码放到 u 中,进一步的显示到浏览器地址栏里,不就被别人看到了吗,相比之下, POST 则是在 body 中,不会在界面上显示出来,所以就更安全,这很显然不对

我们通常说的"安全"指的是你传递的数据,不容易被黑客获取或者被黑客获取到之后不容易被破解,当不放在界面上显示,并不能体现它很安全,万一加密不严格,一下子就被黑客捕获到了,这不就是不安全。所以安全性和 选择get还是post无关,关键在于加密。

3.GET 只能给服务器传输文本数据. POST 可以给服务器传输文本和二进制数据.

(1)GET 也不是不能使用 body (body 中是可以直接放二进制的)
(2)GET 也可以把 二进制的数据进行 base64 转码(变为文本数据), 放到 ur 的 query string 中

4. GET 请求是幂等的. POST 请求不是幂等的.

幂等是指输入相同的内容, 输出是稳定的, GET 和 POST 具体是否是幂等,取决于代码的实现,

1个典型的 GET 不幂等的情况: 搜狗的广告搜索

广告数据就是通过 GET 请求获取的.这些 GET 请求设计的时候一定是不幂等的.这里出哪些广告, 广告的顺序,背后涉及到一系列复杂的逻辑

5.GET 请求可以被浏览器缓存,POST 不可以被缓存

幂等性的延续,如果请求是幂等,自然就可以缓存,不是幂等就不行。所以Get请求也有可能不被缓存,post也可能被缓存

6.GET 请求可以被浏览器收藏夹收藏,POST 不能被浏览器收藏,这个同样是错误的。

请求头(header)

它是一个键值对结构的数据,每个键值对都是独占一行的,键和值之间使用 :空格 来区分

键值对之间以换行符分割。

header中的键值对跟query string不同,query string中的键值对是程序猿自己定义的,而header中的键值对都是标准规定的内容。

header中包含的键值对有很多,但是大部分都不关键,主要介绍几个关键的。

HOST

表示目标服务器的域名或 IP 地址。

Conte-Length

表示 body 中的数据长度,单位是字节,一旦有了body就需要知道body的长度,才能知道一个完整的http请求。

由于HTTP是基于TCP,那么这里就会谈到粘包问题 ,前面我们谈到的粘包问题有两种解决方案,一个是指定分隔符,一个是指定长度。如果是GET请求没有body,通过空行;如果是POST请求有body,通过空行找到body的开始,通过Conte-Length找到body的介绍位置。

Content-Typent

表示请求报文中body内部的数据类型,有以下三种数据类型

1.json,具体类型为 application/json

2.form表单的格式,具体类型为application/x-www-form-urlencoded:form

3.form-data 的格式,multipart/form-data: form

User-Agent (简称 UA)

UA里的信息主要有两个部分:浏览器版本,操作系统版本

在以前UA主要是描述你用啥样的设备,啥样的浏览器。对于现在来说作用不是很大了(以前的时候,因为浏览器的版本差异太大,有的不能接受图片视频等等,所以来区分,但是现在浏览器的版本其实大差不差),现在主要是区分该设备是pc端还是移动端

Referer

表示当前页面是从哪个页面跳转过来的.

如果直接在浏览器中输入URL, 或者直接通过收藏夹访问页面时是没有 Referer 的.

在搜狗浏览器搜索信息时(页面跳转):此处的Referer就是表示从搜狗主页跳转过来的。
用途:对于网页上面的广告,用户点击一次搜狗就能挣钱(按点击计费),那么在一个月之内某个广告被点击的次数就需要进行统计,搜狗需要统计,广告主也需要统计。每次点击就会把请求发送到搜狗服务器上,搜狗的服务器就可以记录一条日志(日志就是打印)。广告主这边也会有服务器,每次收到请求就会记录一条日志,统计日志的数目即可。广告主可能有多渠道投放,就需要对不同的渠道进行区分,此时就可以通过Referer进行区分,是从搜狗跳转过来的Referer就是搜狗的域名,从百度跳转过来的Referer就是百度的域名。

有没有一种可能,存在某个人把访问广告主服务器的请求的 referer 给修改了呢?运营商(移动联通等)完全有能力做这个事情,修改成别人的refer,自己再赚一笔,在2015年存在非常多这样的现象,这叫做运营商劫持。运营商劫持每天都会对互联网公司造成巨大损失,公司就从技术上进行"反制",HTTPS就是重要的反制手段,HTTPS直接针对HTTP请求中的内容进行加密,这样就不会再被修改了(关于https如何加密,后序介绍)

cookie

Cookie 可以认为是 浏览器 在本地存储数据的一种机制。

在程序运行过程中,需要一些数据在浏览器这边存储,并且在后续请求的时候数据可能需要再发给服务器,所以我们就利用cookie存储这些数据,如上次登陆时间.上次访问时间.用户的身份信息,累计的访问次数。

实际上更容易想到的是,把这样的数据直接存储到本地文件中,但是实际上不可行的.浏览器为了考虑到安全性,禁止网页直接访问你的电脑的文件系统(怕你访问某个网站,结果网页里有病毒,把你硬盘上的所有学习资料都给删掉了),网页代码中也就无法直接生成一个硬盘的文件来储数据了.

为了保证安全性,又能进行存储数据,于是就引入了 Cookie,cookie内部只能存储键值对,键值对之间,使用;分割 键和值使用 =分割,如下

这些 内容 就是浏览器本地存储的 cookie,都会在后续请求服务器的时候,把这些内容给代入到请求中传给服务器,服务器通过 Cookie 的内容做一些逻辑上的处理
cookie内部的内容都是程序猿自定义的,咱们作为外人不能理解。后续咱们自己写网站,也可以自定义了

Cookie 往往是将服务器返回的数据进行存储,存储的都是临时数据,Cookie 存储到浏览器所在主机的硬盘上,并且是按照域名为单位来存储的,意思就是每个域名都可以存自己的 Cookie,彼此之间不影响。
对于剩下的空行和body:

空行是作为请求头的结束标记

而body我们之前也讲过了,是我们自定义要传输的信息,并且post中存在,get并不存在body(一般是这样规定的哈哈,实际上语法并没有规定)
这篇文章主要是讲了请求报文格式,下篇我们再继续讲响应报文格式

相关推荐
Mr-Apple38 分钟前
Decode rpc invocation failed: null -> DecodeableRpcInvocation
网络·网络协议·rpc
IpdataCloud1 小时前
IP 风险画像如何实现对恶意 IP 的有效拦截?
网络·网络协议·tcp/ip
运维有小邓@2 小时前
比较入站和出站防火墙规则
服务器·网络·php
IT小饕餮2 小时前
MUX-vlan
网络
TPBoreas2 小时前
Ubuntu开放端口
linux·服务器·网络
2501_916013743 小时前
日常开发中,iOS 性能调优我们怎么做?
websocket·网络协议·tcp/ip·http·网络安全·https·udp
狄加山6753 小时前
STM32的网络天气时钟项目
网络·stm32·嵌入式硬件
从零开始学习人工智能3 小时前
4G与5G网络频率:技术演进与应用场景解析
网络·5g
livemetee3 小时前
Netty 的 Reactor 模型
网络