Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔

文章目录

  • 前言
  • HTTP协议
    • HTTP 具体的应用场景
    • HTTP 协议格式
      • 如何才能看到 HTTP的报文格式?
      • 抓包工具:其实就是一个第三方的程序。
        • 安装抓包工具
        • 简单介绍一下 fiddler 抓包工具。
    • 协议格式总结
      • 我们先来看 HTTP 的 请求部分
        • 格式概括介绍
        • HTTP 请求 详解
          • URL
            • URL 中的可省略部分
            • URL 的 encode / decode机制
          • HTTP 请求 的 方法
            • 由于方法的现状,就衍生出了一个经典的面试题
          • 请求报头(header)
            • Host
            • Content-Length && ContentType
            • User-Agent (简称 UA)
            • Referer
            • Cookie
          • 认识请求 "正文" (body)
      • 我们再来看 HTTP 的 响应部分
        • 格式概括介绍
        • 响应详解
          • 首行 - 状态码
            • 总结
        • 响应“报头”
          • Content-Type
  • 通过代码来构造 HTTP 请求
    • 基于 form 表单 构造 HTTP请求
    • 基于 ajax 构造 HTTP请求
    • 基于 Java - socket 来构建 HTTP请求
    • 总结
  • HTTPS
  • HTTPS的工作过程
    • 对称加密:使用同一个秘钥,就可以进行加密,也可以进行解密
    • 非对称加密
    • 完整流程
    • 总结
  • Tomcat
    • Tomcat 是什么?
    • Tomcat 的 下载安装
    • 如何使用 Tomcat
      • 第一步:启动
      • 使用浏览器来访问 Tomcat
      • 总结

前言

上篇文章,基于前端的一套基础知识,构造出了博客系统的页面。
但是,Java 程序员,主要的工作还是围绕服务器后端开发的。
从本篇博文开始,我们来了解后端部分。

HTTP协议

在我们正式学习基于 Java 写 服务器 之前,我们还有一个重要的知识要给大家介绍:HTTP 协议。

HTTP协议,相当于是对前面的网络协议做进一步的补充.
前面说到,计算机网络的核心概念,就是网络协议。
网络协议种类非常多,其中一些耳熟能详的 IP、TCP、UDP…
其中还有一个应用非常广泛的协议:HTTP
HTTP协议,也是日常开发中最最最常使用的一种协议。
可能说,我们在公司里,在开发项目的时候,TCP 和 UDP 可能会很少用到,IP协议可能就更少了。
但是!不管你是怎么样的,HTTP 一定是用得最多的。
因此,HTTP 协议大概率是我们日后开发中用的最多的协议。
故这一块需要我们慎重对待,一方面工作中用得多,另一方面面试要考。

另外,HTTP 处于 TCP / IP 五层协议栈中的 应用层 。
也就是说,HTTP 是一个应用层协议。
而且,HTTP 在传输层是基于 TCP的。
注意!这里的 HTTP,是指的 HTTP1 和 HTTP 2 ,它们都是基于 TCP的。
而最新版本的 HTTP3,是基于 UDP的。
但是!当下互联网上绝大部分都是使用的 HTTP 的 HTTP1.1版本。

前面博文中讲过:
整个协议栈,上层 和 下层之间是一定的关联关系的。
上层协议,要调用下层协议。
反过来说,下层协议,要给上层协议提供一个支撑。
那么,HTTP 作为一个应用层协议,它在进行传输数据的时候,它就要基于 TCP的这一套机制保证。
TCP 是 保证 传输的可靠性,而 HTTP 更倾向于 业务层次上的东西。

前面也讲过,传输层协议,主要关注的是 端对端 之间的数据传输。
尤其是TCP,重点关注的是可靠传输
而我们在这个基础上 的 应用层协议,则是站在程序应用的角度,要对传输的数据,来进行具体的使用。

举个例子,我们在网上相中一个床刷子,感觉还不错。于是我们就下单了。
然后,别人卖家给我们发货,卖家给我们发货这件事,就相当于是一个 TCP 层次上的传输。
卖家只在乎发的货,有没有到我们填写地址附近的菜鸟驿站,或者说货在发送的过程,有没有丢;或者是在发送的过程,有没有损坏;又或者说,发错了货,等这些问题。(传输的可靠性)
但是卖家不关注,我们拿到刷子之后,具体做什么。
无论我们是拿着这个刷子去刷床,还是打人,这都是属于我们拿到数据后,具体去怎样的使用,卖家都是不关注的。
所以,TCP关注的是数据的传输可不可靠,而 HTTP 更关注于 数据传过来之后,具体怎么去使用。

另外,在应用层协议 HTTP 中,很多时候,都是程序员自己定义的。

程序员来根据实际场景,来设计协议,并且基于这个协议来进行开发。
但是,程序员大的圈子里,水平都是参差不齐的。
有的非常 nb,有的就非常菜 了。

此时,一个 HTTP 协议,全靠程序员自己来设计。
大佬设计的没有问题,非常好用。
而菜鸡设计的,肯定是存在问题的。

为了解决这样的问题,于是有些大佬就发明一些很好用的协议,直接让大家照搬。
即使是一个菜鸟,也能根设计出一个还不错的协议出来。
HTTP 就是其中的一个典型代表。

而且 HTTP 最主要的特点:HTTP虽然是已经设计好了的。但是自身的可扩展性是非常强,可以根据实际需要,让程序员传输各种自定义的数据信息。
正是因为如此,HTTP 是一个非常通用的协议。
这个场景能用,那个场景也能用,只不过在不同的场景下,不同的程序员再去传输不同的具体数据。但是整体格式是非常接近的。

这也是为什么 HTTP协议,会被广泛使用的一个主要原因。

那么,HTTP具体会被用到哪些场景中呢?


HTTP 具体的应用场景

HTTP 具体的应用场景,其实大家天天都在用。

只要我们随便打开一个浏览器,打开其中任意一个的网站,这个时候其实你就用到了 HTTP。
或者你打开了一个手机APP,随便加载一些数据,这个时候其实你也大概率用到了 HTTP。

可以说,HTTP 为构建出当下这样的一个丰富多彩的互联网世界,起到了一个至关重要的作用。


HTTP 协议格式

HTTP 是一个文本格式的协议

协议的格式,我们在接触 TCP 和 UDP 协议等等…的时候,我们就已经接触过了.
协议格式:数据具体是怎么进行组织的。
比如:UDP 的报文结构
UDP : 报头(源端口(16位),目的端口(16位),报文长度(16位),校验和(16位)) + 载荷
通过 UDP协议的结构,我们知道:原来一个UDP数据报长这样。

因此,类似的,我们想要理解 HTTP,也需要去理解清楚 HTTP的协议格式。
而且 HTTP 协议的格式,相比于 UDP 的协议格式,还是更复杂的。
而是,HTTP 协议格式 和 UDP 和 TCP 的画风,也不一样。

前面讲的 TCP / IP / UDP,这些协议都是属于“二进制”的协议。
就是说,理解它们的含义,就经常会涉及到 二进制 的bit位。
比如:16位眼校验和,6位标志位(TCP)… 这些都是和二进制有关系的。

而 HTTP 则是一个文本格式的协议。
也就是说,我们不需要去理解具体的二进制位,而只是理解文本的格式即可。
相比前面的协议,HTTP协议格式虽然复杂了点,但是对人来说是友好的。
因为 HTTP的协议格式是文本格式,所以更方便于 人用肉眼来观察。
这不比看着一大堆的二进制数据香?


如何才能看到 HTTP的报文格式?

其实我们可以通过 一些 “转包工具” , 来获取到具体的 HTTP 交互过程中的 请求 / 响应 的细节。
与其去看课本上画的结构图,不如我们真正动手来抓包看一看。
看一看实际的 HTTP 请求 和 响应 都长什么样子。
其实我前面讲的 TCP / UDP 这些,也是可以借助抓包工具来分析的


抓包工具:其实就是一个第三方的程序。

它是怎么玩的呢?

在这个网络通信的过程中,类似于“代理”,
这个 “代理”,我来举个例子说明一下:【ps:纯属虚构,因为我莫得girlfriend。】
有一天,我的女朋友想吃 辣条(麻辣王子,很麻很辣)。
她自己是不是可以去超市买啊?
但是!这里有一个问题:女朋友是一个很懒的人。
她虽然很想吃辣条,但是她又懒得去。
怎么办?它就有一个办法,叫我给它去买。
girlfriend:“我想吃辣条了,你去给我买,好嘛?”
此时,我就屁颠屁颠的去超市买了辣条,带回来给它。

