Go to comments

网络大师课 分层模型和应用层协议

一、五层网络模型

网络要解决的问题是,两程序之间如何交换数据

由于这个一个复杂的问题,所以分成多层,每一层解决具体的事情


1、五层网络模型

20211008163417.jpg

应用层:

负责规定消息的格式是什么样的,

该层有不同的协议,不同的协议消息的格式就不一样


传输层:

负责消息的传递方式,主要是负责可靠传输

TCP  是可靠的传输,准确无误的传输到对方

UDP 不一定是不可靠的传输

不同的传输方式,决定了网络的传输效率(传的快还是传的慢)


网络层:

负责如何来找到对方

在茫茫的互联网中有这么多台计算机,如何准确的找到对方,有可能兜了一大圈发现就是本机


可以简单的这样理解(虽然解释的没有那么准确),

比如你家里有几台电脑,这几台连到互联网,靠的是一台路由器,也就是说家里面不管有几台电脑,用的是公网上同一个 IP 地址,

每家都有一台路由器,这些路由器用的是不同的公网 IP 地址

我们访问别人家的计算机,比如发一个聊天消息“你好”过去,是发到对方家里面的路由器,这就是网络层通过 IP 来解决的问题

对方发消息回来也是一样,找到我家的路由器


数据链路层:

“网络层”跟“数据链路层”解决的问题差不多,都是为了要找到对方

1. 网络层,它不太清楚路由器背后有几电脑,它只是路由器与路由器之间的交互

    但是路由器背后连接着家里的多台电脑(准确的说,路由器应该叫交换机)

2. 数据链路层,能够准确的找到对方具体那一台电脑要,

    平时说的 MAC 地址就是数据链路层里面的相关知识,因为 MAC 是找具体计算机的


IP 地址会变,MAC 地址是不会变的,

比如袁老师今天在成都,明天就出差到哈尔滨了,带着同一台电脑,

IP 地址会随着位置而变化,有点像家庭住址,

但是 MAC 地址相当于指纹,走到哪都不会变,是计算机出厂的时候焊死在网卡里面的

找到具体计算机后,就是传输了,


物理层:

如何传输呢?通过物理介质或无线的电磁波传递过去,这物理层解决的问题,

如何把我们的数据转换成电信号或是光信号


2、数据传输的示例


该示例解释了

一个消息,是如何从一台计算机的某一个程序,传输到另一个计算机的某一个程序

20211008163458.jpg

发送方:

发送的原始消息是“我爱你”

应用层,给原始消息套一层东西(比如 http 协议),然后交给下一层

传输层,它解决可靠传输,再套一层东西,比如加一些校验码(对方就可以校验一下消息是否完整传过来了),然后又往下层传递

网络层,加一层,比如 IP 地址,我的 ip 地址,对方的 ip 地址

数据链路层,前后都会加东西,我的 mac 地址,对方的 mac 地址

物理层,变成电信号或光信号,通过物理介质或无线电磁波传递过去


接收方:

物理层传递过去后,收到一大堆东西,

数据链路层,它看得懂这一大堆东西,因为它看得东自己加的东西,把自己加的东西去掉,交给网络层

网络层,它看得懂自己加的 ip 地址,把自己加的去掉,又给传输层

传输层,它能看懂用协议是搞定可靠传输的,于是它校验一下,如果有问题重新传,没有问题就把自己的东西去掉,给应用层

应用层,把加的东去掉就看到原始的消息“我爱你”,显示到计算机屏幕上


在电光火石之间,就完成了这一系列的操作,


前端了解到这个程度就行了,

珍惜有限的时间!


二、应用层协议

前端工程师主要是跟“应用层”打交道,应用层里面有很多的协议、很多的标准,其中我们的要研究两个

1. URL 地址

2. HTTP


1、URL 地址

URL(uniform resource locator,统一资源定位符)用于定位网络服务


在茫茫互联网上有很多服务,比如看新闻、视频、听音乐等等,

我们用 URL 来描述要找的这些服务在哪


URL 就是一个字符串,一个统一的标准格式的字符串,叫统一资源定位符

定位的是资源

资源就是服务

202301121029498.jpg

