计算机网络
计算机网络
月晕网络基础知识
总结理解了TCP/IP
的必备基础知识,包括OSI
参考模型、网络概念本质、网络构建等
协议
在计算机网络与信息通信领域,人们经常会提起协议,互联网中常有的具有代表性的协议有**IP,TCP,HTTP
** 等,而LAN
(局域网)中常见的协议有IPX/SPX等
常见的协议
计算机网络体系结构将这些网络协议进行了系统的归纳 TCP/IP 就是IP、TCP、HTTP等协议的集合。
网络体系结构 | 协议 | 主要用途 |
---|---|---|
TCP/IP | IP、ICMP、TCP、UDP、HTTP、SNMP等 | 互联网、局域网 |
IPX/SPX | IPX、SPX、NPC… | 个人电脑局域网 |
… | … | … |
协议的重要性
简单来说 协议就是计算机与计算机之间通过网络实现通信时事先达到的一种约定 这种约定使得不同的厂商的设备 不同的cpu 不同的操作系统组成的计算机只要遵守相同的协议就能进行通信 如果使用的协议不同就无法通信 这就好比不同国家的不同语言说话怎样也无法理解。
协议可以分成很多种,每一种协议都明确规定了行为规范,两台计算机必须能够支持相同的协议并遵守相同的协议进行处理才能实现相互通信
CPU和OS
- CPU(Central Processing Unit):中央处理器,他如同一台计算机的心脏,每个程序都是由它调度执行的,CPU的性能很大程度上也决定着一台计算机的处理性能
- OS(Operating System):操作系统,是一种基础的软件,他集合了CPU管理,内存管理,计算机外围设备管理以及程序运行管理等重要的功能 本篇只介绍TCP和IP协议的处理 很多情况其实已经内嵌到操作系统中了
协议的理解
举个简单的例子 现在有A,B,C三个人,A只会说汉语,B只会说英语,C都会说,现在A要与C说话,A与B说话该怎么进行沟通呢? 现在我们规定
- 将汉语和英语当作 ‘协议‘
- 将聊天当作 ‘通信‘
- 将说话的内容当作 ‘数据‘
那么由于A与B各持一种语言 无法交流 因为他们之间谈话的协议(语言)不同 双方都无法将数据(想说的话)传递给对方
A和C 之间的聊天情况 两个人都使用汉语这个协议就能理解对方所要表达的具体含义了 A和C为了顺利沟通采用了同一协议,使得他们之间能够传递所期望的数据(说给对方的话)
如此看来,协议如同人们平常说话的语言,虽然语言是人类说话的特性 但计算机和计算机之间进行通信的时候也可以是认为基于人类的语言实现了相互通信
计算机从物理连接层面到应用程序的软件层面,各个组件都严格遵循着事先达成的约定才能真正的实现通信 每个计算机还必须装有实现通信最基本的功能
分组交换协议
分组交换是指将大数据分割为一个个叫包(Packet)的较小单位进行传输的方法,这里的包,就如同我们在邮局里见到的邮包 分组交换就是将大数据分装为一个个这样的邮包交给对方
当人们邮寄包裹的时候 通常会填写一个寄件单贴到表单上在交给邮局 邮寄一般都会有寄件人和收件人的详细地址,类似地,计算机通信也会在每一个分组中附加上源主机地址和目标主机地址给通信线路 这些发送端地址,接收端地址,己及分组序列号写入的部分称为’报文首部’
一个较大的数据被分为多个分组时,为了标明是原始数据中的那一部分 就有必要将分组的序号写入包中,接收端会根据这个序号,再将每个分组按序号重新装配为原始数据
通信协议中,通常会根据报文首部应该写入那些信息,应该如何处理这些信息,相互通信的每一台计算机则根据协议构造报文首部,读取首部内容等
协议分层和OSI参考模型
协议的分层
ISO在制定标准化OSI 对网络体系结构进行了充分的讨论 最终形成了做为通信协议设计指标的OSI参考模型 将通信协议中必要的功能分成了7层 使得比较复杂的网络协议更加简单化
在这一模型中 每个分层都接受由它下一层所提供的特定服务,并且负责为自己的上一层提供特定的服务,上下层进行交互时所遵循的约定叫做接口同一层之间的交互所遵循的约定叫做协议
OSI参考模型
OSI参考模型将这样一个复杂的协议整理并分成了易于理解的七个分层
OSI(参考模型):将通信划分为7个分层 称之为OSI参考模型
OSI(协议):让异构的计算机之间能相互通信,OSI协议以OSI参考模型为基础界定了每个阶层的协议和每个阶层之间相关接口的标准
五层网络模型
面对复杂的问题,可以使用分层的方式来简化
经过不断的演化,网络最终形成了五层模型:
四层、五层、七层
应用层协议
URL
URL(uniform resource localtor,统一资源定位符) 用于定位网络服务
URL是一个固定格式的字符串
表达了那台计算机(domain)中的那个程序(port)寻找那个服务(path),并注明了获取服务的具体细节(path),以及要用什么样的协议通信(schema)
- 当协议是
http
端口为80
时,端口可以省略 - 当协议是
https
端口为443
时,端口可以省略 schema
、domain
、path
是必填的,其他的根据具体的要求填写
HTTP
超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个广泛运用互联网的应用层协议
该协议规定了两个方面的内容:
- 传递消息的模式
- 传递消息的格式
传递消息的模式
HTTP使用了一种极为简单的消息传递模式,「请求-响应」模式发起请求的称之为客户端,接收请求并完成响应的称之为服务器。「请求-响应」完成后,一次交互结束。
传递消息的格式
HTTP的消息格式是一种纯文本的格式 文本分三个部分:
请求行
请求头请求体
具体每一部分写什么内容,要看具体的服务要求
发送HTTP请求
安装
vscode
插件REST Clinet
新建文件
xxx.http
编写请求文本
1
2
3
4
5
6
7
8POST /api/user/login
Host localhost:7001
Content-Type: application/json
{
"loginId": 'admin',
"loginPwd": 'explosion'
}
熟悉关键信息
请求方法
请求行中的第一个单词是请求方法
在HTTP协议中,请求方法仅有语义的区别,只是表达了这次请求的「愿望」。
关于请求方法的协议原文见 HTTP/1.1规范RFC7231-Chapter4
比如GET
表达了客户端想要获取一些东西,POST
表达了客户端想要提交一些东西
常见的方法有:
GET
获取post
提交PUT
修改DELETE
删除
具体在开发中应该选择什么请求方法,一定是看服务方的要求
请求头 - Host
Host
标注了URL
地址中的Domain + Port
示例:
1 | Host: study.duyiedu.com |
请求头 - Content-Type
Content-Type
标注了附带的请求体是什么格式
比如,请求体的数据为loginId:admin, loginPwd:123456
,请求体可以用不同的格式发出
1 | Content-Type: application/x-www-form-urlencoded |
1 | Content-Type: application/json |
1 | Content-Type: multipart/form-data; boundary=aaa |
响应码
响应码(状态码、消息码)是响应行中的一个数字,后面往往跟上一个对应的单词,用于表达服务器对这个响应的整体「态度」
常见的响应码有:
常见的状态码有:
200 OK:一切正常。
301 Moved Permanently:资源已被永久重定向。
你的请求我收到了,但是呢,你要的东西不在这个地址了,我已经永远的把它移动到了一个新的地址,麻烦你取请求新的地址,地址我放到了响应头的Location中了
试试请求:www.douyutv.com
302 Found:资源已被临时重定向。
你的请求我收到了,但是呢,你要的东西不在这个地址了,我临时的把它移动到了一个新的地址,麻烦你取请求新的地址,地址我放到了请求头的Location中了
304 Not Modified:文档内容未被修改。
你的请求我收到了,你要的东西跟之前是一样的,没有任何的变化,所以我就不给你结果了,你自己就用以前的吧。啥?你没有缓存以前的内容,关我啥事
400 Bad Request:语义有误,当前请求无法被服务器理解。
你给我发的是个啥啊,我听都听不懂
403 Forbidden:服务器拒绝执行。
你的请求我已收到,但是我就是不给你东西
404 Not Found:资源不存在。
你的请求我收到了,但我没有你要的东西
500 Internal Server Error:服务器内部错误。
你的请求我已收到,但这道题我不会,解不出来,先睡了
响应头 - Content-Type
Content-Type
标注了附带的响应体是什么格式
常见的值有:
text/plain
: 普通的纯文本text/html
:html文档text/javascript
或application/javascript
:js代码text/css
:css代码image/jpeg
:jpg图片attachment
:附件- 其他
MIME
类型
浏览器的通信能力
用户代理
浏览器可以代替用户完成http请求,代替用户解析响应结果,所以我们称之为:
用户代理 user agent
在网络层面,对于前端开发者,必须要知道浏览器拥有的两大核心能力:
- 自动发出请求的能力
- 自动解析响应的能力
自动发出请求的能力
当一些事情发生的时候,浏览器会代替用户自动发出http请求,常见的包括:
用户在地址栏输入了一个url地址,并按下了回车
浏览器会自动解析URL,并发出一个
GET
请求,同时抛弃当前页面。当用户点击了页面中的a元素
浏览器会拿到a元素的href地址,并发出一个
GET
请求,同时抛弃当前页面。当用户点击了提交按钮
<button type="submit">...</button>
浏览器会获取按钮所在的
<form>
元素,拿到它的action
属性地址,同时拿到它method
属性值,然后把表单中的数据组织到请求体中,发出指定方法
的请求,同时抛弃当前页面。这种方式的提交现在越来越少见了
当解析HTML时遇到了
<link> <img> <script> <video> <audio>
等元素浏览器会拿到对应的地址,发出
GET
请求当用户点击了刷新
浏览器会拿到当前页面的地址,以及当前页面的请求方法,重新发一次请求,同时抛弃当前页面。
浏览器在发出请求时,会自动附带一些请求头
==重点来了==
从古至今,服务器和浏览器都有一个约定:
当发送GET请求时,不会附带请求体
这个约定深刻的影响着后续的前后端各种应用,现在,几乎所有人都在潜意识中认同了这一点,无论是前端开发人员还是后端开发人员。
由于前后端程序的默认行为,逐步造成了GET和POST的各种差异:
浏览器在发送 GET 请求时,不会附带请求体
GET 请求的传递信息量有限,适合传递少量数据;POST 请求的传递信息量是没有限制的,适合传输大量数据。
GET 请求只能传递 ASCII 数据,遇到非 ASCII 数据需要进行编码;POST 请求没有限制
大部分 GET 请求传递的数据都附带在 path 参数中,能够通过分享地址完整的重现页面,但同时也暴露了数据,若有敏感数据传递,不应该使用 GET 请求,至少不应该放到 path 中
POST 不会被保存到浏览器的历史记录中
刷新页面时,若当前的页面是通过 POST 请求得到的,则浏览器会提示用户是否重新提交。若是 GET 请求得到的页面则没有提示。
自动解析响应的能力
浏览器不仅能发送请求,还能够针对服务器的各种响应结果做出不同的自动处理
常见的处理有:
识别响应码
浏览器能够自动识别响应码,当出现一些特殊的响应码时浏览器会自动完成处理,比如
301、302
根据响应结果做不同的处理
浏览器能够自动分析响应头中的
Content-Type
,根据不同的值进行不同处理,比如:text/plain
: 普通的纯文本,浏览器通常会将响应体原封不动的显示到页面上text/html
:html文档,浏览器通常会将响应体作为页面进行渲染text/javascript
或application/javascript
:js代码,浏览器通常会使用JS执行引擎将它解析执行text/css
:css代码,浏览器会将它视为样式image/jpeg
:浏览器会将它视为jpg图片application/octet-stream
:二进制数据,会触发浏览器下载功能attachment
:附件,会触发下载功能该值和其他值不同,应放到
Content-Disposition
头中。
基本流程
AJAX
浏览器本身就具备网络通信的能力,但在早期,浏览器并没有把这个能力开放给JS。
最早是微软在IE浏览器中把这一能力向JS开放,让JS可以在代码中实现发送请求,并不会刷新页面,这项技术在2005年被正式命名为AJAX(Asynchronous Javascript And XML)
AJAX 就是指在web应用程序中异步向服务器发送请求。
它的实现方式有两种,XMLHttpRequest 简称XHR
和Fetch
以下是两者的对比
功能点 | XHR | Fetch |
---|---|---|
基本的请求能力 | ✅ | ✅ |
基本的获取响应能力 | ✅ | ✅ |
监控请求进度 | ✅ | ❌ |
监控响应进度 | ✅ | ✅ |
Service Worker中是否可用 | ❌ | ✅ |
控制cookie的携带 | ❌ | ✅ |
控制重定向 | ❌ | ✅ |
请求取消 | ✅ | ✅ |
自定义referrer | ❌ | ✅ |
流 | ❌ | ✅ |
API风格 | Event | Promise |
活跃度 | 停止更新 | 不断更新 |
跨域问题及解决方案
同源策略及跨域问题
同源策略是一套浏览器安全机制,当一个源的文档和脚本,与另一个源的资源进行通信时,同源策略就会对这个通信做出不同程度的限制。
简单来说,同源策略对 同源资源 放行,对 异源资源 限制
因此限制造成的开发问题,称之为跨域(异源)问题
同源和异源
1 | 源(origin) = 协议 + 域名 + 端口 |
例如:
https://study.duyiedu.com/api/movie
的源为https://study.duyiedu.com
http://localhost:7001/index.html
的源为http://localhost:7001
两个URL地址的源完全相同,则称之为同源,否则称之为异源(跨域)
跨域出现的场景
跨域可能出现在三种场景:
网络通信
a元素的跳转;加载css、js、图片等;AJAX等等
JS API
window.open
、window.parent
、iframe.contentWindow
等等存储
WebStorage
、IndexedDB
等等
对于不同的跨域场景,以及每个场景中不同的跨域方式,同源策略都有不同的限制。
本文重点讨论网络通信中AJAX
的跨域问题
网络中的跨域
当浏览器运行页面后,会发出很多的网络请求,例如CSS、JS、图片、AJAX等等
请求页面的源称之为页面源,在该页面中发出的请求称之为目标源。
当页面源和目标源一致时,则为同源请求,否则为异源请求(跨域请求)
浏览器如何限制异源请求?
浏览器出于多方面的考量,制定了非常繁杂的规则来限制各种跨域请求,但总体的原则非常简单:
- 对标签发出的跨域请求轻微限制
- 对AJAX发出的跨域请求严厉限制
解决方案
CORS
CORS(Cross-Origin Resource Sharing)是最正统的跨域解决方案,同时也是浏览器推荐的解决方案。
CORS是一套规则,用于帮助浏览器判断是否校验通过。
CORS的基本理念是:
- 只要服务器明确表示允许,则校验通过
- 服务器明确拒绝或没有表示,则校验不通过
所以,使用CORS解决跨域,必须要保证服务器是「自己人」
请求分类
CORS将请求分为两类:==简单请求==和==预检请求==。
对不同种类的请求它的规则有所区别。
所以要理解CORS,首先要理解它是如何划分请求的。
简单请求
完整判定逻辑:https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests
简单来说,只要全部满足下列条件,就是简单请求:
请求方法是
GET
、POST
、HEAD
之一头部字段满足CORS安全规范,详见 W3C
浏览器默认自带的头部字段都是满足安全规范的,只要开发者不改动和新增头部,就不会打破此条规则
如果有
Content-Type
,必须是下列值中的一个text/plain
multipart/form-data
application/x-www-form-urlencoded
预检请求(preflight)
只要不是简单请求,均为预检请求
练习
1 | // 下面的跨域请求哪些是简单请求,哪些是预检请求 |
对简单请求的验证
对预检请求的验证
- 发送预检请求
- 发送真实请求(和简单请求一致)
细节1 - 关于cookie
默认情况下,ajax的跨域请求并不会附带cookie,这样一来,某些需要权限的操作就无法进行
不过可以通过简单的配置就可以实现附带cookie
1 | // xhr |
这样一来,该跨域的ajax请求就是一个附带身份凭证的请求
当一个请求需要附带cookie时,无论它是简单请求,还是预检请求,都会在请求头中添加cookie
字段
而服务器响应时,需要明确告知客户端:服务器允许这样的凭据
告知的方式也非常的简单,只需要在响应头中添加:Access-Control-Allow-Credentials: true
即可
对于一个附带身份凭证的请求,若服务器没有明确告知,浏览器仍然视为跨域被拒绝。
另外要特别注意的是:**对于附带身份凭证的请求,服务器不得设置 Access-Control-Allow-Origin 的值为*
*。这就是为什么不推荐使用的原因
细节2 - 关于跨域获取响应头
在跨域访问时,JS只能拿到一些最基本的响应头,如:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果要访问其他头,则需要服务器设置本响应头。
Access-Control-Expose-Headers
头让服务器把允许浏览器访问的头放入白名单,例如:
1 | Access-Control-Expose-Headers: authorization, a, b |
这样JS就能够访问指定的响应头了。
JSONP
在很久很久以前…并没有CORS方案
在那个年代,古人靠着非凡的智慧来解决这一问题
虽然可以解决问题,但JSONP有着明显的缺陷:
仅能使用GET请求
容易产生安全隐患
恶意攻击者可能利用
callback=恶意函数
的方式实现XSS
攻击容易被非法站点恶意调用
因此,除非是某些特殊的原因,否则永远不应该使用JSONP
代理
CORS和JSONP均要求服务器是「自己人」
那如果不是呢?
那就找一个中间人(代理)
比如,前端小王想要请求获取王者荣耀英雄数据,但直接请求腾讯服务器会造成跨域
由于腾讯服务器不是「自己人」,小王决定用代理解决
如何选择
最重要的,是要保持生产环境和开发环境一致
下面是一张决策图
具体的几种场景