总得来说,女朋友想吃到辣条,有两种方法:
1、她自己去买
2、叫我帮她去买
这第二种方法,我其实就是代理。
代理,说白了就是一个跑腿的。
更准确的来说,就是一个传话的。
结合实际:
女朋友告诉我,她想吃辣条。
然后,我听到这句话之后,我就来到了小卖部老板面前。
我就是说:“老板,来一包98年的辣条”
然后,老板就给了我一包辣条。
我就拿着辣条回去了。
回到家,我再把辣条交给了女朋友。
在这个过程中,与其说我是跑腿的,不如说我是给老板传达我女朋友的需求。老板把东西给我,我再把它带回去,给女朋友。

类似的,我们的抓包工具,其实就像是一个代理。
结合代码:客户端把请求 “告诉了” 抓包工具,抓包工具得知请求之后,将其转发给了 服务器,服务器对其请求作出的响应,把响应“交给”装包工具,抓包工具再将响应返回给 客户端。
所以,它其实就可以获取到,传输这里面的一些细节.


换个说法,如果是第一种情况。女朋友自己去买辣条。
她肯定是要出门。
我看到出门的她,内心感到很疑惑,她今天不是没事吗?怎么就出去?
是要做什么吗?
也就是说,我并不知道她出去具体要干什么。

如果是第二章情况,她告诉我,让我去买辣条。
那么,我就知道她具体想干什么。
因此,这个代理,是可以直接获取到传话的具体信息。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第1张图片
而且,可告诉大家的是:“代理” 这种情况,在计算机中是非常常见的!
所以,大家要对它有着一定的认知。


安装抓包工具

其实抓包的工具有很多。

当前博主要使用的是 Fiddler,
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第2张图片
fiddler 是一款专门 抓 HTTP 的抓包工具。
当然并不是要求你们和我使用一样的抓包工具。
其它的抓包工具用法也是类似的。
只要你们会用就行,和 fiddler 的差别,应该不大。
注意!fiddler 除了 HTTP的包,没办法抓到 TCP / UDP / IP 这样的包 .
要想抓到这些包,需要使用 wireshark 这样的工具。
wireshark 是来者不拒的,各种网络请求都能抓。只不过看起来比较杂
而 fiddler 是专注于 抓 HTTP的请求的,对于 HTTP 的协议分析,用起来是非常方便的。

下载 fiddler,去官网下载。

谨防 P2P 下载器,前段时间 315 不是吧 P2P 给曝光了嘛。
当时 P2P 就被下架了。
据说, 这个 P2P 下载器,又卷土重来了。【就是换了层皮】
怎么区分是官网?很简单!全是英文的,最靠谱。

下载完成之后,打开它。
一般是在 开始菜单中,去找。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第3张图片
建议设置快捷创建快捷方式,方便一点。

进入之后,大概是这么一个界面
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第4张图片
首先,我们需要设置一下。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第5张图片
这样做的目的,就是为了抓 HTTPS 的包。
因为当下网络上的大部分网站都是 HTTPS的。
如果不开启 抓 HTTPS的数据报,其实就基本没有什么可以抓的了。

另外,需要注意一点:如果你电脑上装着其他的代理程序 / 插件。
比如:
xxx加速器,xxx ,xx FQ工具…
这些都会导致,两者之间产生冲突的。
就会导致 fiddler 什么也抓不着。
所以,你们在使用 fiddler 的时候,要事先退出相关的程序。


简单介绍一下 fiddler 抓包工具。



下面,我们就来看一下,HTTP 未加修饰的本体数据是什么样子的?

协议格式总结

下面,我们就来对照这幅 请求 - 响应 图,来加深对 HTTP协议的理解。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第6张图片


我们先来看 HTTP 的 请求部分

格式概括介绍


HTTP 请求 详解

URL

URL - Uniform Resource Locator:全球资源定位器

简单来说,就是表示 网络上唯一资源的地址符。
地址符,就是描述该资源在哪。

我们要明确 网络是由什么来构成的。
先是由主机来构成的。
主机:电脑,路由器,交换机,手机等等…,通过网络来连接在一起,构成一个“网络”。
然后接下来,主机是其中的一部分,同时每个主机上,又包含了很多具体的一些资源。

比如说:
我这个主机上面存了一些 HTML 文件,他那个主机上面存了一些 Word文档,还有人主机上面存了一些电影。
这些都属于是具体的资源。
所以像这些资源,通常其实通过文件的方式来进行组织的。
故我们定位到网上的一个唯一资源地址,其实就是:既要能够明确主机是谁,又要明确访问这个主机上的资源,具体是哪个。

下面我们来举几个例子。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第7张图片
上面三个都是 URL,所以我们当前就能看到:URL,其实会有不同的具体的表现形式,但是整体格式是类似的。
说白了,通过浏览器,打开网页的时候,地址栏里填写的这个“网址”,其实就是 URL。
虽然上面的URL,看起来差别很大。
但其实,整体的格式非常有特点。
下面,我们来看一个对这个格式的总结


URL小结:
对于 URL 来说,它里面的结构看起来比较复杂,其实最重要的,和开发最关系紧密的,主要就是四个部分:
1、IP地址 / 域名
2、端口号(经常会被隐藏,充数的存在)
3、带层次结构的路径
4、queryString(查询字符串)
这四个最好掌握!!!!
尤其是 3 和 4 ,和我们写代码是密切相关的!


URL 中的可省略部分

Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第8张图片

协议名: 可以省略, 省略后默认为 http://

ip 地址 / 域名: 在 HTML 中可以省略(比如 img, link, script, a 标签的 src 或者 href 属性). 省略后表示服务器的 ip / 域名与当前 HTML 所属的 ip / 域名一致.

端口号: 可以省略. 省略后如果是 http 协议, 端口号自动设为 80; 如果是 https 协议, 端口号自动设为 443.

带层次的文件路径: 可以省略. 省略后相当于 / . 有些服务器会在发现 / 路径的时候自动访问/index.html

查询字符串: 可以省略

片段标识: 可以省略


URL 的 encode / decode机制

当query string 中如果包含了特殊字符,就需要对特殊字符进行转义。
这个转义的过程,就叫做 URLencode,反之,把转义后的内容还原回来,就叫做URLdecode。

那么为什么需要进行 encode?
其实很好理解,前面也看到了,一个 URL 里面是有很多特殊的含义的符号的。
比如:
/  :  ?   &   = …这些符号都是在 URL中具有特定含义的。
万一,queryString 里面也包含这类特殊符号,就可能导致 URL 被解析失败!
比如:我们刚才说的 ?号,是用来分割 路径 和 查询字符串。
如果前面 和 后面都存在 问号,那完蛋。
服务器收到这条请求,就懵逼了,到底那个问号后是 查询字符串?它就会产生误会。
为了消除歧义,就规定了,一旦 queryString中遇到了这些符号,就将其转义。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第9张图片


HTTP 请求 的 方法

前面讲 HTTP 请求部分的时候,就涉及到:GET 和 POST。
get:就是 得到 / 获得 一个东西。
POST:就是 向服务器 传递 / 发送 一个东西。

HTTP 协议的方法非常多!
但是,最最最常用的,也就是 GET 和 POST。

举个例子:
南朝著名的大才子谢灵运写过一句话:
天下文才十斗,曹子建独占八斗,我占一斗,余者天下共分之。
其实说白了,就是变相的夸自己。
代入方法中,GET 独占 八斗,POST占一斗,剩下的其他的方法,分余下的这一斗。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第10张图片
上面这个表格,只是列举部分方法,你们也看得出来,这里只罗列到了1.1版本。
PUT 与 POST 相似,只是具有幂等特性,一般用于更新
DELETE 删除服务器指定资源
OPTIONS 返回服务器所支持的请求方法
HEAD 类似于GET,只不过响应体不返回,只返回响应头
TRACE 回显服务器端收到的请求,测试的时候会用到这个
CONNECT 预留,暂无使用
HTTP 后续的 2,3版本,暂不考虑。

另外,说一下:HTTP 中引入这些方法的初衷,是为了表示不同的“语义”。
语义这个词,在HTML中讲标签的时候,就涉及到了 有语义标签(h1,a,p,img) 和 无语义标签(div,span)。
语义:就是指这个词,是否有特定的含义。
h1 是一级标题,a是超链接,p 是自然段,img是图片。
HTTP 中的 方法,也是一样的,具有自己的“语义”。
GET:获取资源,POST:传输实体主题…