整个字符串表达的意思
domain 域名a.com在茫茫互联网中要找哪一台计算机
port 端口80端口是找到这台计算上是哪一个程序
path 路径/news/detail程序中可能有很多服务,要找哪一服务
query 参数id=1服务里面的具体的细节
scheam 协议http通信协议(scheam或者用protocol,都是协议的意思)
hash 哈希#t1

哈希一半不参与网络通信,不用太关心它,

后端跟浏览器都不 care 它,一般是在本页内部跳转

其实整个 url 是在不断的细化通信的细节,具体每一个部分写什么要看服务器


示例,

右键点击 Open with Live Server,

打开 Live Server 之后,地址栏就是 url 地址  http://localhost:5500/index.html 

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
  <h1>Hello</h1>
</body>
</html>


域名:

可以这样理解,在互联网上每一台计算机都会对应一个域名,

通过域名可以在互联网上找到计算机,域名这里也可以写 ip 地址,

localhost 特殊的域名,表示访问本机,

127.0.0.1 也是本机


端口:

一台计算上会运行很多的程序,不同的程序监听不同的端口,

就像在一栋楼里面有很多的房间,要找哪个房间,要把房间的号码给我,

5500 的意思表示 Live Server 插件住的房间编号是 5500


通过 localhost 找到本机,

通过 5500 打开了房间,住是一个 Live Server 程序在里面运行


所以通过这一段  a.com:80  就可以准确找到,某一台计算机上的某一个应用程序,

那么这个程序可以做很多事情,不同的路径,它可能做不一样的事情,提供不一样的服务


路径:

这个 /index.html 路径能做什么?

要看这个程序是怎么写的,

1. 它可能给我们一张图片、可能给一个 js 文件、可能给一个 css 代码,

    甚至报一个 404 错误,告诉我们资源不存在,它可能做任何事情

2. 只不过 Live Serve 这个程序比较常规,

    它就找到对应的 /index.html 文件,然后把文件的内容发送到我们的浏览器,于是浏览器渲染就看到 Hello


袁老师说这个地方很容易产生偏差,

当访问这个地址  http://localhost:5500/index.html 页面上渲染出来 Hello

并不意味着在这台 localhost 计算机,访问的 5500 程序有这个 /index.html 文件


因为网络通信不是通过文件的,

而且路径(比如  /news/detail )是被监听的端口(比如 80 )这个程序拿到的,

上面说了,这个 80 程序爱给我们什么就给我们什么,它可以做任何事情


写一个服务器程序

1. npm init 

2. npm i express body-parser cors

3. 新建文件 server.js

const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const app = express();
app.use(cors());
app.use(bodyParser.json());
app.get('*', (req, res) => {
  // res.heaher('Conten-Type', 'image/png');
  res.send('快乐的池塘里有一只小青蛙');
});

const PORT = 9528;

app.listen(PORT, () => {
  console.log(`server start on port ${PORT}`)
});

4. 启动 node server.js


访问的这个地址 http://localhost:9528/abc/def/hij/kkk.css

现在我们知道,这个路径 /abc/def/hij/ 的程序里面没有 kkk.css 文件,

但是得到了内容“快乐的池塘里有一只小青蛙”,是程序给我们的


当然,服务器程序可以判断,有没有这个 kkk.css 文件,

如果有,

把该文件读出来,把文件的内容发给我们


一定要清楚这是“浏览器”跟“服务器”,这两个程序在通信

浏览器向服务器请求东西,

服务器给浏览器一个东西,至于怎么给,服务器有自己的逻辑,甚至可以把 kkk.css 返回一个图片 


所以,通过这些,

不要认为路径 /abc/def/hij/kkk.css,跟服务器程序的里面的文件相关联,

没有任何的关联,

只不过有些程序比较常规,把 url 的路径当成文件读,这种规矩的程序,我们把这个程序叫静态资源服务器


同学总结,

路径只是一个服务,

只能表达我想要什么东西,仅此而已


query:

表示某一些服务的细节

比如百度   https://www.baidu.com/s?wd=渡一教育 

/s     表示服务,是该路径程序就给一个搜索服务

?wd  附加信息,表示服务的具体的细节,要搜索的内容


url 的一些细节

