通过网络初识,我们认识了网络的协议栈,TCP/IP 分为五层:应用层,传输层,网络层,数据链路层,物理层。也介绍了其中的关键协议。而这些协议的理解,是我们写网络代码的基础。
应用层,是程序员打交道最多的层次。
与应用程序直接相关,程序员写的代码,只要是涉及到网络通信的,都可以视为是应用层的一部分~~
应用层这里的东西是和程序员直接相关的,而且应用层中设计到的网络通信协议,很多也都是程序员自定制的。(也就是你自己写代码的时候,发明创造出来的协议。)
那具体如何自定义协议呢?
自定义协议,分为两个阶段:
1.根据需求,明确传输哪些信息。
就拿外卖平台举例:
首先,会显示一个商家列表。
客户端 - 服务器 传递的信息(根据需求来的)
请求:
用户的位置信息(经纬度,用户的id)
响应:
商家的id,商家的名字,商家的图片,评分,配送费,种类[…]
2.约定好信息组织的格式
有很多种方式:
1)行文本的方式
上述给出的方案,只是随便写的,实际约定的时候,可以有很多变数的。
列和列之间,不一定使用“,”,也可以使用“;”,也可以使用“.”也可以使用 “\t”
多个数据之间,也不一定使用 “\n”,也可以随心所欲使用任何你喜欢的分隔符。
自定义协议,你就是说了算的那个,只要客户端和he服务器都按照这同一套规则来进行构造/解析数据就行了。
(如果客户端和服务器是你一个人搞定,随便怎么约定都行,如果客户端和服务器是两个人搞,需要你两商量好,达成一致。)
2)通过 xml 格式来约定请求和响应的数据。
说起 xml 可能不太熟悉,但说起 html ,就不那么陌生了,最起码听过。
html 和 xml 都是成对的标签构成的键值对结构,而 html 标签内容都是固定的,大佬们约定好的,你不能乱写,也不能创建新的标签。(现在 html5 允许自定义标签了)但 xml 标签内容是自定义的。
xml 是用来网络传输的,和浏览器怎么显示无关,html 是约定浏览器是怎么显示的。
xml 可以用于很多场景,组织一段格式化数据用来网络传输,作为配置文件等…
对于 xml 方案,放到十年前,用的还是挺多的,但是现在,已经很少用 xml 进行网络传输了。
xml 的优点:可读性好。
缺点:冗余信息太多了,网络传输中,消耗更多的带宽。(对于服务器来说,带宽是最贵的)
3)使用 json (json 当下最流行的网络数据格式组织的方案)
json 的优点:可读性也是很好的,消耗的带宽,也比刚才谈到的 xml 更节省
缺点:还是存在冗余信息。
4)protobuf
基于二进制的格式,对数据进行压缩,不涉及到 json/xml 冗余信息了。带宽消耗最少,可读性就变差了。
对于 protobuf 在性能要求高的场景就需要使用,如果性能要求不高,还是建议使用 json。
它是用开发效率换执行效率(比较少)
总结一下:
数据组织格式:
1.文本行(最原始)
2.xml (比较原始,可读性好,冗余较多)
3.json(主流的方式,可读性好,冗余一般)
4.protobuf(高性能场景下使用的方式,可读性差,冗余最小)
但凡实现一个具体的程序,写代码之前,一定是要先约定应用层协议的格式的,应用层这里,除了自定义协议之外,也有一些大佬们现成搞好的协议了。
(FTP 文件传输, SSH 远程操作主机, telnet 网络调试工具, HTTP 协议[重点]…)。
HTTP 协议是当前 web 开发中最核心的协议,使用网站,都会用到 http,并且 Spring,也是围绕 http转。
我们打开一个网站我们会看见 https 。 https = http 基础上 + 安全层(S 表示安全层 SSL)
HTTP
HTTP是什么
HTTP(全称为“超文本传输协议”)是一种应用非常广泛的 应用层协议。
“超文本”的含义,就是传输的内容不仅仅是文本(比如 html,css这个就是文本),还可以是一些其他的资源,比如图片,视频,音频等二进制的数据。
HTTP诞生于1991年,目前已经发展为最主流使用的一种应用层协议。
HTTP 往往是基于传输层的 TCP 协议实现的。(HTTP1.0 , HTTP 1.1 , HTTP 2.0 均为 TCP,HTTP 3基于 UDP 实现)目前我们主要使用的还是 HTTP 1.1 和 HTTP 2.0。这里以 HTTP 1.1 版本为主。
HTTP 是一问一答 模式的协议。客户端发一个请求,服务器就返回一个响应,请求和响应一一对应。
(网络通信中也有其他的模型:多问一答(上传大文件),一问多答(下载大文件),多问多答(远程控制(todesk)),由于浏览器打开网页的场景/手机app加载数据的场景,就是典型的一问一答场景,所以使用 HTTP 就非常合适。)
抓包工具的使用
理解 HTTP 报文格式时,需要搭配一个重要的工具,进行学习,抓包工具。
抓包工具就相当一个”代理“,它能够获取到网络的数据包,详细的格式都解析出来了。
代理就可以简单理解为一个跑腿小弟,你想买瓶可乐,但是又不想自己买,所以你就把钱给你的跑腿小弟,跑腿小弟来到超市把钱给老板,老板再把可乐拿过来交到你的手上,在这个过程中,这个跑腿小弟对"你"和"老板"之间的交易细节,是非常清楚的。这个跑腿小弟就被称为正向代理(代表客户端干活),当然也有反向代理(代表服务器干活)
电脑上所有的网络通信,都会先发给这个抓包程序,抓包程序再把数据转发给服务器。
还有,在我们使用抓包的时候,一定要关闭梯子/梯子类的浏览器插件,可能会和抓包工具冲突。
抓包工具就以 fiddler 为例(fiddler 是专门抓 http 的,功能更简单也够用,使用更简单)。
安装完后一路next就好了。
电脑上很多程序,不安分,都在后台偷偷的做一些事情。
左侧窗口为当前的请求/响应的列表(fiddler 只抓 http)
字体的颜色:
红色表示报错 蓝色表示这个请求得到了这个网页 绿色的表示得到了一个 js 灰色的表示这个响应的数据已经被缓存了
随便双击一个 http ,随后点击右边窗口
- 左侧窗⼝显⽰了所有的 HTTP请求/响应, 可以选中某个请求查看详情。
- 右侧上⽅显⽰了 HTTP 请求的报⽂内容. (切换到 Raw 标签⻚可以看到详细的数据格式)。
- 右侧下⽅显⽰了 HTTP 响应的报⽂内容. (切换到 Raw 标签⻚可以看到详细的数据格式)。
- 请求和响应的详细数据, 可以通过右下⻆的 View in Notepad 通过记事本打开。
- 可以使⽤ ctrl + a 全选左侧的抓包结果, delete 键清除所有被选中的结果.
抓包结果
HTTP请求的结果:
HTTP响应的结果:
协议格式总结:
HTTP请求
URL
平时我们俗称的 “⽹址” 其实就是说的 URL (Uniform Resource Locator 统⼀资源定位符)。互联⽹上的每个⽂件都有⼀个唯⼀的URL,它包含的信息指出⽂件的位置以及浏览器应该怎么处理它。(描述网络上的唯一资源的位置)
url的格式:
具体的url:
URL 中的可省略部分
- 协议名: 可以省略, 省略后默认为 http://
- ip 地址 / 域名: 在 HTML 中可以省略(⽐如 img, link, script, a 标签的 src 或者 href 属性). 省略后表⽰服务器的 ip / 域名与当前 HTML 所属的 ip / 域名⼀致.
- 端⼝号: 可以省略. 省略后如果是 http 协议, 端⼝号⾃动设为 80; 如果是 https 协议, 端⼝号⾃动设为443.
- 带层次的⽂件路径: 可以省略. 省略后相当于 / . 有些服务器会在发现 / 路径的时候⾃动访问/index.html
- 查询字符串: 可以省略
- ⽚段标识: 可以省略
关于URL的encode
URL 中本身就有一些特殊符号,代表不同的特殊含义 “: / ? # & = …”。
由于query string的内容,是程序员自定义的,万一 query string 里也包含了特殊含义的符号咋办??
因此,某个参数中需要带有这些特殊的字符,就必须先对这些特殊字符进行转义。
一个中文字符由UTF-8或者GBK这样的编码方式构成,虽然在URL 中没有特殊的含义,但是任然需要进行转义,否则浏览器可能把UTF-8/GBK编码中的某个字节当作URL中的特殊符号。只不过很多浏览器为了用户看起来方便,显示的时候显示转义之前的,实际上,抓包中就能看到,是已经转义的数据。
转义的规则如下:将需要转码的字符转为16进制,然后从右到左,取四位(不足四位直接处理),每两位做一位,前面加上%,编码成%XY格式。
这里面“+”被转义成“%2B”。
urldecode就是urlencode的逆过程。
认识“方法”
这里面最重要的就是 GET 和 POST方法,其次就是PUT和DELETE 方法。其他了解就行。
- GET方法
GET 是最常⽤的 HTTP ⽅法. 常⽤于获取服务器上的某个资源。
在浏览器中直接输⼊ URL, 此时浏览器就会发送出⼀个 GET 请求。
另外, HTML 中的 link, img, script 等标签,获取CSS,获取JS等操作也会触发 GET 请求。
在上面的结果中可以看到:
最上面的
是通过浏览器地址栏发送的GET请求。
也可以观察请求的详细结果。
GET请求一般都是没有 body 的,如果需要通过 GET 给服务器发送一些数据,通过 query string 传递过去的。
- POST方法
两个典型的场景:
1、登录
2、上传 => 请求带有正文的。正文就是保存了当前上传的数据的内容。
上述请求中,图片本身是二进制的,通过特殊方式进行转码(base64编码,把二进制转成文本),其实body 也是可以直接填二进制数据。
POST 请求的特点:
- ⾸⾏的第⼀部分为 POST。
- URL 的 query string ⼀般为空 (也可以不为空)。
- header 部分有若⼲个键值对结构。
- body 部分⼀般不为空。 body 内的数据格式通过 header 中的 Content-Type 指定.。body 的⻓度,由 header 中的 Content-Length 指定。
谈谈GET和POST区别:
- 1、语义不同:GET一般用于获取数据,POST一般用于提交数据。(语义上可以混着用)
- 2、携带数据的方式:GET的body一般为空,需要传递的数据通过 query string传递,POST的 query string 一般为空,需要传递的数据通过 body 传递。(POST也是可以带有query string(还是有一些的),GET理论上也是可以带 body(更少见))
- 3、GET 请求一般是幂等的(HTTP标准文档给的建议,但只是建议,不是强制要求),POST请求一般是不幂等的。(如果多次请求得到的结果一样,就视为请求是幂等的)
- 4、GET设计成幂等了,就可以允许 GET 请求的结果被缓存,POST 由于不要求幂等,经常是不幂等的,就认为不能被缓存。
对于PUT和DELETE,实现restful风格的 api 的时候,会用到。
设计服务器接口的一种”习惯“
新增:POST
删除:DELETE
修改:PUT
查询:GET
这四个操作的任何一个,都可以进行增删查改,完全取决于你代码咋写。
认识请求报头(header)
header 的整体的格式也是“键值对”结构,每个键值对占一行。键和值之间使用“: 空格”分割。
报头的种类有很多, 此处仅介绍⼏个常⻅的
- Host:表示服务器主机的地址和端口
有的人可能会想到在 url 里面不是就有 IP地址(域名):端口号吗?为什么还要用Host再记录一下?
由于在绝大部分情况下,这两属性也是一致的,但是也是有一些特殊场景下是不一致的。那就是使用了 代理 ,使用代理之后,url 里的IP:端口号会改变,因此,即使使用了代理,也是可以通过 Host 来获取到最原始的目标是什么。
- Content—Length:表示 body 中的数据长度,单位是字节
HTTP 协议,传输层这里基于 TCP 实现的(版本号 <= 2.0),所谓的HTTP协议,就是把字符串构造成HTTP约定的格式。
把这一串字符串,写入到 tcp socket中,对于TCP 来说,一个连接上可以发多个请求,那服务器这边收到数据的时候就得区分一下,从哪里到哪里是一个完整的http请求数据。
没有body的 http 请求,读到空行,就可以认为是结束了。
有body 的 http 请求呢?先读取首行和 header ,读到空行,解析 header中的 Content—Length 根据这里的值,接下来再读取固定字节的长度。
- Content—Type:表示请求的 body 中的数据格式
提示了接收方如何解析 body 中的数据。
再 fiddler里我们可以直接看出 Content—Type
小知识:
我们来看一个Content-Type为js的body
这些东西都是合法的 js 的语句,只是看起来和咱们学的 js 是不一样的,本质语法都是同一套。像C++/Java这样的代码,会编译成二进制,发布给用户,用户拿到的是二进制代码,用户想根据二进制还原出源代码是什么样子的,是非常麻烦的。
由于 js 这样的代码则是把原始代码直接由用户浏览器下载到,用户就能看到 js 的原始的样子,因此,为了防止别人基于你的代码,搭建出一样的网页,我们会用专门的工具,把 js 做出变换,使得代码混淆(也就是在代码逻辑不变的情况下,把js 代码给改乱,让别人读起来的成本更高)。
对于 Content-Length 和 Content-Type ,如果请求有 body,并且没有这两个属性(哪怕只有一个)都认为是非法的/错误的http报文。
- User-Agent(简称UA):表示用户使用的设备的浏览器/操作系统的属性情况。
为什么要有UA?
随着互联网的快速发展,浏览器的版本也在不断更新,同一个时间段内,有些用户的浏览器,版本是比较旧的,支持的功能少,有些用户浏览器版本更新,支持的功能多,如果要是支持的功能少,可能就打不过竞争对手,要是支持的功能多,旧版本浏览器设备的用户,可能就展示不了。所以,要根据用户使用的设备进行区分,通过 UA 中的浏览器版本/操作系统版本,区分出当前用户的设备,最多支持哪些特性~因此程序员就需要维护多套代码,对于老的浏览器,返回功能少的网页,正确显示,对于新的浏览器,返回功能多的网页,体验丰富。此外 UA 还有另外一个用途:可以用来区分用户的设备。
windows/mac => PC 根据用户的设备,返回不同版本的页面
ios/android => 手机
User-Agent 之所以是这个样⼦是因为历史遗留问题.感兴趣的可以去搜一搜UA的故事。
- Referer:描述了当前页面的来源(这个页面是从哪个页面跳转过来的)
如果直接在浏览器中输⼊URL, 或者直接通过收藏夹访问⻚⾯时是没有 Referer 的.
意思是这个页面是从www.sougou.com 跳转过来的。
- Cookie
浏览器展示页面的过程中,页面里虽然可以通过 js 来实现一些逻辑,但是 js 代码无法访问你的硬盘(文件系统),浏览器给 js 带上的紧箍咒。但是在实际开发中,有的时候还是希望把某些数据能够保存到本地硬盘的。因此,浏览器引入了 cookie 机制。
cookie 就是浏览器允许网页在本地硬盘存储数据的一种机制,不是让网页代码直接访问文件系统,而是做了一层抽象,而是浏览器的 cookie 提供了键值对存储机制。
Cookie 这里是按照键值对的方式来存储数据的~
讲到现在,细心的小伙伴会发现我们已经提了很多遍 “键值对”了。
浏览器中可以直接看到当前页面保存的cookie有哪些。
cookie 按照域名维度来组织的,每个不同的域名下都可以有不同的 Cookie, 不同⽹站之间的 Cookie 并不冲突
这些都是程序员自定的。
浏览器保存了这些 cookie 之后,就会在后续给服务器发送请求的时候,把这些 cookie 键值对放到请求 cookie header 中传输给服务器。
cookie 到哪里去:最终发回给服务器。
cookie 从哪里来:也是从服务器这边来的.(后端开发程序员决定的)
Cookie 里的数据都是程序员自定义内容,业务相关的,但是有一个典型的场景,属于“通用业务”,登陆和用户认证。
会话:描述了客户端和服务器之间的一种交互关系
数据库,也是服务器。对应的客户端,就是你的应用程序 /Workbench…
你的这个客户端访问一次数据库服务器,这个中间的过程也可以称为是一次会话。
Cookie是可能会过期的(有的网站,对于安全性要求不高,过期时间就长,有的网站,对于安全性要求很高,过期时间就短),服务器返回 Cookie 的时候,是可以设置有效时间。有的 Cookie 中的 sessionId 过期了,此时就需要用户重新登录了。但用户重新登录的时候,是否需要重新手动输入一遍密码,就是浏览器的记住密码功能解决的问题了。
这个过程和去医院看病很相似.
- 到了医院先挂号. 挂号时候需要提供⾝份证, 同时得到了⼀张 “就诊卡”, 这个就诊卡就相当于患者的"令牌(token)".
- 后续去各个科室进⾏检查, 诊断, 开药等操作, 都不必再出⽰⾝份证了, 只要凭就诊卡即可识别出当前患者的⾝份.
- 看完病了之后, 不想要就诊卡了, 就可以注销这个卡. 此时患者的⾝份和就诊卡的关联就销毁了. (类似于⽹站的注销操作)
- ⼜来看病, 可以办⼀张新的就诊卡, 此时就得到了⼀个新的 ”令牌“
请求”正文“(body)
正文中的内容格式和 header 中的 Content-Type 密切相关。
我们都是可以通过 fiddler 观察的。这里就不多于讲了。就展开比较常见的json。
application/json
这里都是自定义的。
HTTP响应
状态码(status code)
状态码表示访问页面的结果,是访问成功,还是出错,出错是什么原因。
以下是常见的状态码。
- 200 OK : 这是一个最常见的状态码,表示访问成功。
- 404 Not Found : 访问的资源没找到。(浏览器输入一个URL,目的就是为了访问对方服务器的一个资源,而URL中,ip定位到主机,port定位到程序,path定位到程序管理的资源,如果 path 访问的资源,在服务器上没有,就会 404)
- 403 Forbidden : 表示访问被拒绝(没有权限),有的页面通常需要用户具有一定的权限才能访问,如果用户没有登录直接访问,就容易见到403。
- 405 Method Not Allowed : 方法不被允许,请求的方法和服务器这边声明的注解不匹配,就会出现405。(网上找别人的网站出现 405,比较难找的,但自己开发过程中,很容易出现405 )
- 500 Internal Server Error : 服务器出现错误,一般是服务器的代码执行过程中遇到了一些特殊情况(服务器异常崩溃)会产生这个状态码。也就是服务器处理逻辑的代码中抛出异常,但是你没有catch到。
- 504 Gateway Timeout : 当服务器负载比较大的时候,服务器处理单条请求的时候消耗的时间就会很长,就可能会导致出现超时的情况。(Gateway:网关,也就是网络的入口,一般来说,入口服务器就认为是网关,可能是一个软件,也可能是一个专门的机器)
- 302 Move temporarily : 临时重定向。
- 301 Moved Permanently :永久重定向,当浏览器收到这种响应时,后续的请求都会被自动改成新的地址。
理解”重定向“
相当于访问A服务器,服务器A告诉你去找 B。例如迁移域名。如果直接迁移了l,就会使所有保存旧域名的人无法访问了,于是就把服务器架设在新域名上,给旧域名设置重定向,可以重定向到新域名,因此如果访问新域名就能直接进,访问旧域名就会自动跳转到新域名上。
在登录页面中经常会见到302,用于实现登录成功后自动跳转到主页,响应报文的header部分会包含一个location字段,表示要跳转到哪个页面。
可以看到header中的 Location:http://gitee.com/ ,接下来浏览器就会自动发送GET请求,获取Location:http://gitee.com/。
状态码整体有很多:
2XX 都可以视为是成功,3XX 都是重定向, 4XX 客户端出错,用户构造的请求有问题, 5XX 服务器出错(Java程序员主要关注这个,一定是服务器出问题了大概率就是你的代码有bug)
认识响应“报头”(header)
响应报头的基本格式和请求报头的格式基本一致,类似于 Content -Type,Content-Length等属性的含义也和请求中的含义一致。
Content-Type
响应中的 Content-Type 常见取值有以下几种:
- text/html:body数据格式是 HTML
- text/css:body数据格式是CSS
- application/javascript:body数据格式是 JavaScript。
- application/json:body数据格式是JSON。
响应“正文”(body)
正文的具体格式取决于 Content-Type。可以观察上面几个抓包结果中的响应部分。
1、text/html
2、text/css
3、application/javascript
4.application/json
通过 Java socket 构造 HTTP 请求
所谓的“发送 HTTP 请求”,本质上就是按照 HTTP 的格式往 TCP Socket 中写入一个字符串。
所谓的“接受 HTTP 响应”,本质上就是从 TCP Socket 中读取一个字符串,再按照 HTTP 的格式来解析。
此外呢,我们还可以使用 Postman,apifox 这样的工具来构造 HTTP 请求。