有的人可能会有疑问:为什么这么多方法,到最后,“只剩下”两个在使用呢?
不知道大家是否听过一句话:
理想很丰满,但是现实很骨感。
设计HTTP的大佬希望:程序员能够按照 HTTP 语义来使用这里的各种方法。
但是!随着时间的推移,使用就跑偏了。
现在大家写代码,基本都是 GET / POST 一梭子,就把数据给搞定了。
基本没有考虑语义的事情,与大佬的美好愿望完全就不搭边了。
正因为如此,所以也就是导致了 多种 HTTP 方法之间的界限,就变得模糊了。
本来,get 是从服务器里拿东西,post是 我给服务器送个东西。
但是用着用着,大家就没按照规则去使用,开式瞎b用了。

这个时候,用GET 也可以给 服务器送个东西,POST也可以从服务器那个东西。
就完全没有按照规则来办事,怎么方便怎么来,怎么简单怎么玩,任性!
所以,现在对于方法的使用,还是处于一个比较混乱的情况。
所有的方法,都出现了比较接近和混淆的状况。【这是HTTP的现状】


由于方法的现状,就衍生出了一个经典的面试题

谈谈 GET 和 POST 的区别。

回答这个问题的时候,大家一定要明确一点!
第一句话,应该先盖棺定论!!!
GET 和 POST 没有本质上的区别。
这句话,就是在暗指 HTTP 方法的“混乱现状”。
具体来说,相当于是 GET 能使用的场景,POST 也能胜任。
POST 能胜任的场景,换 GET上,也能做到。

注意!我们所说的,本质上没有区别,是指它们之间能够相互替换。
但是!GET 和 POST,站在细节上是存在区别的!!!!
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第11张图片
通常情况下,GET 是没有body的,GET 是通过 queryString 向服务器传递数据的。
通常情况下,POST是有body的,POST 通过 body 向服务器传递数据,但是POST没有queryString。
但是,请注意!上面两句话的成立的前提是:通常情况下

这么说吧:
如果我就想让 GET 有 body(自己构造一个带body的GET请求)
或者就想让 POST 带有 queryString,是完全可以的!!!!

可以这么认为:这里所说的细节上的区别,不是硬性区别,而只是一种习惯性的用法。
这么说吧,你可以遵守习惯,也可以改掉这个习惯。

所以,我们未来在实际开发中,看到的GET 和 POST,大部分都是 GET 没body,有queryString;POST 没queryString,有body。
但是!也不排除会遇到这种,GET 和 POST,都有 body 和 queryString 的情况。
比如有些公司,就会要求你使用 POST 来处理所有的请求,是完全有可能的。
总之,具体还是看公司的要求。

所以,GET 和 POST 之间,并没有本质区别。
本质区别,就像是:有你没我,有我没你的情况,两者之间是无法替代的。
这是第一个区别!


第二区别:语义
GET 通常用来去数据,POST通常用来上传数据。
【其实这才是第一个区别,上面是第二个区别,只是上面的讲顺畅了点,就先讲了】
不过,这一点在当前的现状下,已经不明显了。
现状是,GET也经常用来上传数据,POST也经常来获取数据。


第三个区别:
GET请求一般是幂等的,POST请求一般是不幂等。【这个也不是硬性要求,而是习惯】
幂等,是数学上的一种术语。
幂等:每次我们输入相同的数据,得到的输出结果是确定的。
不幂等:每次我们输入相同的数据,得到的输出结果是无法确定的。

举个例子:
幂等,就好像是 这一次种瓜得瓜,下一次接着种瓜,得到的还是瓜,得到的结果是可以确定。
不幂等,就是这一次种瓜得瓜,下一次可能就是种瓜得香蕉,得到的结果是不确定的。

在实际开发中,我们习惯于 将 GET 设置为 幂等的,将POST设置为不幂等的。


第四个区别:
GET 可以被缓存,POST 不可以被缓存。
缓存:就是提前把结果给记住。
如果是幂等,记住结果是很有用的,节省了下次访问的开销。
如果是不幂等的,就不应该去提前记,因为结果是不确定的,下一次访问的开销还是存在的。

注意!能不能缓存 和 幂不幂等,是有关联的。
如果你在设计 GET 的时候,没有把它设计成幂等,那么GET,也是不能缓存的。
POST 也是同理。

注意!以上的区别,都是经得起考证的!
有些朋友,可能看了网上的一些资料,也讨论到了一些其它的区别。
大家一定要慎重对待!!!
网上的很多资料说的区别,都是错的!!!!

其中最典型的错误:
GET请求传递的数据长度有上限。(指的就是URL有上限)
POST 没有上限。
这种说法是错误的!!!
在 HTTP文档中,明文规定:标准本身是不限制 URL 的长度。

网上流传这个说法的原因是上古时期的浏览器实现,没有完全遵守标准导致的。
这个说法,放到现在。
已经不适合现代的浏览器。


请求报头(header)

header 里面是一些键值对。
不同的键值对表示了不同的含义。
当前这里的键值对种类很多,本只是介绍一些简单常见的。


Host

表示服务器主机的地址和端口
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第12张图片


Content-Length && ContentType

Content-Length:表示 body 中的数据长度.
Content-Type:表示请求的 body 中的数据格式

关于 Content-Type 的详细情况:https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type

关于 Content-Length的补充。
HTTP也是基于TCP的 协议。TCP 是一个面向字节流的协议。
这里就会存在 粘包问题
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第13张图片
什么是粘包问题?
前面也是讲过的,当前我们基于TCP传输的应用层数据,它其实就会在我们的接收缓冲区里面,一个字节一个字节紧紧的挨在一起。
那么,当应用层来读这里的数据的时候,一读,都不知道这数据从哪到哪是一个完整的应用层数据。这个时候就很容易给应用程序带有歧义,以至于带来一些不必要的麻烦。
因此,怎么去解决这个问题?
前面也讲了。
合理设计应用层协议,来明确 包与包之间的边界!!
1、使用分割符
2、使用长度
这两点,在HTTP中都有体现。

空行,对应的就是分割符,明确包与包之间的界限。
如果当前有若干个GET请求,到了 TCP 接收缓冲区中了。
应用程序在读取请求的时候,就会以空行作为分割符。
每次读到一个空格,就知道一个请求就读完了。
再一次读取,肯定就是下一个请求的开始


使用长度,对应的就是 Content-Length
如果当前是有若干个 POST 请求,到了TCP缓冲区了。
这个时候,我们空行后面还有body。
当应用程序 读到空行之后,就需要按照 Content-Length 表明的长度(单位字节),继续读若干长度的数据【也就是读取正文】。
这个时候,也就代表读到了一个完整的数据

所以,在HTTP里面,即借助了分隔符,又借助了 长度,两者一结合。
就可以很好的区分出,从哪到哪是一份完整HTTP的请求。


User-Agent (简称 UA)

UA(用户代理):表示的是,当前用户是在拿一个什么样的东西来上网!!!
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第14张图片
虽然User-Agent 这一个键值对,我们并不能全部看懂。
但是不妨碍我们得出结论:
我们可以发现:User-Agent,总的来说,分为2部分信息:操作系统 + 浏览器 信息。

为什么要有这样一个信息呢?
其实理由也很简单:
在上古时期,就是在互联网刚开始发展的时候。
那个时候,浏览器处在一个飞速进步的状态。
最开始的浏览器,只能显示文本,【HTML】
再后来,能够显示图片了。【HTML】
再后来,能够显示各种复杂的样式了。【CSS】
再后来,能够加载 js 实现交互了。【JS】
再后来,能够支持各种多媒体了(播放视频什么的…)。

那么,问题就来了:
由于那个时期的浏览器发展很快,所以就导致市场被割裂了。
一部分用户,用的是比较老的浏览器。(只能显示文本)
一部分用户,用的是比较新的浏览器。(能够支持 js)
因此,也就给我们网站开发人员带来了很大的挑战。
在设计一个网页的时候,到底要不要给这个网页加上 JS ,或者CSS。
加了吧,有一些用户的浏览器不支持。
不加吧,有一些用户的体验不好。
【用户表示:我能用,你凭什么不给我用?】
【程序员:我开发的浏览器不支持 JS,岂不是说明我很菜?】