1. 当协议是 http 端口为 80 时,端口可以省略

2. 当协议是 https 端口为 443 时,端口可以省略

3. schema、domain、path 是必填的,其他的根据具体的要求填写

    域名,必填,决定计算机在哪里

    协议,必填,平时浏览器会自动补全。浏览器先用 http 试,如果网站支持 https,一般会自动跳转到 https

    path,必填,平时不写,浏览器默认补全 / 路径,斜杠表示根路径


2、HTTP 协议

超文本传输协议(Hyper Text Transfer Protocol, HTTP)是一个广泛用于互联网应用层的协议


HTTP 是一个协议,它不做任何事情,规定了两方面的内容

1. 传递消息的模式

2. 传递消息的格式


1、传递消息的模式


http协议规定,两个程序之间要相互通信,传递消息,应该怎么传递呢?

1. 永远是一方主动,一方被动

202301121100790.jpg

2. 客户端一方主动向服务器发起一个消息过去,这个过程叫请求 Request

3. 服务器一方拿到一个消息后,回应一个消息叫响应 Response


HTTP规定限制要这样通话:

客户端先发送请求过去了,可以理解为电话打过去了,说完一句了要停,

服务器端会回应一个消息,

双方就挂电话了

如果还要发消息,

客户端重新发一个请求消息过去后,然后服务器端回应一个响应,

这种一来一回的消息模式叫“请求-响应”模式


“请求-响应”模式是非常简单的一种模式,

这种模式很好理解,很好推广,但同时也会有一些问题(具体后面会说)


现在知道了,

请求一个过去传的是一个 Request 消息,响应回来一个 Resqonse,消息格式是什么呢?


2、传递消息的格式

HTTP 的消息的格式是一个纯文本的"字符串",这个字符串由三个部分组成

请求行 Line
请求头 Header

请求体 Body


请求就是把这个字符串发到服务器,服务器响应也是生成一个字符串发回来

这些都发生在应用层


实际体验一下,

有非常多的工具可以发送 http 请求,这里推荐一个非常直观的工具 REST Client

以看到 “请求”跟“响应” 两完整个字符串的组成的部分


发送一个获取百度首页的 Request 请求消息

1. 安装 vscode 插件 REST Client

2. 新建后缀为 .http 的文件,比如 test.http 

3. 在 test.http 文件里面编辑请求消息,一般 GET 不带请求体(也可以写请求体)

GET / HTTP/1.1
Host: www.baidu.com

4. 点击 Send Request 发生请求消息,发出去后得到百度给我们的响应,响应也是一个字符串,也是由三部分组成


HTTP 消息的本质,

就是一个字符串发过去,一个字符串发回来

本质是文本格式的消息


Ps:

HTTP 2 不在是文本格式了,是二进制格式,

但是对我们的开发影响不大,仍然当成文本格式理解


下面详细介绍,

文本格式的字符串是由什么组成的


3、请求消息里面的关键信息

请求方法

请求行中第一个单词 GET 表示请求方法

http 协议官方描述,请求方法只有含义上的不同,只是表达了这次请求的愿望

The request method token is the primary source of request semantics;

it indicates the purpose for which the client has made this request and what is expected by the client as a successful result.


The request method token  请求方法 token

is the primary source of request semantics  是请求语义的主要来源,

意思是请求方法表达了请求的含义


具体什么含义呢?

it indicates the purpose 它指明了一个目的,

client has made this request 客户端发出这个请求的目的,


所以面试的时候问题 GET 和 POST 的区别

从 HTTP 协议的角度看,不同的请求方法只是表达了不同的愿望,他们只有含义(语义)上的区别

GET 表达了客户端想要一些东西,想拿一些东西,就是发出的这个请求的目的,

POST 表达了客户端想提交一些东西


由于只有语义上的区别,GET 表达了客户端想要一些东西,没什么东西要给出去,

所以浏览器觉得 GET 方法应该没有请求体,只有请求行、请求头,请求体是空的,

但是在协议层面上,它可以带请求体,只是一般很少这么做


常见的请求方法有

GET    获取

POST  提交

PUT    修改

DELETE 删除


请求路径

请求行中的 / 斜杠,表示 url 地址中的请求路径

格式是  请求路径加 + query 


