计算机网络
应用层 - 语义共识
应用层是网络体系中最接近用户的一层。它的本质是定义了应用程序之间通信的“业务语言和交互规则”。它不负责数据的传输,只负责数据的理解与表达(例如:HTTP、FTP、DNS、SMTP)。
如果没有应用层,两台电脑虽然可以通过底层传输数据,但接收方根本不知道这串二进制数据是一个网页、一幅图片,还是一封邮件。应用层就是为了让不同的应用程序能够达成“语义共识”。
HTTP/1.1 协议的文本结构
以最核心的 HTTP/1.1 协议为例,它的实现极其朴素,就是靠纯文本的结构化约束。 协议规定,一个请求必须包含:请求行(方法+路径+版本)、请求头(键值对)和请求体。
GET /index.html HTTP/1.1
Host: www.example.com
Connection: keep-alive
[请求体-此处为空]DNS 域名解析
DNS 是一个应用层的协议,用于将域名解析为 IP 地址,DNS 服务器是一个分布式的数据库,用于存储域名和 IP 地址的映射关系
DNS 将人类可读的域名转换为计算机可读的 IP 地址。这个转换过程涉及到 DNS 客户端(也称为解析器)向 DNS 服务器发送请求,以及 DNS 服务器返回响应
为什么应用层无法保证数据可靠性
应用层只规定了数据的格式,它既不知道对方的设备开没开机,也不知道发出去的数据会不会在中途丢包,更没有能力去给数据排先后顺序。
所以这个活交给了传输层。
传输层 - 端到端可靠性
传输层是负责进程到进程(Process to Process)通信的一层。它在复杂的网络中,为运行在不同主机上的应用进程提供了端到端的逻辑通信(核心协议:TCP、UDP)。
应用层只管产生数据,但网络是不可靠的。如果没有传输层,数据就会面临丢包、乱序、错误的风险,而且一台机器上有那么多程序(微信、浏览器、游戏),底层收到的网络数据根本不知道该分给谁。传输层就是为了解决“数据可靠性”和“端口分发(复用/分用)”而生的。
TCP
以 TCP 协议为例,为了实现你刚才说的两点,设计了极为复杂的机制:
- 连接管理:用“三次握手”确认双方的收发能力,用“四次挥手”体面分手。
- 可靠性保证:给每个字节编上序号(Sequence Number)。接收方收到后必须回一个确认号(Acknowledgment Number)。如果发送方迟迟没收到确认,就会触发超时重传。
传输层的局限
TCP 协议手里只有端口号(比如 80、443),它确实能保证数据完整,但它抬头一看,茫茫互联网有数十亿台设备。TCP 根本不知道目标的物理位置在哪,更不知道该走哪条路由路径才能把这个数据包送到世界上另一台电脑上!
所以这个活交给了网络层。
网络层 - 跨网寻址与选路
网络层是负责主机到主机(Host to Host)通信的一层。它不关心数据是哪个软件发出的,也不管数据对不对,它只负责把数据包从地球上的 A 电脑送到 B 电脑(核心协议:IP、ICMP)。
传输层只知道“端口”,不知道“路径”。如果没有网络层,数据包就像一封只写了“收件人:张三(端口)”,但没写“家庭住址”的信,根本寄不出去。网络层就是为了解决“跨网络寻址”和“路由选择(选路)”而生的。
- IP 地址(寻址):给全球每一台联网设备分配一个独一无二的地址(如 192.168.1.1 或 IPv6)。IP 地址分为网络号(你在哪个小区)和主机号(你是几栋几单元)。
- 路由表(选路):网络中的路由器就像邮局分拣员。每个路由器手里都有一张路由表,数据包每到一个路由器,路由器就看一眼它的“目的 IP”,然后告诉它:“下一跳(Next Hop)往左边走”。
IP 地址是一个虚拟的逻辑地址。但网线、光纤、无线电波在传输信号时,根本不认 IP 地址,它们只认网卡上出厂就烧死死、物理上唯一的 MAC 地址(Media Access Control Address)。而且在同一个局域网里,大伙都在一条线路上喊话,怎么保证不冲突?
网络层在局域网的大门口停住了,它必须把“物理世界最后一百米”的脏活,推给最底层的数据链路层。
数据链路层与物理层 - 局域网精准投递与光电转换
数据链路层负责相邻节点(Node to Node)之间的可靠传输。它把网络层交下来的 IP 数据包封装成“帧(Frame)”,并在网线、光纤等物理介质上进行传输。
网络层只负责出谋划策(规划路径),但真正干活的网卡、光纤只认电信号和光信号。如果没有数据链路层,虚拟的路由路径就无法变成物理世界里跳动的电子。它存在的目的就是解决“局域网内精准投递”和“物理介质对接”的问题。
- 以太网帧:它会在数据头部贴上源 MAC 地址和目的 MAC 地址。
- 物理层投递:网卡把这些“帧”转换成高低电平(0 和 1),通过网线发射出去。
分层的必要性
分层可以将庞大的复杂问题,转换为较小的局部问题,局部问题更容易研究和处理
物理层解决了:
- 什么样的传输介质
- 什么样的物理接口
- 什么样的信号表示数据
数据链路层解决了:
- 如何标记网络中的主机(主机编址问题,比如 MAC)
- 如何从比特流中区分地址和数据
网络层解决了:
- 如何标记各网络以及网络中的各主机(网络和主机共同编址,比如 IP 地址)
- 如何转发分组,进行路由选择
运输层解决了:
- 如何解决应用进程之间基于网络的通信问题(比如端口号)
- 接触出现误码,丢包的传输错误问题
应用层解决了:
- 应用进程间的交互来完成特定的网络应用(按照协议编写程序,比如 HTTP,SMTP)
简而言之:
- 应用层 - 解决通过应用程序的交互来实现特定网络应用的问题
- 运输层 - 解决进程之间基于网络的通信问题
- 网络层 - 解决分组在多个网络上传输的问题
- 数据链路层 - 解决分组在一个网络上传输的问题
- 物理层 = 解决使用何种信号来传输比特的问题
DNS
DNS 查询与响应详解
电脑或手机(通常是操作系统或网络浏览器)问 DNS 服务器:“www.example.com 的 IP 地址是多少?”
这个过程就像查电话号码一样,首先,电脑或手机(作为 DNS 客户端)会向 DNS 服务器发起一个请求,询问:“www.example.com 的 IP 地址是多少?” 这个请求会包含一些信息,例如一个随机生成的“查询 ID”,用于后续匹配响应;还会说明要查询的域名(QNAME,比如www.example.com)以及要查询的记录类型(QTYPE),例如 A 记录表示要查询 IPv4 地址,AAAA 记录表示要查询 IPv6 地址,CNAME 记录表示要查询别名等等。收到请求后,DNS 服务器会进行查询,然后将结果返回给 DNS 客户端。DNS 服务器返回的响应会包含与请求匹配的“查询 ID”,以及查询的结果。结果中会包含域名(Name)、记录类型(Type,对应于请求中的 QTYPE,例如 A、AAAA 或 CNAME),以及实际的资源数据(RDATA),例如 IP 地址或别名。
QTYPE(查询类型):
A记录:IPv4 地址AAAA记录:IPv6 地址CNAME记录:别名MX记录:邮件交换服务器TXT记录:文本信息
QCLASS(查询类别):通常为 IN(Internet)
DNS 查询类型:
- 递归查询:客户端向 DNS 服务器请求完整解析,如果服务器不知道答案,它会代替客户端向其他服务器查询,直到得到结果
- 迭代查询:客户端只向 DNS 服务器请求,如果服务器不知道答案,它会返回可以查询的其他服务器的地址,客户端再向那些服务器查询。
DNS 缓存:DNS 客户端和服务器通常会缓存 DNS 记录,以减少查询延迟。TTL 值决定了缓存的有效时间。
HTTP - 通向万维网的协议
当在浏览器中输入 URL 时,浏览器会根据地址栏的 URL,从 Web 服务端获取文件资源等信息,从而显示出网页,像这种发送请求获取服务器资源的 Web 浏览器等,都可称为客户端
Web 使用一种名为 HTTP 的协议作为规范,完成从客户端到服务器端等一系列的运作流程,Web 是建立在 HTTP 协议上通信的
提示
HTTP(Hyper Text Transfer Protocol)本来译为超文本转移协议,但是通常被译为超文本传输协议
HTTP 的诞生
在最初的时候,接触到的 Web 的人很少,但是为了形成共享信息的设想,便设计出了多文档相互关联形成的超文本,连接成可互相参阅的万维网,因此提出了三种万维网构建技术:
- HTML 作为文档的文本标记语言
- HTTP 作为文档的传递协议
- URL 作为文档的所在地址
这是发展历史:
- HTTP 在 1990 年问世,但是并没有作为正式的标准,HTTP 正式作为标准是 1996 年,版本被定为 HTTP/1.0,这个标准一直被服务端使用至今,增加了很多请求方法,状态码,响应头,缓存等
- 1990 年,第一台 Web 服务器和 Web 浏览器诞生,HTML 1.0 诞生,由于存在很多模糊不清的地方,被废弃了
- 1993 年,可以显示图像的浏览器问世
- 1994 年,网景公司发布了自家的浏览器,微软公司也发布了 IE 1.0 和 2.0,HTML 也到了 2.0 时代,微软和网景爆发浏览器大战,浏览器厂商对标准化视而不见
- 1997 年公布的 HTTP/1.1 是目前主流的 HTTP 协议版本,增加了持久性连接,流水线,host 字段
- 2000 年,网景公司逐渐衰落,宣布浏览器大战告一段落
- 2004 年,Mozilla 发布了 Firefox 浏览器,第二次浏览器大战宣布开始。与此同时 IE 一直升级到了 10 版本,其他浏览器厂商,Chrome、Opera、Safari 等开始抢占市场份额
- HTTP/2 是 HTTP 协议的第二个主要的版本,在 2015 年被发布,所有的数据都是以帧进行传输,服务端可以主动推送信息到客户端,主要解决了前面的版本效率低下的问题
HTTP 的出现主要是解决文本传输的难题,由于协议本身很简单,现在的 HTTP 已经超出了 Web 的局限,被运用到了各种场景
URI 和 URL
URL 是浏览器访问 Web 服务器时需要输入的地址,而 URI 是统一资源定位符,它是一个标准,标准的 URI 协议方案有很多种,URI 用于标记某一互联网资源,而 URL 标志资源的地点,由此可见,URL 是 URI 的子集
一个完整的 URI 格式是这样的:协议方案://登录信息@服务器地址:端口号/文件路径?查询字符串#片段标记
这是一个网址的例子:http://user:pass@www.example.com:80/dir/index.html?id=1#p1
- 协议方案:这个 URI 采用 HTTP 协议
- 登录信息:指定用户名和密码作为从服务器获取资源时必要的身份验证,是可选的
- 服务器地址:可以是域名或者 IP 地址
- 端口号:指定服务器连接的网络端口,可选的
- 带层次的文件路径:指定服务器上的文件路径访定位特指的资源
- 查询字符串:针对已经指定的文件路径内的资源,是可选的
- 片段标记,用于获取已获得资源中的子资源(文档中的某个位置),是可选的
报文
用于 HTTP 协议交互的信息被称为 HTTP 报文,请求端叫请求报文,响应端的叫做响应报文。报文本身是由多行数据构成的字符串文本,报文大致可以分为首部和主体,通常情况下不一定有主体部分
请求报文的首部由下面数据组成:
请求头 - 由起始行和首部组成
请求体
请求头:包含应用于请求的方法,请求 URI 和 HTTP 版本
状态行:包含响应结果的状态吗,原因短语和 HTTP 版本
首部字段:包含请求和响应的各种条件和属性的各类首部,一般是通用首部、请求首部、响应首部和实体首部
其他:包含 HTTP 的 RFC 里未定义的首部,比如 Cookie 等
响应报文由以下部分组成:
- 响应头 - 由起始行和首部组成
- 响应体
HTTP 在传输数据的时候可以直接传输数据本体,但也可能会通过编码的方式提升传输速率,在传输时进行编码会有效地处理大量的访问请求,但是通常会消耗更多 CPU 资源
报文是 HTTP 通信的基本单位,而实体是作为请求或响应的有效载荷数据被传输,其内容由实体首部和实体主体组成。通常情况下,报文主体等于实体主体
HTTP 在进行传输时,为了提升速率,可能会先压缩一下数据,然后再发送,HTTP 中有一种被称为内容编码的功能也可以进行这样的操作,内容编码能够指明实体内容上的编码格式,并原样压缩实体,编码后的实体由客户端接受并负责解码,这是常用的内容编码:
- gzip:GNU zip
- compress:UNIX 标准压缩
- deflate:zlib
- identity:不进行编码
在实体资源未全部传输完成之前,浏览器是无法显示请求页面的,因此在传输大体积数据时,通过把数据分割成小的数据块,能够让浏览器逐步的显示页面,这种分块功能叫做分块传输编码
使用分块传输编码的实体主体会由客户端负责解码,恢复到编码前的实体主体
HTTP 协议中使用了多部份对象集合的方法,在一份报文中可以含有多种类型的实体,通常是在上传图片或文件时使用
这是一些多部份对象集合所包含的对象:
multipart/form-data:表单上传二进制文件时使用multipart/byteranges:响应报文包含了多个范围的内容时使用
在报文中使用多部份对象集合时,需要在首部字段里加上Content-Type
Content-Type是很重要的字段,决定了 Body 的编码方式:
- application/x-www-form-urlencoded:默认的编码方式
- application/json:序列化后的 JSON 字符串
- text/xml:XML 作为编码方式
- text/plain:普通纯文本
动词
在进行 HTTP 通信时,必有一个客户端和服务端,通常情况下,客户端和服务端是可以互换的,而 HTTP 能够明确区分哪端是客户端和服务端
在发送请求的时候,可以通过一些明确的动词来告诉服务器的意图,比如:
- GET:常用于获取资源
- POST:用来传输实体的主体,POST 最主要的目的是并不是获取响应的主体内容
- PUT:用于传输文件
- DELETE:删除文件
- HEAD:获取报文首部,和 GET 一样,只是不返回主体部分
- OPTIONS:询问支持的方法
- TRACE:追踪路径
- CONNECT:使用安全隧道协议连接代理
状态码
HTTP 状态码负责表示客户端的请求返回结果,标记服务端的处理是否正常,借助状态码可以知道服务端是进行了正常的处理,还是出现了错误
状态码是以 3 位数字和原因短语组成的,数字中的第一位指定了响应的类别,后两类没有分类:
- 1xx:信息行状态码,表示接收的请求正在处理
- 2xx:成功状态码,表示请求正常处理完毕
- 3xx:重定向状态码,需要进行附加操作以完成请求
- 4xx:客户端错误状态码,表示服务端无法处理请求
- 5xx:服务端错误状态码,表示服务端处理请求出错
只要遵守状态码类别的定义,便可以改变所定义的状态码,或者服务端自行创建状态码都可以,仅仅记录的已知状态码就有很多种,实际上正常使用的大致只有 14 种
- 200:OK
- 204:Not Content,没有返回内容
- 206:Partial Content,进行了范围请求
- 301:Moved Permanently,永久重定向
- 302:Found,临时重定向
- 303:See Other,表示由于请求对应的资源存在着另一个 URI,应使用 GET 方法定向获取请求的资源
- 304:No Modified,浏览器缓存相关,允许访问资源,但是服务不会响应内容
- 307:Temporary Redirect,临时重定向,不会从 POST 变为 GET
- 400:Bad Request,
- 401:Unauthorized,
- 403:Forbidden
- 404:Not Found
- 500:Internal Server Error
- 503:Service Unavailable
内容协商
同一个 Web 网站可能存在多份相同的内容页面,比如英文和中文的页面,虽然内容上相同,但使用的语言并不同,当浏览器的默认语言为英文或中文,访问相同的页面时,则会显示对应的英文或中文版的页面,这种机制被称为内容协商
内容协商是指客户端和服务端就响应的资源内容进行交涉,然后提供给客户端最适合的资源,内容写上会以响应资源的语言、字符集、编码方式等作为判断的条件,比如包含在请求报文中的某些首部字段:
- Accept:客户端接受哪些类型的信息
- Accept-Charset:接受的字符集
- Accept-Encoding:可接受的内容编码
- Accept-Language:自然语言
- Content-Type:Body 编码方式
- Authorization:证明客户端有权限查看某个资源
- Host:指定被请求的资源主机和端口号
- User-Agent:用户代理:操作系统及版本、CPU 类型、浏览器及版本、渲染引擎、浏览器语言
对于内容协商来说有三种类型:
- 服务器驱动协商:由服务端进行内容协商,以请求的首部字段进行参考,在服务端处理
- 客户端驱动协商:由客户端进行内容协商,用户从浏览器上显示的可选项中进行选择,或者通过 JavaScript 进行自动选择
- 透明协商:是服务端和客户端的结合体,由服务端和客户端各自进行内容协商的一种办法
持久连接
在 HTTP 协议中,每一次通信,就会断开一次 TCP 连接,随着通信次数增加,每次都会造成无谓的TCP 连接建立和断开,增加了消耗。比如发送了一个包含多张图片的 HTML 文档,会产生大量的通信消耗
持久连接就是用来解决这个问题的方案,只要任意一端没有提出断开连接,则保持 TCP 连接状态。即建立 1 次 TCP 连接后进行多次请求和响应的交互,减少了通信量,减轻了服务端的负载,另外请求和响应过早的结束,使页面显示速度也提高了,在 HTTP/1.1 中,所有的连接默认都是持久连接
在 Header 中使用Connection进行控制,默认为keep-alive,如果使用了close,则每个请求都会重新建立 TCP 连接
获取部分内容的请求
在下载一个体积稍大时的文件时,一旦出现了网络中断,就必须从头开始,为了解决这种问题,就出现了一种可恢复的下载机制,就是从之前下载中断的地方恢复下载,要实现这种功能就需要指定下载的范围,将一份文件分成范围下载,即使在某个范围中断了下载,那么重新下载的代价就不会很大
在 HTTP 中进行范围请求时,会用到首部字段 Range 来指定资源的 byte 范围,针对范围请求,响应会返回状态码为 206 的响应报文,对于多重范围的范围请求,会在首部字段 Content-Type 中标明 multipart/byteranges 后返回报文。当然,如果服务器无法响应范围请求,这回返回状态码 200 和完整的实体内容
Web 服务器
一台服务器可以搭建多个独立域名的 Web 网站,也可以作为通信路径上的中转服务器提升传输效率,HTTP/1.1 允许一台 HTTP 服务器搭建多个站点,比如使用一台服务器为多个用户服务,也可以以每个用户持有的域名运行各自不同的网站,这是利用了虚拟主机的功能
在物理层面上即使只有一台服务器,但是用了虚拟主机后,便可以假想成很多个服务器,在客户端使用 HTTP 协议访问服务器时,会通常使用类似于xxx.com这样的域名来进行定位,在互联网上,域名通过 DNS 解析成域名对应的 IP 地址之后便可以访问目标服务器,因此之后的访问都实际上是以 IP 地址形式的访问
基于 Cookie 的状态管理
HTTP 是一种不保存状态的协议,不会对请求和响应之间的通信状态保存。无状态的 HTTP 会减少性能开销,但是又要解决类似的矛盾问题,比如无法保留一个用户从一个页面跳转到另一个页面的登陆状态,于是引进了 Cookie 技术,即在请求和响应报文中写入 Cookie 信息来管理状态,cookie 是一种持久化数据的技术
Cookie 会根据服务器发送的响应报文内的一个叫做Set-Cookie的首部字段信息,通知客户端保存 Cookie,当下次客户端再向该服务器发送请求时,客户端会自动再请求报文中加入 Cookie 值再发送。服务器发现发送过来的 Cookie 后,会检查从哪个客户端发送的请求,然后对比服务器上的 Cookie 记录,最后验证状态
HTTPS
超文本传输安全协议(Hypertext Transfer Protocol Secure,简称:HTTPS)是一种通过计算机网络进行安全通信的传输协议。HTTPS经由HTTP进行通信,利用SSL/TLS来加密数据包。HTTPS的主要目的是提供对网站服务器的身份认证,保护交换数据的隐私与完整性
HTTPS的优点如下:
- 使用 HTTPS 协议可以认证用户和服务器,确保数据发送到正确的客户端和服务器
- 使用 HTTPS 协议可以进行加密传输、身份认证,通信更加安全,防止数据在传输过程+ 中被窃取、修改,确保数据安全性
- HTTPS 是现行架构下最安全的解决方案,虽然不是绝对的安全,但是大幅增加了中间+ 人攻击的成本
HTTPS 的缺点如下:
- HTTPS 需要做服务器和客户端双方的加密个解密处理,耗费更多服务器资源,过程复杂
- HTTPS 协议握手阶段比较费时,增加页面的加载时间
- SSL 证书是收费的,功能越强大的证书费用越高
- HTTPS 连接服务器端资源占用高很多,支持访客稍多的网站需要投入更大的成本
- SSL 证书需要绑定 IP,不能在同一个 IP 上绑定多个域名
跨域资源共享
CORS 预请求
在进行跨域请求的时候,允许使用 GET、POST、HEAD 方法,Content-Type 也限制为 text/plain、multipart/form-data、application/x-www-form-urlencoded,不能使用自定义的请求头,XMLHttpRequestUpload 对象均没有注册任何事件监听器,请求中没有使用 ReadableStream 对象
缓存控制
默认情况下,浏览器不会缓存从服务器上请求的资源,可以通过Cache-Control来进行控制
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Cache-Control?from=from_parent_mindnote
在前端中使用打包工具的时候,脚本文件名是不一样的,这避免了缓存带来了无法刷新的问题
参考资料
- 计算机网络:自顶向下方法
- 一个用于查阅域名价格的网站