当时,为了解决这个问题,聪明的程序员就想到了一个办法。
让浏览器发送的请求中,包含一个数据:“自爆家门”。
就是你浏览器请求过来了,你先告诉我,你的浏览器大概能支持哪些功能。
或者说,告诉我,你的浏览器处于一个什么版本,是旧的,还是新的。
此时,服务器就可以根据浏览器中这个 “自爆家门” 的信息,就可以做出区分了。
根据你的浏览器版本,或者说根据你的浏览器所支持的功能,来返回一个你的浏览器能够支持的网页数据。
这样做,就可以满足每个用户的不同需求。

这里 User-Agent 键值对,就是在做着这样的一个“自爆家门”的工作。


浏览器发展至今,主流浏览器的功能已经差别很小了。
十年前,浏览器兼容性,还是前端开发要考虑的大问题。
兼容性:如何去兼容老的浏览器
十年后的现在,今天基本上是不用考虑了。
因为大部分用户使用的浏览器,都是比较现代的浏览器。
该有的功能,它都有。
与最新的浏览器,没有太大的区别。
也就需要关注那么问题了。
故:放到现在,UA 这个字段起到的作用就已经不那么大了。
其实,我们不告诉服务器,用的是什么浏览器。
服务器也大概知道,在这个年代,不会存在太老旧的浏览器。
所以,直接把页面发给你,你的浏览器肯定是能处理的。


但是!随着当下移动互联网的到来,UA 现在又有了新的使命。
用来区分是 PC端,还是 手机端。

当前 PC端 和 手机端,最大的区别就是 屏幕尺寸和比例。
手机端的屏幕尺寸,是远远小于 PC端的。
一般手机端的网页,就得把按钮之类的,设计的大点。
要不然,你手指点不到按钮。
屏幕比例 PC都是宽屏,手机都是窄屏,比例的不同就导致页面布局就得不同。

因此,我们的服务器就可以根据 UA 来区分 当前是 手机,还是PC。
如果是手机,就返回一个手机版的网页;如果是PC,就返回一个PC版的网页。
这两个版本的网页,它们的尺寸和布局有着很大的不同。
这样做,不管用户使用何种设备,打开一个网页,都会用的很舒服。

这就是当下 UA 的主要功能。

另外,响应式布局也能做到根据设备窗口的大小,来修改页面的样式。
响应式布局,大概就是根据当前浏览器窗口宽度自动修改样式。
这个操作,更加考验前端工程师的功底。
写一个页面,适用于两种不同的平台,确实可以做到,但是代码就会更加的复杂。
代码一复杂,写代码就容易出错。
可能更多的是,写两个页面,分别对应着 手机 和 PC 端。

User-Agent 之所以是这个样子是因为历史遗留问题. 可以参考
User-Agent 的故事: http://www.nowamagic.net/librarys/veda/detail/2576


Referer

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

Referer 不是一定有的。
如果你是通过浏览器地址栏直接输入的 地址,或者直接点击浏览器的收藏夹,进入的网页。
这个时候是没有 Referer,这个键值对的。
毕竟 Referer,它是记录你是从哪个网页跳转过来的,想上面两种,都是直接进入的。
那就没有上线,没有上线,自然就没有 Referer 了。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第15张图片
Referer,这个东西也是一个非常有用的手段。

举个例子:广告系统
这个东西,主要就是按照点击来计算。
什么意思呢?


Cookie

明确一点:浏览器,为了安全,默认情况下是不能让页面的 js 访问到 用户电脑上的文件系统的。

假设某个网页上,包含恶意代码。
然后,我们不小心点到的,就可能触发这个恶意代码,可能就把你电脑上的 “学习资料” 全给删除了。

显然这是一件非常难受的事情!
所以浏览器为了保护 用户电脑上的 “学习资料”,就会禁止页面的 js 访问到文件系统。
就是说,页面的 js 是获取不到:当前用户电脑上的文件有哪些 ,就别提进行删除操作了。
这是浏览器为了安全而引入的机制。

但是!这样的安全限制,也带来了一些麻烦。
因为有时候,我们确实需要让页面这里持久化存储一些数据,方便后续访问网站。
举个例子:
其中,最典型的,就是序要存储 用户当前的身份信息。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第16张图片

总结:
Cookie 是浏览器提供的一个持久化存储数据的机制,
Cookie 的最重要的应用场景,就是存储会话ID,进一步的让访问服务器的后续页面的时候,能够带上这个 id 从而让服务器能够知道当前的用户信息。
另外,服务器上保存用户信息这样的机制,就被称为 Session(会话)、

那么,Cookie能不能用存别的信息??
当然也是阔以的!!完全取决你怎么去设计。

大家一定要重点理解 cookie 和 session 的 工作机制。
这件事对于我们后面去实现一些,类似于登录页面,这样的功能,是非常有必要的!
那么,当然了,在讲到后面具体代码的时候,还会通过写代码的形式,让大家进一步的了解cookie 和 session 的工作过程。
当下,咱们就只需要“简单” 的,从理论的角度来认识它的一个工作过程就行了。

我花了很多时间,通过“就诊卡” ,这个例子来讲 cookie。
想必大部分人是可以理解cookie的。
但是! Session,这个词可能讲解很抽象,【一笔掠过】
下面,我就针对它,在进行一次补充。


认识请求 “正文” (body)

正文中的内容格式和 header 中的 Content-Type 密切相关. 上面也罗列了三种常见的情况.

1、 application/x-www-form-urlencoded
这种方式,最终的数据,就是通 键值对 的方式,来进行组织的Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第17张图片

2、multipart/form-data
这种方法的作用,就是 上传一个文件
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第18张图片

3、application/json
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第19张图片
学习 HTTP 最好的方式就是 多抓包。
抓包是一个很有用的技能,
1、帮我们理解HTTP

2、帮助我们调整一个 web 程序。
未来,我们去写一写具体的web程序的时候,很有可能当我们写完一个程序之后。
代码虽然能跑,但是达不到预期的效果。
那么具体是什么样的原因,是前端的问题,还是后端的问题。
一抓包,一目了然。

3、实现一个爬虫
这个时候,也是需要用到抓包的,
其实所谓的爬虫,就是我们自己写一个 HTTP 客户端,来模拟人的操作,来自动从网站上获取到一些内容。未来工作额时候,可能也会涉及到一些爬虫的操作。

此时,有人就可能会有疑问了?
爬虫,不是Python写的吗?
注意!这种说法是错误的。
任何一个编程语言,只要能操作网络,都可以写 爬虫。
比如:使用 Java 写爬虫,也是很常见的操作。
像很多博主们,经常会和评论区的人互赞博文,其实都可以通过实现 爬虫,实现自动回访评论区点赞的效果。

又兴趣的,可以自行尝试。
但是!不要轻易尝试!
当前市面上 90 % 的爬虫,都是违法的。
有句话说的好:爬虫写得好,监狱进的早。【大部分都是这样的】

就这么说吧,每个网站都会提供一个 robot.txt 这样的文件。
这个文件里,会告诉你说,那些资源是可以合法抓取的。
理论上来说,只要你爬取了人家给的白名单之外的内容,就算非法行为。

早些年,网络相关的法律不完善的时候 ,爬虫还是有很多的。
后来法律逐渐完善了,现在写爬虫的风险就很大。
比如:新闻常常会报道,某个程序员,因为他写的爬虫程序,就进去了的。
这种新闻是非常容易看到的。

所以,不要冲动!做个遵纪守法的好公民。
至于大家看到爬虫,或者 人工智能,大数据什么的,第一时间就会想到 Python。
其实这写东西的核心,都不是Python。
可能是Python,起初为了扩大影响力,而打的广告,被人所误解了。
又或者是,道听途说,思想上先入为主了。
让你们觉得 写爬虫,首选,就用 Python。

明确一下:
人工智能的核心代码是 C++.
大数据的核心代码是 Java。
爬虫,每个语言都可以写。


我们再来看 HTTP 的 响应部分

格式概括介绍

Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第20张图片


响应详解

响应的大部分,都和请求是一样的。
所以,我会详细将 和 请求不一样的。
一样的,我们就轻微带过。


首行 - 状态码

表示这次请求是成功,还是失败?以及失败的原因是什么。
HTTP 提供的状态码,有很多!!!
下面,我就给大家来介绍几个常用的状态码。

200 OK
表示 浏览器 很顺利的就获取到想要的内容,中间没有出现什么问题。
打开你们的抓包工具,其实大部分抓到数据报,它们的状态都是200
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第21张图片

404 Not Found
表示当前要访问的资源不存在。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第22张图片

403 Forbidden
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第23张图片
403 Forbidden的意思就是,资源我有,但是你没有权限使用。