比如

http://www.baidu.com/s?wd=html 

请求路径是端口号后面的一坨内容 /s?wd=html


HTTP/1.1

这部分基本是固定的,

用的是 http 协议,版本号是 1.1

如果 http2 这里也不是写 2.0

请求行结束了,下面是请求头,


请求头

请求头是很多的属性名和属性值,不同属性表达不同的含义


Host: www.baidu.com:80

Host 属性属性是必须要传递的,表示 url 地址里面的主机域名

默认端口号 80 可以不写


怎么写这个请求地址

https://study.duyiedu.com/api/movies

GET /api/movies HTTP/1.1
Host: study.duyiedu.com


请求体

研究一下请求消息里面的请求体

GET 可以写请求体,但是一般程序不支持,所以不带请求体,

带请求体的用 POST 请求,表示要提交一些东西


比如,注册一个账号

1. 请求方法 POST

2. 在请求头后面两次回车后写“请求体

3. 请求体有三种格式,这里用是 json 格式,

    json 格式就是对象 {} 或 [] 数组都可以,属性值字符串必须用双引号,属性名必须是双引号(不是单引号)

4. 当带请求体后,必须在请求头中表明请求体的格式 Content-Type: application/json

POST /api/user/reg HTTP/1.1
Host: study.duyiedu.com
Content-Type: application/json

{
  "loginId":"ruyiccom",
  "loginPwd":"123123",
  "nickname": "远远的地方"
}

返回的响应体也是一个 json 格式

{

  "code": 0, // 0 表示没有错误码,注册成功

  "msg": "", // 为空表示没有错误消息

  "data": {

    "id": "66f533385f8cc70c8a37e38a",

    "loginId": "ruyiccom",

    "nickname": "远远的地方"

  }

}


注册成功后就可以登陆了,

登陆的路径是 /api/user/login

POST /api/user/login HTTP/1.1
Host: study.duyiedu.com
Content-Type: application/json

{
  "loginId":"ruyiccom",
  "loginPwd":"123123"
}


没有浏览器、没有表单界面,纯粹的 HTTP 就完成了注册和登陆,

本质就是网络通信,完成了登陆和注册


Content-Type 属性

在请求头中使用 Content-Type 属性表达了请求体的格式,有三种格式


比如,请求体的数据为 loginId:admin, loginPwd:123456,可以用不同的格式发出

第一种:json 格式

Content-Type: application/json

{ "loginId": "admin", "loginPwd": "123123" }

第二种:urlencoded 格式跟 url 地址里面的格式是一样的

Content-Type: application/x-www-form-urlencoded

loginId=admin&loginPwd=123123

第三种:multipart/form-data 格式一般做文件上传

Content-Type: multipart/form-data; boundary=aaa

--aaa
Content-Disposition: form-data; name="loginId"

admin
--aaa
Content-Disposition: form-data; name="loginPwd"

123456
--aaa
Content-Disposition: form-data; name="avatar"; filename="small.jpg"
Content-Type: image/jpeg

文件的二进制
--aaa--


multipart/form-data 格式

1. 这种格式的请求体是有很多部分分割而成的,使用分割符 --aaa 进行分割,

    boundary=aaa 分割符可以自定义

2. 比如请登陆的要传两个数据,

    每个 --aaa 下面写一个数据,

    后面没有了 --aaa-- 表示结束

POST /api/user/login HTTP/1.1
Host: study.duyiedu.com
Content-Type: multipart/form-data; boundary=aaa

--aaa

--aaa

--aaa--

3. Content-Disposition: form-data; 前面是固定写法

    name="loginId" 后面服务器接收数据时候要的属性名

    ruyiccom 是属性值

POST /api/user/login HTTP/1.1
Host: study.duyiedu.com
Content-Type: multipart/form-data; boundary=aaa

--aaa
Content-Disposition: form-data; name="loginId";

ruyiccom 
--aaa
Content-Disposition: form-data; name="loginPwd";

123456
--aaa--


上传一个文件,

文件上传的本质就是一个网络通信,按照指定格式的要求把文件发过去

POST /my-code/upload/single.php HTTP/1.1
Host: localhost
Content-Type: multipart/form-data; boundary=aaa