这就是“丑拒”:你去跟美女表个白,美女嫌弃你丑,拒绝了你的表白。
也就是说,你没有这个权限,让她成为你的女朋友。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第24张图片
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第25张图片

405 Method Not Allowed
前面我们已经学习了 HTTP 中所支持的方法, 有 GET, POST, PUT, DELETE 等.
但是对方的服务器不一定都支持所有的方法(或者不允许用户使用一些其他的方法).
此时,就会出现 405 Method Not Allowe
例如:
尝试使用 GET 来访问人家的服务器,但是可能别人只支持POST,于是就会返回 405

这个情况,你去外面的网站抓包,是很难遇到的。
但是!如果后面自己写网站后台,这个就很容易出现。
所以,这个 405 ,留到后面再说。

500 Internal Server Error
意思就是:服务器自己出问题了,意味着出现BUG了。
这种情况,在外面的服务器上发生的概率也是比较低的。

但是 咱们后面自己写代码,也是很容易出现这个情况的。
自己写代码,你懂得!

504 Gateway Timeout
意思就是服务器太繁忙了!请求多得处理不过来。

这就跟LOL 活动领皮肤一样,到开放的那个点,网站就进不去了。【巅峰时刻】

302 Move temporarily
302:重定向

不知道大家有没有听过 陈奕迅的一首歌《爱情转移》
里面有这样的一句歌词
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第26张图片
大概的意思就是:一个人深爱这另一人,但是别人拒绝了,没有走到一起。
这个时候,你看到了另外一个人,你把曾今对那个人的爱,转移到这个人身上。
这就是 爱情转移。
其实重定向,也是类似的。

鉴于,大部分人可能和博主一样,都是单身狗。
可能有些人还明白不了。


我在这里在举个例子:呼叫转移。
呼叫转移,是中国移动,这样的运行商这里,可以办理的一个业务。【其他的运营商应该也可以办】
情况大概是这样的:我现在想换个电话号码,但是旧的电话号码,很多亲人朋友都是记得这个号码。
如果贸然就换了,我还得群发一下我的新号码。这就很麻烦!
怎么办?
我就可以直接办理呼叫转移。
办理之后,我申请一个新的号码。
此时,只要有人拨打我的旧号码,就会由运营商自动转接到我的新号码。
这个操作,就叫做 呼叫转移。
用三个字 来概括,就叫做重定向。

重定向这个词,在计算机的很多场景中都会涉及到,不仅仅是HTTP。
但是虽然在不同的场景中,细节上有些差异,但时候表示的核心含义,都是 “呼叫转移”。

302,就是重定向其中的一种。
302 常见场景:
在登录过程中,是非常典型的情况

在重定向响应中,一般都是需要 Location 属性的。


总结

上面就介绍了,最常见的一些状态码,但是实际上HTTP状态码,种类非常多!Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第27张图片


响应“报头”

响应报头的基本格式和请求报头的格式基本一致.
类似于 Content-Type , Content-Length 等属性的含义也和请求中的含义一致


Content-Type

请求 和 响应中的 Content-Type ,是不一样的。
响应中的 Content-Type 常见取值有以下几种:
text/html : body 数据格式是 HTML
text/css : body 数据格式是 CSS
application/javascript : body 数据格式是 JavaScript
application/json : body 数据格式是 JSON

讲到这里,其实你就会发现,这里的知识点,就和前面讲过的前端知识连接起来。
所以,在这里大家要明确:我们当前写的这些HTML,CSS,JS,最终它是怎么样呈现给用户来去运行的呢?
其实都是靠 HTTP 协议,把这些相关的代码,文件,资源,通过网络,HTTP,加载到用户客户端电脑的浏览器上。然后,再用浏览器对里面的代码进行解析。
所以,获取内容,这样的一个过程。就是借助 HTTP。
那么,HTTP 必然也要承载 “这几种不同的数据”,在承载这些数据的时候,自然就是通过上述的这四种数据格式进行描述了。

关于 Content-Type 的详细情况: https://developer.mozilla.org/enUS/docs/Web/HTTP/Basics_of_HTTP/MIME_types


通过代码来构造 HTTP 请求

一、基于 HTML / JS

1、基于 from 表单
2、基于 ajax
本文主要是针对这两个进行讲解

二、基于Java

基于 socket
虽然这种方式,很少见。
但是,是完全可行的。
而且,在实际开发中用的不如上面的两个方式多。

原因很简单。
想要构造一个 HTTP 请求,肯定是由客户端来构造 HTTP 请求。
最常见的 HTTP 客户端,就是浏览器。
因此,我们浏览器 基于 HTML / JS,这一套机制来构造 HTTP请求,这是属于特别顺利成章的事情。
故,作为一个 web 开发程序员,还是需要重点去学习 HTML / JS 这一套。

像下面,这种基于 Java 来构造一个 HTTP 请求。
如果你要是写一个什么安卓手机APP,那这个时候你可能也会作为一个APP客户端。
那么,你就需要基于 Java 方式来去构造了。
这个了解即可,本文不做过多介绍。


基于 form 表单 构造 HTTP请求

核心 HTML 标签,就是 form 标签。

当前我们是把这样的请求,直接提交给搜狗主页了、
人家搜狗处理这样的参数了吗?
显然是没有!

这就跟表白一样:你表不表白,跟人家理不理你,是两码事。
当前,我们所提交的请求,就是这样的一个情况。
页面还是该怎么显示,就这么显示,是没效果变化的。

后面自己写服务器,自己的服务器就可以针对前端提交来的参数进行处理,就可以实现一些不同的功能了。

另外,有一个点:
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第28张图片
当我们点击输入框,然后浏览器就展示出来了 前面我们输入的内容【或者是一些其他信息】,之所以有这种提示,这是因为我们之前在某个网页中(不限于我们编辑的页面)。填写过相应的信息。浏览器会自动记住我们之前输入过的表单的内容,并且在后面我们填写某个其他的表单的时候自动给你提示。

这种行为,属于浏览器行为。不要慌!不是黑客入侵了!!
你的数据不值钱,不至于有黑客会惦记你。


基于 ajax 构造 HTTP请求

form 表单这种方式,是一个更加原始的构造方式。
使用 form 一定会涉及到 “页面跳转”。
你一旦点击了 提交 按钮,它就会进行 页面跳转。
一旦 页面跳转,浏览器需要加载出 全新页面。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第29张图片
这件事就是非常的不科学了,尤其是页面非常复杂的时候。

所以,我们为了进一步提高页面加载的效率。
我们就希望:能够让页面不去全部加载,而是只加载其中需要变化的某个小部分。
这个情况,就可以使用 ajax 了。
ajax:就是通过 js 代码,来构造出 HTTP 请求。
再通过 js 代码 来处理这里的响应,并且把得到的一些数据给更新到页面上。

细节拓展:ajax 名字中的细节
ajax:Asynchronous JavaScript And XML
JavaScript:js
and:和
XML:数据的一种组织格式。
至于 Asynchronous:异步的
与 synchronized(同步),不是同一个词哦

异步概念,在计算机中,非常常见的一个概念。

举例子来加深对 异步概念的理解:找女朋友吃饭。
我提前约好了她,中午一起去吃个饭。
等到中午,我到了她家楼底下,然后就给她打电话“你准备好了,一起吃饭去?”
她说:“好的,我已经准备好了。你稍微等一会,我马上下来!”

注意!男生 和 女生 口中的 “稍微” 和 “马上下来”,是有着巨大差异的。
男生说:“马上就来”,那是屁颠屁颠的就马上就下来了、
女生说:“马上就来”,那起码是 坐等半小时起步。
什么洗脸,洗头发啊,化妆啊,选衣服等…我感觉半小时下来的都算快了。
除非自己的女朋友是素颜女神,那就可能就很快了。

然后,大部分情况都是前者。
于是,我们在楼下等了接近半小时,迟迟没有听到下楼的动静。
然后,我们再一次打电话,你怎么下来,你到底在干什么,是不是还没起床?
它说:马上!
接着,我们又在下面等了半个多钟头,才看到她人。

在上述过程中,这里的等待,就是一个“同步”的等待。
换句话来说:我是调用者,我女朋友是被调用者。
调用者会一直在这里等着,主动来获取到 被调用者的结果。


注意!此处说的“同步”,和Java中的“同步”,不是同一个东西。
一个计算机术语,在不同的上下文中,表示的意思,是可能不同的。


那么,什么情况是属于异步的呢?
还是上面的例子:约女朋友吃饭。
我到了她家楼底下,然后就给她打电话“你准备好了,一起吃饭去?”
她说:没有,马上!
我呢!就和她说:“我就先找个凉快的地方,玩会手机。你下来了,打电话通知我一声,我来接你。”
简单来说,我就不站在太阳底下傻等了,找个地方歇会,做一些其他的事情。

也就是说调用者在发送一个请求之后,就不管了。
等到被调用者结果出来之后,会主动来通知调用者。

这种情况就是异步。


注意!
同步等待中,有两种情况:
1、阻塞式的等(不见不散)
2、费阻塞式的等(每隔一段时间,就去查询一下结果)
我们上诉例子中的第一种情况,就是 2 情况。

这样吧,我再举个更形象的例子,让你们更容易去区分 同步 和 异步 的区别。
我去吃饭,
然后,我就来到一家小炒店。
我说:“老板,来个炒河粉。”

1、同步阻塞等待情况
我就站在前台那里,盯着后厨来炒粉。
直到粉炒好,我自己端走。

2、同步非阻塞等待。
我就在前台,瞟了一眼后厨的情况,发现还没有轮到我。【粉没有炒好】
于是,我就出去溜达一圈。
溜完回来,回到前台这里,问了下前台,结果是还没炒好。
于是,我找个位置,玩会手机。
经过若干次之后,粉炒好了,我就自己端走了。

3、异步等待
我点完餐,就直接什么都不管了。
直接找个角落左下,玩手机,该干嘛干嘛。
等待 粉炒好了之后,人家直接给我端上来。

同步和异步之间的区别,主要就是看这个结果是 调用者 主动关注,还是 被调用者 来给 调用者 通知。
简单来说,就是看这个结果,是调用者主动去查询到,还是 结果 被推送给 调用者的。

阻塞 和 非阻塞,区别是:等的过程中,能不能做其他事情。
阻塞:就干不了其他的事情。
非阻塞:还能做点别的事情。

同步非阻塞等待 和 异步等待,这两种方式,都是属于等的过程中,可以做别的事情。唯一不同的是:异步有结果通知,同步需要自己去查询结果
也就是说,同步非阻塞等待 对于 调用者 来说,开销要更大!
因为需要反复去查询结果
相比于 异步等待 来说,异步方式,往往是更好的。

这三种情况,一般在 与 IO(输入输出) 相关的场景中,经常会涉及到。
IO 就包含:你通过控制台输入输出 / 通过文件输入输出 / 通过网络输入输出

我们刚才讲的 ajax,就属于 通过 网络输入输出 的情况。
所以,ajax 就会涉及到上述的三种情况之一。
ajax 所使用的等待方式:异步等待

下面我就来看看 ajax 具体的实现情况。
ajax 就是属于 基于 一步等待 的方式来进行的。
首先,先构造出一个 HTTP 请求,然后发给服务器。
那么,在发送 HTTP 请求之后,服务器什么时候返回响应,浏览器是不确定的。
浏览器采用的决策:
先不管它这个请求,是否有回应了。
浏览器里面就继续执行其他的代码。
该干嘛干嘛,你有没有响应回来,我都无所谓。
如果服务器响应回来了,再由浏览器通知我们对应的 JS 代码,以回调函数的方式来处理响应。

下面,我们就来结合具体代码,来进行演示。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第30张图片
上面这里是通过原生 JS 的 ajax 来构造请求,并处理响应的。
原生的写法非常麻烦,也比较抽象,不好理解。
因此本文不再介绍这种方式(我也不会)。
我们来使用一个更加简单,也更好理解的方式 来构造请求。

基于 jQuery 中的 ajax 来演示 相关代码。

这个在 博客系统页面设计 中,引入 markdown 编译器的时候讲到过。
markdown 的引入,需要依赖于 第三方库 jQuery。
jQuery 是 JS 世界中,最知名的库(没有之一)。
曾经的 jQuery 在 JavaScript中的地位,相当于是 Spring 在 Java 中的地位。
最近几年 jQuery 的风头 被 JS 新生的 一些框架给抢走了不少。
现在就是 Vue,React,Angela,这三大框架。

接下来,我们要做的就是 引入 jQuery。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第31张图片
下面,我们就来看一下效果。

目前,ajax了解即可,知道ajax 是不被浏览器允许进行跨域操作的。
后面学习到相应的位置,自然会再做进一步的讲解。


基于 Java - socket 来构建 HTTP请求

这里不是讲解的重点,简单讲一下。
Java 构造一个HTTP请求,主要就是基于 TCP socket。
按照 HTTP 请求的报文格式,构造出一个匹配的字符串。
再写入 socket 既可。

可以参考一下
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 isbody");
                System.out.println(postResp);
    }
}

在实际开发中,确实也会有一些基于Java 构造 HTTP 请求的情况。
这些情况,其实都可以基于第三方库来实现。
不一定非得要直接使用 socket,因为socket 使用起来比较麻烦。
你从代码量就可以看出来的。


总结

关于 HTTP,我们主要还是去深入理解好 HTTP 的 请求格式 与 响应格式,以及能够去熟练使用 fiddler,
掌握了这些东西,其实 HTTP 的理论基础部分,我们就差不多了。
有了这些,再去做一些网站类的开发,其实我们就有了最底层的基石。
后面,我们写代码的时候,不光能帮助我们更好的去理解:程序 与 后端 是如何进行交互的。同时如果出现了一些问题,也容易进行排查和定位的。


HTTPS

HTTPS 相当于 HTTP 的 孪生兄弟。
HTTPS 在 HTTP的基础之上,引入了一个加密层。
加密,就是数据进行加密。

谈到 HTTPS,就不得不说到 运营商劫持了。
下载一个 天天动听
未被劫持的效果, 点击下载按钮, 就会弹出天天动听的下载链接
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第32张图片
已被劫持的效果, 点击下载按钮, 就会弹出 QQ 浏览器的下载链接
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第33张图片
由于我们通过网络传输的任何的数据包都会经过运营商的网络设备(路由器, 交换机等), 那么运营商的网络设备就可以解析出你传输的数据内容, 并进行篡改

点击 “下载按钮”, 其实就是在给服务器发送了一个 HTTP 请求, 获取到的 HTTP 响应其实就包含了该 APP的下载链接. 运营商劫持之后, 就发现这个请求是要下载天天动听, 那么就自动的把交给用户的响应给篡改成 “QQ浏览器” 的下载地址了
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第34张图片
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第35张图片
那么,如何避免这种情况呢?
就是加密!
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第36张图片


HTTPS的工作过程

既然要保证数据安全, 就需要进行 “加密”.
网络传输中不再直接传输明文了, 而是加密之后的 “密文”.

另外,前面也说到过,加密和解密这件事情,本身就是一个和数学密切相关的事情。
数学你懂的。。。所以我们此处,只能简单的讨论一下流程。
无法讨论 加密 和 解密 的“实现细节”。

另外,大家要明确一点:加密之后,也不是就绝对安全,只是说破解起来计算量很大,成本很高。【破解的成本 高于 数据本身的价值】
这么说吧,哪怕使用当前最牛逼的计算机,破解起来也需要个几十年,上百年的。我们就认为是安全的。
毕竟保护个十几年,数据早就“过期了”,价值也早就不存在了。
所以破解这样的数据,没有意义。
总得来说,只要破解的成本高于数据本身的价值,就是安全的。

举个例子:
不知道大家有没有看到这样的一个新闻。
有一个团伙,造假钞。
造的贼好,以至于验钞机根本区分不出来。
但是呢!有一个问题:假钞是造出来,一张 100 元的假钞,但是它的成本价 110元
这就是很dan疼了。还倒贴。

HTTPS 中引入的加密层,称为SSL / TLS、

SSL,是属于以前旧的叫法
TLS,是属于新的,升级过的新叫法。
其实这两种叫法,可以认为是同一个东西。

SSL / TLS 加密的方式有很多, 但是整体可以分成两大类: 对称加密 和 非对称加密.


对称加密:使用同一个秘钥,就可以进行加密,也可以进行解密

Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第37张图片

这里解决问题的关键,就是需要引入,非对称加密了。


非对称加密

非对称加密,有两个密钥,分别叫做 公钥 和 私钥。

公钥:就是人人都能获取到
私钥:就是只有自己才知道
就可以使用公钥来加密,使用私钥来解密。
或者,使用私钥加密,公钥来解密。