--aaa
Content-Disposition: form-data; name="avatar";
filename="img247635499.jpg"
Content-Type: image/jpeg

< ./img247635499.jpg
--aaa--

服务器不需要传用户名和密码,只传一个图片

1. /my-code/upload/single.php 请求行的路径表示单个文件上传

2. Content-Disposition: form-data; 前面是固定

    name="avatar" 表示服务器要的属性名是 avatar,要服务器配合

3. 还有一些附加信息

    filename="img247635499.jpg" 告诉服务器文件的后缀名,一般写本地上传的文件名

    Content-Type=image/jpeg  表示图片的类型,跟上面的 filename 后缀名是对应的,这种 image/jpeg 写法表示 MIME 类型

4. 属性值是图片,文件是二进制,

    在文本编辑器里面写不了 0101 二进制,

    写出来什么都是文本,因为这是字符串的 0 和 1,

    插件可以用特殊的符号 < ./img247635499.jpg


MIME 类型是标准字符串描述属性类型,意思是用一个字符串来描述数据类型

MIME类型

描述的数据类型

text/javascript 或 application/javascript

描述js代码
text/css描述css代码
text/plain普通的纯文本
text/htmlhtml文档
image/jpegjpg图片
image/png
image/gif
application/jsonjson格式的数据
attachment附件
其他 MIME 类型

 

总结

请求的本质就发一个字符串过去

请求行:关注的是请求方法,表达的是请求的目的

请求头:关注必填的 Host 表示目标的地址,如果发请求体必须用 Content-type 属性描述请求体的类型


4、详解响应

响应也是三部分

请求

请求头

请求体 

HTTP/1.1 200 OK
Server: nginx/1.21.1
Date: Wed, 02 Oct 2024 10:37:52 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 110
Connection: keep-alive
Vary: Origin
Accept-Ranges: bytes
authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY2ZjUzMzM4NWY4Y2M3MGM4YTM3ZTM4YSIsImlhdCI6MTcyNzg2NTQ3MiwiZXhwIjoxNzI4NDcwMjcyfQ.byacBHSuLIrySfKx2INgKR3Qa2mWLRQ7Y7hysAWMnw0
x-frame-options: SAMEORIGIN
x-xss-protection: 1; mode=block
x-content-type-options: nosniff
x-download-options: noopen
x-readtime: 7

{
  "code": 0,
  "msg": "",
  "data": {
    "id": "66f533385f8cc70c8a37e38a",
    "loginId": "ruyiccom",
    "nickname": "远远的地方"
  }
}


响应码

关注这个  200 OK 

一数字 200,一个单词 OK,一般是对应的

表达的意思是,先汇总的说一下这次响应是一个什么情况,相当于用非常简短的化描述目前的情况


常见的响应码

分类描述用法
1**信息,服务器收到请求,需要请求者继续执行操作

服务器也收到请求了,也做出了响应,但是还没有完,浏览器还要继续发一些别的请求,

比如请求升级的协议、续传等

2**成功,一般表示请求成功

最常见的是 200 ok

3**

也是成功,表示重定向

你访问的地址现在没了,请你到另外一个地址去找

301 永久重定向

302 临时重定向

4**客户端错误请求的语法错误或无法完成请求
5**服务器错误服务器在处理请求的过程中发生了错误


1**、2**、3** 都是表示没问题


常见的状态码有:

 200 OK  一切正常


 301 Moved Permanently  资源已被永久重定向


你的请求我收到了,

但是呢,你要的东西不在这个地址了,

我已经永远的把它移动到了一个新的地址,麻烦你取请求新的地址,

而且以后永远不要来了,地址我放到了响应头的 Location 属性中了


比如请求斗鱼

先设置一下插件,不然自动重新定向

1. 搜索 rest redirect

2. 找到 Rest-clinet:Followredirect

3. 去掉勾 Follow HTTP 3xx responses as redirects.

POST / HTTP/1.1
Host: www.douyutv.com

得到 301 重定向,并且在头里面告诉资源永久的移动到另外的地址

HTTP/./ 301 Moved Permanently

location: https://www.douyu.com


 302 Found  资源已被临时重定向


你的请求我收到了,