能否根据公钥,推导出私钥?
理论上是可行的,但是实际上不行。
因为计算量太过于庞大。


直观上理解公钥和私钥:
很多小区,单元门口,有一个‘信箱’。
邮递员把信放入对应的信箱里,然后拿一个锁头,把信箱锁住。
此时的情况,就是我们有一把钥匙 和 n把锁头。
然后,我们把这些锁头发给邮递员。
每个邮递员都有你邮箱的锁头,故每个邮递员都可以凭着这个锁头,把信锁到你的信箱里。只有我们自己持有这这把钥匙,能够开箱,拿出里面的信。

此处的锁头,就相当于是公钥,我们自己的钥匙就相当于私钥。
基于私钥来进行解密,获取到里面的内容。
【在实际情况中,在密码学里,这是一个复杂的数学变化的过程】

因此,我们就可以基于非对称加密,就可以让服务器自己生成一对公钥和私钥,公钥发出去(惹人都能拿到),私钥自己保存。
在前面不是说,客户端生成一个对称密钥嘛,客户端就可以使用服务器的公钥。
对 对称密钥 进行加密,然后把数据传给服务器,服务器再通过私钥进行解密。

简单来说:
客户端生成一个对称密钥,使用服务器发送的公钥,对其进行加密。
再发送给服务器,服务器通过自己的私钥进行解密,就能获取对称私钥。
可以这么去理解,如果你没有私钥,是读取不到 对称密钥的。
需要拥有 服务器的私钥才能 读取 / 解密。
公钥,只是让外来者看到他应该看到的东西,私密的东西,通过公钥是看不出来的。
而且 在传输的过程中,并没有涉及到私钥的传输。
所以私钥,也就无法获取到。
因此,中间的网络设备没有私钥, 即使截获了数据, 也无法进行解密, 也就无法获取到对称密钥,更别提还原出内部的原文,并且篡改了。
【总得来说,公钥就是一把人都能看到的锁,私钥就是唯一能打开这把锁的钥匙】
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第38张图片
有的人会好奇:既然非对称加密这么好使?
还要对称加密干什么?直接非对称一把嗦,就行了啊!
注意!
这是不行的!!
因为在实际实现中,对称加密的计算开销 是远远小于 非对称加密的计算开销的。
换句话来说:对称密钥算的快,非对称算的慢。
你光使用 非对称加密 进行传输密钥,因为这个密钥只传输一次,数据量也不是很大。
所以是用非对称来加密一次,开销还不是很大。
但是!如果你后续每次加密都是通过 非对称 来进行加密的,那么开销会非常大。
算死你,哦,不对。
快看你电脑再冒烟!!!

如果只是少来少去的用用这个非对称加密,那还好,成本也不是很大。
但是如果所有的数据都走非对称加密,这个事的成本就太大了。

引入了 非对称加密之后,感觉座位踏实了不少。
其实,非也!!!
这仍然存在着一个非常大的漏铜!!!

问题就出在:服务器要把自己的公钥返回客户端。
在这个操作中,就可能会涉及到一个非常经典的“中间人攻击”。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第39张图片

既然存在 “中间人攻击”,我们又该如何去处理呢?

关键要点:得让客户端能够确认,当前的公钥是来自于服务器的,而不是黑客伪造的。

验证身份,也好办。
例如:
我们去网吧,或者去住旅馆。
都是需要 登记 身份信息的,你要是没有一个明确的身份,是不会让你进去的。
如何验证我们的身份?我们有身份证啊!
假如,我们现在要去上网,来到吧台。
把 钱 和身份证 给网管说:“网管,开机子,先来2个小时!”
网管收到钱和身份证之后,就会把你的身份证一刷,就获取到你的身份信息了。
其真实目的,就是为了验证你的身份信息。
在网吧待过的都知道,网吧 和 警察的关系。
这一刷,其实就是在访问公安局的相关服务器,来验证你的身份信息。
如果你那个假的身份证给别人一刷,那就不露馅了嘛。
当然,真的要是拿个假身份证去刷卡,网管也不会面露异色。
等你上机之后,偷偷打电话报警。
然后,等你反应过来的时候,你的身边已经站在两名同志了。。。

因此,当前这里面如何验证 公钥 是服务器的,而不是黑客的?
其实,我们就需要一个第三方的公证机构(类似公安机构的存在)来证明这个公钥是一个合法的公钥。
因为我们是信任公信机构的(就像我们信任警察一样),公信机构说这个公钥OK,我们就认为这个公钥可信。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第40张图片
以上描述的东西都是包含在 SSL / TLS 中的,SSL 不仅仅是应用于 HTTPS,很多其它地方也会用到 SSL。

有一个问题:既然 HTTP数据都已经加密过了。
为什么 fiddler 仍然能抓到并解析 HTTPS 里的数据报呢?

照理说,都进行非对称加密了。fiddler应该是获取不到 对称密钥的。
也就不应该能获取到里面的信息的啊?

还记得前面的叫你们下完fiddler,第一件要做的实际,就是 设置 捕获 HTTPS 吗?
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第41张图片
当我们勾选这些的时候,它会弹出一个对话框。
这个对话框,就是 fiddler 能捕获 HTTPS 请求,并且解析的关键。
这个对话框里面是一些 fiddler的证书。
之所以,让大家去点击是(yes),是为了让操作系统信任 fiddler 提供的证书。
相当于用户给 fiddler 授权了,允许 fiddler 进行 “中间人攻击”。
换句话来说,fiddler 就像是我们信任的人,去帮我们拿快递。
我们不用去担心,他会拿着东西跑路。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第42张图片


完整流程

左侧都是客户端做的事情, 右侧都是服务器做的事情
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第43张图片


总结

HTTPS 工作过程中涉及到的密钥有三组.

第一组(非对称加密): 用于校验证书是否被篡改. 服务器持有私钥(私钥在注册证书时获得), 客户端持有公钥(操作系统包含了可信任的 CA 认证机构有哪些, 同时持有对应的公钥). 服务器使用这个私钥对证书的签名进行加密. 客户端通过这个公钥解密获取到证书的签名, 从而校验证书内容是否是篡改过.

第二组(非对称加密): 用于协商生成对称加密的密钥. 服务器生成这组 私钥-公钥 对, 然后通过证书把公钥传递给客户端. 然后客户端用这个公钥给生成的对称加密的密钥加密, 传输给服务器, 服务器通过私钥解密获取到对称加密密钥.

第三组(对称加密): 客户端和服务器后续传输的数据都通过这个对称密钥加密解密.

其实一切的关键都是围绕这个对称加密的密钥. 其他的机制都是辅助这个密钥工作的.

第二组非对称加密的密钥是为了让客户端把这个对称密钥传给服务器.
第一组非对称加密的密钥是为了让客户端拿到第二组非对称加密的公钥


Tomcat

Tomcat 是什么?

谈到 “汤姆猫”, 大家可能更多想到的是大名鼎鼎的这个:
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第44张图片
事实上, Java 世界中的 “汤姆猫” 完全不是一回事, 但是同样大名鼎鼎.
在这里插入图片描述

虽然这个猫很挫,但是名声相当大。
Tomcat 是一个 HTTP 服务器.
前面我们已经学习了 TCP / UDP 服务器。
我们 知道 HTTP 协议,也可以基于TCP协议来实现。

其实 HTTP 服务器,就是在 TCP 服务器的基础上,加上了一些额外的功能。
1、能够解析请求中的 HTTP报文:把请求转换成结构化数据(对象)
2、可以很方便的构造 HTTP 响应。
进一步来说,HTTP 服务器提供了一组API,方便程序员直接调用,来操作HTTP协议。
从而简化程序员的开发工程。

其实说白了 Tomcat 就是一个服务器。
基于 TCP 服务器,实现了 HTTP协议的 解析 和 构造。
那么,这个时候,当我们用户再去使用 HTTP 服务器的时候,那就非常容易了。
我们就只需要调用Tomcat里面的 API,就可以实现一个自己 自定制的,一个专有的 HTTP服务器。

另外,大家需要明确一点:
HTTP服务器,是属于一个很大的类别。
这个分类下面,其中就包含了很多很多具体的实现,
Tomcat 只是其中的一种。
Tomcat 在 Java 这个圈子里,是最知名的一种。

但是实际上,HTTP服务器的整个圈子很大。
除了 Tomcat 之外,还有很多其它知名的 HTTP服务器。
不同的生态环境下,所涉及到的 服务器 也是不一样的。