但是呢,你要的东西不在这个地址了,

我临时的把它移动到了一个新的地址,麻烦你取请求新的地址,

地址我放到了请求头的 Location 中了

以后还可以回来,说不定以后东西又放回来了


 304 Not Modified  文档内容未被修改(会涉及到缓存)


你的请求我收到了,

你要的东西跟之前是一样的,没有任何的变化,所以我就不给你结果了,你自己就用以前的吧。

啥?你没有缓存以前的内容,关我啥事


 400 Bad Request  语义有误

服务器说,我看不懂你给我发的信息,我听不懂你发过来的是什么


4 开头的一般是客户端的错误,也不一定真的的是客户端错误,

服务器程序是可以改的,

在服务器的全局错误里面,可以这样写,无论发生什么错误,响应的错误吗都是 400

try{


}catch{

  res.code = 400

}


 403 Forbidden  表示服务器拒绝执行

你的请求我已收到,但是你没有权限,我就是不给你东西


 404 Not Found  资源不存在。

这是最常见的,你的请求我收到了,但我没有你要的东西


 500 Internal Server Error   服务器内部错误

服务器报错了,它也不知道怎么回事,客户端那边收到 500

5 开头的一般是服务器的错误


服务器到底发什么响应码,完全是被服务器控制的

比如,用户注册失败,很多公司的做法是

1. 响应行是 200 ok

2. 但是把错误码放在响应体里面 code: 400

HTTP/1.1 200 OK
Server: nginx/1.21.1
Date: Sat, 28 Sep 2024 05:58:25 GMT
Content-Type: application/json
Content-Length: 48
Connection: keep-alive
Vary: Origin
Accept-Ranges: bytes

{
  "code": 400,
  "msg": "账号已存在",
  "data": null
}


响应头

响应头只关注关注一个 Content-Type 属性,它表示响应体里面是什么格式,跟请求头里面是一样的


上面注册失败的,表示响应体是一个 json 格式的字符串

Content-Type: application/json


请求一个网页,比如请求百度

GET / HTTP/1.1
Host: baidu.com

Content-Type: text/html 请求体里面是一个 html 文档


请求图片

GET /blog/uploads/image/202311/169936706514263.jpg HTTP/1.1
Host: ruyic.com

请求体当成文件渲染了

保存为 .http 文件,是响应的原始格式

拖进 vscode 打开,仍然打开

选文本编辑器打开

响应头里面有 Content-Type: image/jpeg


请求 css 文件

Content-Type: text/css; charset=UTF-8


5、课后回答的一些问题

三次握手,四次挥手

我们的原始消息是请求体或响应体,应用层加 行 和 头 后交给传输层,

传输层用 TCP 协议,

TCP 要进行三次握手和四次挥手


可以把 TCP 想象成打电话,电话过去先不着急说正事


连接的建立(三次挥手)

客户端:喂能听见吗 

服务器:能听见你说话,你能听见我说话吗

客户端:我也能听见你说话


三次握手后联通了,才正式发送 HTTP 协议的消息

请求 - 响应”完后要挂电话了,

挂电话是四次挥手


连接的销毁(四次挥手)

开始

客户端:我说完了,我现在可以挂电话吗

服务器:我知道你说完了,先别忙着挂,我还有话要说

服务器继续说...,说完后,

服务器:我也说完了,可以挂了吗?

客户端:好的,可以挂了

结束


只要 TCP 协议连接,就是双方电话打通了,

HTTP 协议随时可以通信,

只要一方不挂电话,HTTP 协议就永远“请求-响应”的通信下去


哈希

http://abc.con/xxx#/header

哈希改变的是 /header 


最早的时候哈希是做锚连接的

点击 <a href="#/header1">header</a> 改变了哈希

页面自动滑到 <h1 id="/header1">标题1</h1>


单页应用框架

#/header 想要跳转某一个组件,

就跟页面上的一个  id="/header"  就造成了冲突


所以 

官方推出 H5: history api 

它可以改变地址的路径,浏览器也不刷新


6、思考题

根据这节课学到的知识,思考一个问题:如果不使用浏览器,是否能够完成页面浏览?



Leave a comment 0 Comments.

Leave a Reply

换一张