我们作为一个 Java 程序员,主要还是了解 Tomcat。
但是呢,我们以后在工作中,可能还会接触到一些其他的HTTP服务器。
其中可能性最大的就是 Nginx
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第45张图片


Tomcat 的 下载安装

在 Tomcat 官网下载即可. Tomcat 版本很多, 我们最常使用的是 Tomcat 8
链接:https://tomcat.apache.org/download-80.cgi

Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第46张图片
以上就是 Tomcat 中,比较重要的目录。
我重点给大家讲了4个目录:
1、bin:Tomcat的 启动 / 停止 脚本
2、conf:Tomcat 的 配置文件
3、logs:日志
4、webapps: servlet 程序 / war包 / 网站。

正因为 :一个Tomcat上面可以同时部署多个 Servlet 程序,所以,也可以把 Tomcat 称为 “Servlet 的容器”,简称“容器”。

这是网上经常会看到的 一种说法:Tomcat 是一个容器。

容器:这个词,在计算机这个圈子里,已经被用烂了。
到处都能看到“容器(Container)”的概念。
比如:
容器1:Java不是有集合类嘛,这个东西,对应在C++里面,就被称为是“容器”。这就导致了有些 Java的程序员,也把 集合类 称为 “容器”。

容器2:Tomcat 是 Servlet 的容器,一个Tomcat上面可以承载 多个 webapp

容器3:Spring 可管理很多很多个 Bean 对象,Spring 也可以称为 Bean 容器。

容器4:Docker 也是涉及到 容器 的概念。Docker可以认为是轻量级的虚拟机。
轻量级的虚拟机:软件模拟出来的一个计算机
一个 Docker 程序里,可以有 多个轻量级的虚拟机。
每个轻量虚拟机里面又可以包含很多程序。
我们就把这个 轻量级的虚拟机 称为 “容器”。
进一步的也把 Docker 也称为 容器了。
。。。。
顶着 “容器”名字 的 东西太多。
所以,大家以后在看到 “容器” 这个词的时候,一定要擦亮眼睛。
仔细的结合上下文,来分析分析 当前这个 “容器”到底是指什么!
不要把不同的容器,混为一谈!!


如何使用 Tomcat

第一步:启动

前面也讲到过:进入 Tomcat的 bin 文件。
在里面 找到 startup.bat,双击它,即可启动。
但是!有坑!
Tomcat 启动失败 / 闪退 的第一个原因:环境变量的设置。

有细心的朋友会发现,这里有一部分都是乱码诶?
这是因为 Tomcat 内部使用的编码方式是 utf8(utf8 就是咱们当下最主流的编码方式)
换句话来说:
当前的数据是按照 utf8 来构造的,但是 cmd 在显示的时候按照了 GBK 的方式来解析。
这个时候,输出的方式 和 输入的方式不匹配,势必就会出现乱码。
如何解决乱码,思路也很简单!

统一编码方式:把 Tomcat的编码方式 修改成 GBK 格式,又或者把 cmd的编码方式改成 utf8.
改成 GBK 显然不符合当下主流,pass掉。
那么,只能考虑 把 cmd的编码方式改成 utf8.
但是这种改动,不是一件容易的事情!
因为 cmd 自身并没有提供设置字符集的功能!!!!

这样的话,唯一可行的方法,就是去修改电脑的注册表。(但最好别用!!!)
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第47张图片
所以,我们的最终解决的办法,就是“凉拌”!
乱码显示就乱码显示吧!就这么用!
因为,现在只是暂时这样给大家看下,Tomcat的运行结果。
我们后续使用 Tomcat,还要其它的方式
1、结合 idea 来使用
2、直接在 linux 上使用
这两种方式,都不会产生乱码的效果。
因此,当前的乱码问题,就可以忽略了!


Tomcat 启动失败 / 闪退 的第二个原因:端口好被占用了。

Tomcat是一个 HTTP服务器,启动的时候要绑定端口。
一般是绑定两个端口号 8080,8005中的一个。
如果这个端口已经被占用了,此时你启动Tomcat,肯定是失败的。
启动失败的效果,就是闪退。

最典型的情况:就是你已经启动了一个Tomcat,然后你再次尝试启动第二个Tomcat。
那么你第二个 Tomcat 妥妥的失败。

还有一个情况:
就是说,有些朋友,在环境变量配置好的情况下,连第一个Tomcat也启动不了.
此时,很可能就是他(她)的电脑上有别的程序占用了 8080/ 8005 端口号。

比如:有些同学在学校也学过 web 开发,配置 Windows 自带的 IIS 服务器。
这个服务器默认端口号也是 8080 / 8005。
如果 IIS 在运行中,那么你的Tomcat 是启动不了的。

我们需要做的是:找到是哪个程序占用了 8080 / 8005 端口号,关闭对应的程序。
或者,给 Tomcat 换个端口。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第48张图片
总得来说,Tomcat 启动不了,原因无非就是上面的两条、
1、配置环境变量
2、端口号被占用


使用浏览器来访问 Tomcat

把 Tomcat 启动完成之后,就可以使用浏览器来访问 Tomcat.
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第49张图片
光看这个欢迎页面,没什么意思。
如果能把自己部署的一些页面放到 Tomcat 上去,那就还有点意思。
可参考网络协议之TCP/IP协议

虽然我们部署好了页面到 Tomcat 上,但是这里还有个问题。
当前如果页面简单还好,直接放到 ROOT 就行了。
但是!
如果当前页面比较复杂:若干个 HTML,还依赖了一些 CSS,图片,JS…
此时,如果全部堆在这个 ROOT 目录里,就非常不优雅!

如果你想放到 云服务器,也很简单。
将对应的页面文件,压缩一下。
然后云服务器上,找到webapps文件,并进入到 webapps 文件里面。
然后,把刚才压缩的包,直接拖动到 云服务器里。
该压缩包就会自动上传。

接着对其进行解压缩:unzip 压缩包全名(带后缀的)
接下来,就是一样的。
重启一下服务器:
cd 两个点点(是符号的点点啊!)   :回到上级目录 webapps底下
cd bin : 进入bin 文件
sh shutdown.sh :关闭Tomcat服务器
sh startup.sh:启动Tomcat服务器
然后,就在浏览器的地址栏输入 外网IP + 冒号 + 端口号 + 文件目录 + 文件里面的具体文件全名【带后缀的】
回车之后,使用云服务器的朋友就可以看到页面显示效果了。

其实我们把写好的页面程序放到Tomcat上,这就相当于 “发布 / 上线” 的过程。这样一发布之后,外面的普通用户都是可以访问到这里的页面的。
【别人能访问到你的网页的前提是你得有一个外网IP】
【换句话来说,你得有钱租一个云服务器】
注意!我们写页面是为了给别人看的,给别人使用,这是我们写页面的初衷。
而不是写出来之后,一个人在那里自嗨。


总结

以上内容,都是基于 Tomcat 来部署 “静态页面”。
静态页面:页面内容是固定不变的。

后面还需要学习 基于 Tomcat 来生成 动态页面。
根据用户输入的不同,会得到不同的结果。

搜狗的页面就可以当做是一个 静态页面。
无论怎么去刷新,这个页面都是一样的效果。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第50张图片

也可以将搜狗,当做是一个动态页面。
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第51张图片
更为明显的是,我们输入关键字之后,所进入的页面就是一个不折不扣的动态页面。
根据用户输入的不同,会得到不同的结果、
Tomcat 和 HTTP协议 - JavaEE初阶 - 细节狂魔_第52张图片

其实 web 开发主要的工作就是在 动态页面 这边。
因此接下来重点学习的,就是 动态页面的构造。
动态页面的构造:其实就是学习 Tomcat 给程序员提供的这组用来操作 HTTP 的 API。
我们给 这组 API,单独起了个名字:Servlet。

Servlet,可以说是在Java世界中,进行 web 开发的一个基石。
后续,我们未来用到一些像 Spring 这样的框架,其实它也是基于 Servlet 这样的机制,进行了进一步的封装。

Servlet,好不好学?
后面讲 Servlet 的时候,你就会发现:
这里的 API,其实 和 HTTP协议,是关系非常紧密的。
如果你非常熟悉 HTTP协议,那么这个API,你会学的非常轻松!

但是整体来说,Servlet 这里的学习难度还是比较大的。
难度大的原因:
不是因为 代码,而是因为这个环境不好搞。

好了,本文到这里结束了。
下面一篇,就是讲解 servlet了。

你可能感兴趣的:(JavaEE初阶,JavaEE初阶)