跳转至

1. 计算机网络结构(概述)(重点)

七层模型:应表会传网数物

五层模型:应传网数物

1. 五层结构

  • 应用层 :提供用户接口,特指能够发起网络流量的程序
  • 传输层: 提供的是进程间的通用数据传输服务。由于应用层协议很多,定义通用的传输层协议就可以支持不断增多的应用层协议。传输层包括两种协议:
  • 传输控制协议 TCP,提供面向连接、可靠的数据传输服务,数据单位为报文段;
  • 用户数据报协议 UDP,提供无连接、尽最大努力的数据传输服务,数据单位为用户数据报。
  • TCP 主要提供完整性服务,UDP 主要提供及时性服务。
  • 网络层:为主机间提供数据传输服务,而运输层协议是为主机中的进程提供服务。网络层把传输层传递下来的报文段或者用户数据报封装成分组。(负责选择最佳路径 规划IP地址)
  • 路由器查看数据包目标IP地址,根据路由表为数据包选择路径。路由表中的类目可以人工添加(静态路由)也可以动态生成(动态路由)。
  • 数据链路层:不同的网络类型,发送数据的机制不同,数据链路层就是将数据包封装成能够在不同的网络传输的帧。能够进行差错检验,但不纠错,监测处错误丢掉该帧。
  • 帧的开始和结束,透明传输,差错校验
  • 物理层:实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异。

2. ISO七层模型中表示层和会话层功能是什么?

  • 表示层 :数据压缩、加密以及数据描述。这使得应用程序不必担心在各台主机中表示/存储的内部格式(二进制、ASCII,比如乱码)不同的问题。
  • 会话层 :建立会话,如session认证、断点续传。通信的应用程序之间建立、维护和释放面向用户的连接。通信的应用程序之间建立会话,需要传输层建立1个或多个连接。(...后台运行的木马,netstat -n)

3. 数据在各层之间的传递过程

在向下的过程中,需要添加下层协议所需要的首部或者尾部,而在向上的过程中不断拆开首部和尾部。

  1. 路由器只有下面三层协议,因为路由器位于网络核心中,不需要为进程或者应用程序提供服务,因此也就不需要运输层和应用层。
  2. 交换机只有下面两层协议

4. TCP/IP四层模型

TCP/IP 四层模型 是目前被广泛采用的一种模型,我们可以将 TCP / IP 模型看作是 OSI 七层模型的精简版本,由以下 4 层组成:

  1. 应用层
  2. 传输层
  3. 网络层
  4. 网络接口层

需要注意的是,我们并不能将 TCP/IP 四层模型 和 OSI 七层模型完全精确地匹配起来,不过可以简单将两者对应起来。

网络接口层:

我们可以把网络接口层看作是数据链路层和物理层的合体。

  1. 数据链路层(data link layer)通常简称为链路层( 两台主机之间的数据传输,总是在一段一段的链路上传送的)。数据链路层的作用是将网络层交下来的 IP 数据报组装成帧,在两个相邻节点间的链路上传送帧。每一帧包括数据和必要的控制信息(如同步信息,地址信息,差错控制等)。
  2. 物理层的作用是实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异

可以直接说一下 数据链路层和物理层的作用

补充:

数据链路层上传输使用的是物理地址(MAC 地址)。ARP 协议是将 IP 地址转换为 MAC 地址。RARP 是 MAC 地址到 IP 地址的转换。

ICMP 协议功能:(1)确认 IP 包是否成功到达目标地址;(2)通知在发送过程中 IP 包被丢弃的原因。

ping命令的功能

(1)能验证网络的连通性 (2)会统计响应时间和TTL(IP包中的Time To Live,生存周期)

如何验证? (1)ping命令会先发送一个 ICMP Echo Request给对端 (2)对端接收到之后, 会返回一个ICMP Echo Reply (3)若没有返回,就是超时了,会认为指定的网络地址不存在。

5. 为什么要网络分层?

  1. 各层之间相互独立:各层之间相互独立,各层之间不需要关心其他层是如何实现的,只需要知道自己如何调用下层提供好的功能就可以了(可以简单理解为接口调用)。这个和我们对开发时系统进行分层是一个道理。
  2. 提高了整体灵活性 :每一层都可以使用最适合的技术来实现,你只需要保证你提供的功能以及暴露的接口的规则没有改变就行了。这个和我们平时开发系统的时候要求的高内聚、低耦合的原则也是可以对应上的。
  3. 大问题化小 : 分层可以将复杂的网络间题分解为许多比较小的、界线比较清晰简单的小问题来处理和解决。这样使得复杂的计算机网络系统变得易于设计,实现和标准化。 这个和我们平时开发的时候,一般会将系统功能分解,然后将复杂的问题分解为容易理解的更小的问题是相对应的,这些较小的问题具有更好的边界(目标和接口)定义

6. 从输入URL到页面展示到底发生了什么?(非常重要)

  1. 浏览器接收到⽤户请求,先检查浏览器缓存⾥是否有缓存该资源,如果有直接返回;如果没有进⼊下⼀ 步⽹络请求。
  2. ⽹络请求前,进⾏ DNS解析 ,以获取请求域名的 IP地址 。如果请求协议是 HTTPS ,那么还需要 建⽴TLS连接 。DNS解析时会按本地浏览器缓存->本地 Host ⽂件->路由器缓存-> DNS 服务器-> 根 DNS 服务器的顺序查询域名对应 IP ,直到找到为⽌。
  3. 浏览器与服务器IP建⽴TCP连接。连接建⽴后,浏览器端会构建请求⾏、请求头等信息,并把和该域名 相关的 Cookie 等数据附加到请求头中,向服务器构建请求信息。
  4. 服务器接收到请求信息,根据请求⽣成响应数据。
  5. 浏览器解析响应头。若响应头状态码为 301、302 ,会重定向到新地址;若响应数据类型是字节流类 型,⼀般会将请求提交给下载管理器;若是HTML类型,会进⼊下⼀部渲染流程。
  6. 浏览器解析 HTML ⽂件,创建 DOM 树,解析 CSS 进⾏样式计算,然后将CSS和DOM合并,构建渲 染树;最后布局和绘制渲染树,完成页面展示。

应用层

DNS

DNS是什么?

DNS(Domain Name System) 是⼀种⽤于将域名(例如www.baidu.com)转换为IP地址(例如 220.181.111.188 )的分布式系统。在互联⽹上,计算机和其他⽹络设备使⽤IP地址来相互识别和通信。然⽽, IP地址是⼀串数字,不太⽅便⼈们使⽤和记忆,所以就使⽤了域名来代替复杂的IP地址

为什么DNS采用分布式设计?

因为如果采用集中式设计有以下问题:

  1. 单点故障 如果 DNS 服务器崩溃,那么整个⽹络随之瘫痪。通信容量(traaffic volume) ,单个 DNS 服务器不得不处理所有的 DNS 查询,这种查询级别可能是上百万上千万级,⼀台服务器很难满⾜;
  2. 远距离集中式数据库 单个 DNS 服务器不可能 邻近 所有的⽤户,假设在美国的 DNS 服务器不可能临近让澳⼤利亚的查询使⽤,其中查 询请求势必会经过低速和拥堵的链路,造成严重的时延;
  3. 维护 维护成本巨⼤,⽽且还需要频繁更新。

域名的层级关系

DNS 中的域名都是⽤句点来分隔的,⽐如 www.server.com ,这⾥的句点代表了不同层次之间的界限。 在域名中,越靠右的位置表示其层级越⾼。

DNS解析过程

  1. 先查询浏览器缓存是否有该域名对应的IP地址。
  2. 如果浏览器缓存中没有,会去计算机本地的Host⽂件中查询是否有对应的缓存。
  3. 如果Host⽂件中也没有则会向本地的DNS服务器(通常由你的互联⽹服务提供商(ISP)提供, ⽐如中国移 动)发送⼀个DNS查询请求。
  4. 如果本地DNS解析器有该域名的ip地址,就会直接返回,如果没有缓存该域名的解析记录,它会向根DNS服务器发出查询请求。根DNS服务器并不负责解析域名,但它能告诉本地DNS解析器应该向哪个顶级域 (.com/.net/.org)的DNS服务器继续查询。
  5. 本地DNS解析器接着向指定的顶级域名DNS服务器发出查询请求。顶级域DNS服务器也不负责具体的域名解 析,但它能告诉本地DNS解析器应该前往哪个权威DNS服务器查询下⼀步的信息。
  6. 本地DNS解析器最后向权威DNS服务器发送查询请求。 权威DNS服务器是负责存储特定域名和IP地址映射的 服务器。当权威DNS服务器收到查询请求时,它会查找"example.com"域名对应的IP地址,并将结果返回给本 地DNS解析器。
  7. 本地DNS解析器将收到的IP地址返回给浏览器,并且还会将域名解析结果缓存在本地,以便下次访问时更快地 响应。
  8. 浏览器发起连接: 本地DNS解析器已经将IP地址返回给您的计算机,您的浏览器可以使⽤该IP地址与⽬标服 务器建⽴连接,开始获取⽹⻚内容。

递归查询和迭代查询

递归查询和迭代查询是在DNS解析过程中⽤于获取域名解析信息的两种不同⽅法。

  1. 递归查询

在递归查询中,DNS客户端(通常是本地DNS解析器)向上层DNS服务器(如根域名服务器、顶级域名服务器)发 起查询请求,并要求这些服务器直接提供完整的解析结果。递归查询的特点是,DNS客户端只需要发送⼀个查询请 求,然后等待完整的解析结果。上层DNS服务器会⾃⾏查询下⼀级的服务器,并将最终结果返回给DNS客户端。

  1. 迭代查询

在迭代查询中,DNS客户端向上层DNS服务器发起查询请求,但不要求直接提供完整的解析结果。相反,DNS客户 端只是询问上层服务器⼀个更⾼级的域名服务器的地址,然后再⾃⾏向那个更⾼级的服务器发起查询请求,以此类 推,直到获取完整的解析结果为⽌。 递归查询适合普通⽤户和客户端,⽽迭代查询适⽤于DNS服务器之间的通信。

为什么域名解析用UDP协议?

因为UDP快啊!UDP的DNS协议只要一个请求、一个应答就好了。

而使用基于TCP的DNS协议要三次握手、发送数据以及应答、四次挥手,但是UDP协议传输内容不能超过512字节。

不过客户端向DNS服务器查询域名,一般返回的内容都不超过512字节,用UDP传输即可。

为什么区域传送用TCP协议?

因为TCP协议可靠性好啊!

你要从主DNS上复制内容啊,你用不可靠的UDP? 因为TCP协议传输的内容大啊,你用最大只能传512字节的UDP协议?万一同步的数据大于512字节,你怎么办?所以用TCP协议比较好!

Ping命令基于哪一层协议的原理是什么?

ping命令基于网络层的命令,是基于ICMP协议工作的。

HTTP(重点)

HTTP 基本概念

HTTP 是什么?

能否详细解释「超文本传输协议」?

HTTP 是超文本传输协议,也就是HyperText Transfer Protocol。

它可以拆成三个部分:

  • 超文本
  • 传输
  • 协议

针对 HTTP 协议,我们可以这么理解。

HTTP 是一个用在计算机世界里的协议。它使用计算机能够理解的语言确立了一种计算机之间交流通信的规范(两个以上的参与者),以及相关的各种控制和错误处理方式(行为约定和规范

针对传输,我们可以进一步理解了 HTTP。

HTTP 是一个在计算机世界里专门用来在两点之间传输数据的约定和规范。

HTTP 传输的内容是「超文本」。

它就是超越了普通文本的文本,它是文字、图片、视频等的混合体,最关键有超链接,能从一个超文本跳转到另外一个超文本。

即:HTTP 是一个在计算机世界里专门在「两点」之间「传输」文字、图片、音频、视频等「超文本」数据的「约定和规范」。

HTTP常见的状态码

状态码 类别 原因短语
1XX Informational(信息性状态码) 接收的请求正在处理
2XX Success(成功状态码) 请求正常处理完毕
3XX Redirection(重定向状态码) 需要进行附加操作以完成请求
4XX Client Error(客户端错误状态码) 服务器无法处理请求
5XX Server Error(服务器错误状态码) 服务器处理请求出错

下面了解即可:

  • 100 Continue :表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应。

  • 200 OK

  • 204 No Content :请求已经成功处理,但是返回的响应报文不包含实体的主体部分。一般在只需要从客户端往服务器发送信息,而不需要返回数据时使用。
  • 206 Partial Content :表示客户端进行了范围请求。响应报文包含由 Content-Range 指定范围的实体内容。

  • 301 Moved Permanently :永久性重定向

  • 302 Found :临时性重定向
  • 303 See Other :和 302 有着相同的功能,但是 303 明确要求客户端应该采用 GET 方法获取资源。

  • 400 Bad Request :请求报文中存在语法错误。

  • 401 Unauthorized :该状态码表示发送的请求需要有认证信息(BASIC 认证、DIGEST 认证)。如果之前已进行过一次请求,则表示用户认证失败。
  • 403 Forbidden :请求被拒绝,服务器端没有必要给出拒绝的详细理由。
  • 404 Not Found

  • 500 Internal Server Error :服务器正在执行请求时发生错误。

  • 503 Service Unavailable :服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。

HTTP 常见的字段

Host 字段: 客户端发送请求时,用来指定服务器的域名。(比如 www.baidu.com)

有了 Host 字段,就可以将请求发往「同一台」服务器上的不同网站。

Content-Length 字段: 服务器在返回数据时,会有 Content-Length 字段,表明本次回应的数据长度。

HTTP 是基于 TCP 传输协议进行通信的,而使用了 TCP 传输协议,就会存在一个“粘包”的问题,HTTP 协议通过设置回车符、换行符作为 HTTP header 的边界,通过 Content-Length 字段作为 HTTP body 的边界,这两个方式都是为了解决“粘包”的问题

扩展:什么是 TCP 粘包?

扩展:基于UDP的传输协议?

DNS(Domain Name System):DNS是用于将域名解析为相应IP地址的协议。DNS使用UDP进行域名解析请求和响应的传输。

DHCP(Dynamic Host Configuration Protocol):DHCP用于自动分配IP地址、子网掩码、网关和其他网络配置参数给客户端设备。在初始阶段,DHCP使用UDP广播进行客户端和服务器之间的通信。

TFTP(Trivial File Transfer Protocol):TFTP是一种简单的文件传输协议,通常用于在网络上传输小文件。TFTP使用UDP进行文件传输。

SNMP(Simple Network Management Protocol):SNMP是一种网络管理协议,用于监控和管理网络设备。SNMP使用UDP进行管理信息的传输。

Connection 字段:Connection 字段最常用于客户端要求服务器使用「HTTP 长连接」机制,以便其他请求复用。

HTTP 长连接的特点是,只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。

HTTP/1.1 版本的默认连接都是长连接,但为了兼容老版本的 HTTP,需要指定 Connection 首部字段的值为 Keep-Alive

开启了 HTTP Keep-Alive 机制后, 连接就不会中断,而是保持连接。当客户端发送另一个请求时,它会使用同一个连接,一直持续到客户端或服务器端提出断开连接。

扩展:HTTP Keep-Alive 和 TCP Keepalive 有什么区别?

相关问题:

HTTP如何实现长连接?什么时候会超时?

http长连接是指http的请求头和响应头 均有connection: keep-alive的请求,也就是客户端和服务端均为keep-alive的请求。

实际上,http是请求/响应式的,无长短之分,长连接实际上是指TCP的连接为长连接。这样多个http请求就可以复用一个TCP连接,减少了了TCP连接建立和断开的消耗。

当 超过 Keep-Alive: timeout (连接存活时间),就需要重新建立连接。

Content-Type 字段:Content-Type 字段用于服务器回应时,告诉客户端,本次数据是什么格式。

客户端请求的时候,可以使用 Accept 字段声明自己可以接受哪些数据格式。

Content-Encoding 字段:Content-Encoding 字段说明数据的压缩方法。表示服务器返回的数据使用了什么压缩格式

客户端在请求时,用 Accept-Encoding 字段说明自己可以接受哪些压缩方法。

HTTP 请求方法

  1. GET: 获取资源,服务端返回报文实体主体部分。
  2. HEAD: HEAD 与 GET 类似,但是服务端不返回报文实体主体部分。
  3. POST: POST 传输实体主体
  4. PUT: PUT 上传文件,存在安全性问题,一般不建议使用
  5. PATCH: PATCH 对资源进行部分修改
  6. DELETE: DELETE 删除文件,与 PUT 功能相反,并且同样不带验证机制
  7. OPTIONS 查询指定的 URL 能够支持的方法,会返回 Allow:GET,POST,HEAD 这样的内容。
  8. CONNECT 要求在与代理服务器通信时建立隧道
  9. TRACE 追踪路径,服务器会将通信路径返回给客户端,通常不会使用 TRACE,并且它容易受到 XST 攻击(Cross-Site Tracing,跨站追踪)

GET和POST的区别(重点)

  1. 作⽤不同
  2. GET⽤于从服务端获取资源
  3. POST⼀般⽤来向服务器端提交数据
  4. 参数传递⽅式不同
  5. GET请求的参数⼀般写在URL中,且只接受ASCII字符
  6. POST请求参数⼀般放在请求体中,对于数据类型也没有限制
  7. 安全性不同
  8. 因为参数传递⽅式的不同,所以两者安全性不同,GET请求的参数直接暴露在URL中,所以更不安全,不能⽤来传递敏感信息。
  9. 参数⻓度限制不同
  10. GET传送的数据量较⼩,不能⼤于2KB。
  11. POST传送的数据量较⼤,⼀般被默认为不受限制。

HTTP 协议没有 Body 和 URL 的⻓度限制,对 URL 限制的⼤多是浏览器和服务器的原因。

  1. 编码⽅式不同
  2. GET 请求只能进⾏ URL 编码(application/x-www-form-urlencoded)
  3. POST ⽀持多种编码⽅式(application/x-www-form-urlencoded 或 multipart/form-data。为⼆进制数据使⽤多种编码。)
  4. 缓存机制不同
  5. GET 请求会被浏览器主动cache,⽽ POST 不会,除⾮⼿动设置。
  6. GET 请求参数会被完整保留在浏览器历史记录⾥,⽽ POST 中的参数不会被保留。
  7. GET 产⽣的 URL 地址可以被 保存为书签,⽽ POST 不可以。
  8. GET 在浏览器回退时是⽆害的,⽽ POST 会再次提交请求。
  9. 时间消耗不同
  10. GET 产⽣⼀个 TCP 数据包;
  11. POST 产⽣两个 TCP 数据包。
  • 对于 GET ⽅式的请求,浏览器会把 header 和 data ⼀并发送出去,服务器响应 200(返回数据);
  • ⽽对于 POST,浏览器先发送 Header,服务器响应 100 continue,浏览器再发送 data,服务器响 应 200 ok(返回数据)
  1. 幂等

意思是多次执⾏相同的操作,结果都是「相同」的。

  • GET ⽅法就是安全且幂等的,因为它是「只读」操作,⽆论操作多少次,服务器上的数据都是安全的, 且每次的结果都是相同的。
  • POST 因为是「新增或提交数据」的操作,会修改服务器上的资源,所以是不安全的,且多次提交数据 就会创建多个资源,所以不是幂等的

HTTP 缓存

对于已经请求过的资源,客户端或代理服务器会将其副本保存在本地存储中,并且在下次请求同⼀资源时,⾸先检 查缓存中是否存在有效的副本。如果存在有效的缓存,就直接读取本地的数据,不必在通过⽹络获取服务器的响应 了,这就是 HTTP缓存

HTTP 缓存有两种实现⽅式,分别是强制缓存和协商缓存

什么是强制缓存?

强缓存指的是只要浏览器判断缓存没有过期,则直接使用浏览器的本地缓存,决定是否使用缓存的主动性在于浏览器这边。

强缓存是利用下面这两个 HTTP 响应头部(Response Header)字段实现的,它们都用来表示资源在客户端缓存的有效期:

  • Cache-Control, 是一个相对时间;
  • Expires,是一个绝对时间

如果 HTTP 响应头部同时有 Cache-Control 和 Expires 字段的话,Cache-Control 的优先级高于 Expires

扩展:

Expires强缓存 :设置⼀个强缓存时间,此时间范围内,从内存中读取缓存并返回,因为 Expires 判断强缓存过期的机制是获取本地时间戳,与之前拿到的资源⽂件中的 Expires 字段的时间做⽐较。来判断是否需要对服务器发起请求, 所以这⾥有⼀个巨⼤的漏洞:“如果我本地时间不准咋办?”正是因为这个原因,该字段⽬ 前已经基本上被废弃了。

Cache-Control强缓存 : http1.1 中增加该字段,只要在资源的响应头上写上需要缓存多久就好了,单位是秒。 Cache-Control:max-age=N , 有max-age、s-maxage、no-cache、no-store、private、public这 六个属性。

  • max-age 决定客户端资源被缓存多久。
  • s-maxage 决定代理服务器缓存的时⻓。
  • no-cache 表示是强制进⾏协商缓存。
  • no-store 是表示禁⽌任何缓存策略。
  • public 表示资源既可以被浏览器缓存也可以被代理服务器缓存。
  • private 表示资源只能被浏览器缓存,默认为private

使用 Cache-Control 来实现强缓存。具体的实现流程如下:

  • 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response 头部加上 Cache-Control,Cache-Control 中设置了过期时间大小;
  • 浏览器再次请求访问服务器中的该资源时,会先通过请求资源的时间与 Cache-Control 中设置的过期时间大小,来计算出该资源是否过期,如果没有,则使用该缓存,否则重新请求服务器;
  • 服务器再次收到请求后,会再次更新 Response 头部的 Cache-Control

什么是协商缓存?

当我们在浏览器使用开发者工具的时候,你可能会看到过某些请求的响应码是 304,这个是告诉浏览器可以使用本地缓存的资源,通常这种通过服务端告知客户端是否可以使用缓存的方式被称为协商缓存。

上图就是一个协商缓存的过程,所以协商缓存就是与服务端协商之后,通过协商结果来判断是否使用本地缓存

协商缓存可以基于两种头部来实现。

第一种,基于请求头部中的 If-Modified-Since 字段与响应头部中的 Last-Modified 字段实现

这两个字段的意思是:

  • 响应头部中的 Last-Modified:标示这个响应资源的最后修改时间;
  • 请求头部中的 If-Modified-Since:当资源过期了,发现响应头中具有 Last-Modified 声明,则再次发起请求的时候带上 Last-Modified 的时间,服务器收到请求后发现有 If-Modified-Since 则与被请求资源的最后修改时间进行对比(Last-Modified),如果最后修改时间较新(大),说明资源又被改过,则返回最新资源,HTTP 200 OK;如果最后修改时间较旧(小),说明资源无新修改,响应 HTTP 304 走缓存。

基于 Last-Modified 和 If-Modified-Since 的协商缓存流程:

  1. ⾸先需要在服务器端读出⽂件修改时间,
  2. 将读出来的修改时间赋给响应头的 Last-Modified 字段。
  3. 最后设置 Cache-control:no-cache
  4. 当客户端读取到 Last-Modified 的时候,会在下次的请求标头中携带⼀个字段: If-Modified-Since ,⽽这 个请求头中的 If-Modified-Since 就是服务器第⼀次修改时候给他的时间
  5. 之后每次对该资源的请求,都会带上 If-Modified-Since 这个字段,⽽服务端就需要拿到这个时间并再次读 取该资源的修改时间,让他们两个做⼀个⽐对来决定是读取缓存还是返回新的资源
  6. 如果最后修改时间较新(⼤),说明资源⼜被改过,则返回最新资源, HTTP 200 OK 如果最后修改时间较旧 (⼩),说明资源⽆新修改,响应 HTTP 304 ⾛缓存

缺点:

  • 因为是根据⽂件修改时间来判断的,所以,在⽂件内容本身不修改的情况下,依然有可能更新⽂件修改时间 (⽐如修改⽂件名再改回来),这样,就有可能⽂件内容明明没有修改,但是缓存依然失效了。
  • 当⽂件在极短时间内完成修改的时候(⽐如⼏百毫秒)。因为⽂件修改时间记录的最⼩单位是秒,所以,如果⽂件在⼏百毫秒内完成修改的话,⽂件修改时间不会改变,这样,即使⽂件内容修改了,依然不会返回新的⽂件。

第二种:基于请求头部中的 If-None-Match 字段与响应头部中的 ETag 字段实现,将原先协商缓存的⽐较时间戳的形式修改成了⽐较⽂件指纹(根据⽂件内容计算出的唯⼀哈希值)

这两个字段的意思是:

  • 响应头部中 Etag:唯一标识响应资源;
  • 请求头部中的 If-None-Match:当资源过期时,浏览器发现响应头里有 Etag,则再次向服务器发起请求时,会将请求头 If-None-Match 值设置为 Etag 的值。服务器收到请求后进行比对,如果资源没有变化返回 304,如果资源变化了返回 200。

第一种实现方式是基于时间实现的,第二种实现方式是基于一个唯一标识实现的,相对来说后者可以更加准确地判断文件内容是否被修改,避免由于时间篡改导致的不可靠问题。

如果在第一次请求资源的时候,服务端返回的 HTTP 响应头部同时有 Etag 和 Last-Modified 字段,那么客户端再下一次请求的时候,如果带上了 ETag 和 Last-Modified 字段信息给服务端,这时 Etag 的优先级更高,也就是服务端先会判断 Etag 是否变化了,如果 Etag 有变化就不用在判断 Last-Modified 了,如果 Etag 没有变化,然后再看 Last-Modified。

为什么 ETag 的优先级更高?这是因为 ETag 主要能解决 Last-Modified 几个比较难以解决的问题:

  1. 在没有修改文件内容情况下文件的最后修改时间可能也会改变,这会导致客户端认为这文件被改动了,从而重新请求;
  2. 可能有些文件是在秒级以内修改的,If-Modified-Since 能检查到的粒度是秒级的,使用 Etag就能够保证这种需求下客户端在 1 秒内能刷新多次;
  3. 有些服务器不能精确获取文件的最后修改时间。

注意,协商缓存这两个字段都需要配合强制缓存中 Cache-Control 字段来使用,只有在未能命中强制缓存的时候,才能发起带有协商缓存字段的请求

当使用 ETag 字段实现的协商缓存的过程:

  • 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response 头部加上 ETag 唯一标识,这个唯一标识的值是根据当前请求的资源生成的;

  • 当浏览器再次请求访问服务器中的该资源时,首先会先检查强制缓存是否过期:

  • 如果没有过期,则直接使用本地缓存;

  • 如果缓存过期了,会在 Request 头部加上 If-None-Match 字段,该字段的值就是 ETag 唯一标识;

  • 服务器再次收到请求后,

会根据请求中的 If-None-Match 值与当前请求的资源生成的唯一标识进行比较

  • 如果值相等,则返回 304 Not Modified,不会返回资源
  • 如果不相等,则返回 200 状态码和返回资源,并在 Response 头部加上新的 ETag 唯一标识;

  • 如果浏览器收到 304 的请求响应状态码,则会从本地缓存中加载资源,否则更新资源。

缺点:

  • ETag需要计算⽂件指纹这样意味着,服务端需要更多的计算开销。如果⽂件尺⼨⼤,数量多,并且计算频 繁,那么ETag的计算就会影响服务器的性能。显然,ETag在这样的场景下就不是很适合。
  • ETag有强验证和弱验证,所谓将强验证,ETag⽣成的哈希码深⼊到每个字节。哪怕⽂件中只有⼀个字节改变 了,也会⽣成不同的哈希值,它可以保证⽂件内容绝对的不变。但是,强验证⾮常消耗计算量。ETag还有⼀ 个弱验证,弱验证是提取⽂件的部分属性来⽣成哈希值。因为不必精确到每个字节,所以他的整体速度会⽐强 验证快,但是准确率不⾼。会降低协商缓存的有效性

强缓存和协商缓存的工作流程:

HTTP 特性

特性:简单、灵活、易于扩展、应⽤⼴泛和跨平台。

  1. 简单

基本报⽂格式为header+body,头部信息也是key-value简单⽂本的形式,易于理解。

  1. 灵活和易于扩展

  2. HTTP协议⾥的各种请求⽅法、URI/URL、状态码、头字段等每个组成要求都没有被固定死,允许开发⼈员⾃定义和扩充;

  3. HTTP⼯作在应⽤层(OSI第七层),下层可以随意变化;

    HTTPS就是在HTTP与TCP之间增加了SSL/TSL安全传输层

    HTTP/1.1 和 HTTP/2.0 传输协议使用的是 TCP 协议,而到了 HTTP/3.0 传输协议改用了 UDP 协议。

  4. ⽆状态、明⽂传输、不安全

无状态的好处,因为服务器不会去记忆 HTTP 的状态,所以不需要额外的资源来记录状态信息,这能减轻服务器的负担,能够把更多的 CPU 和内存用来对外提供服务。

无状态的坏处,既然服务器没有记忆能力,它在完成有关联性的操作时会非常麻烦。

对于⽆状态的问题,解决⽅案友很多种,其中⽐较简单的⽅式的是 Cookie 技术, Cookie 通过在请求和响应报⽂中写⼊ Cookie 信息来控制客户端的状态。

相当于,在客户端第一次请求后,服务器会下发一个装有客户信息的「小贴纸」,后续客户端请求服务器的时候,带上「小贴纸」,服务器就能认得了了

明⽂传输:

  • 传输过程中的信息,是可⽅便阅读的,通过浏览器的F12控制台或Wireshark抓包都可以直接⾁眼查看

  • 明⽂传输为我们调试⼯作带来了极⼤的便利性,但信息透明,容易被窃取。

不安全:

1. 通信使用明文(不加密),内容可能会被窃听。比如,**账号信息容易泄漏,那你号没了。**
2. 不验证通信方的身份,因此有可能遭遇伪装。比如,**访问假的淘宝、拼多多,那你钱没了。**
3. 无法证明报文的完整性,所以有可能已遭篡改。比如,**网页上植入垃圾广告,视觉污染,眼没了。**

可以⽤ HTTPS 的⽅式解决,也就是通过引⼊ SSL/TLS 层,使得在安全上达到了极致

HTTP 1.1的性能

HTTP 协议是基于 TCP/IP,并且使用了「请求 - 应答」的通信模式,所以性能的关键就在这两点里。

  1. 长连接

早期 HTTP/1.0 性能上的一个很大的问题,那就是每发起一个请求,都要新建一次 TCP 连接(三次握手),而且是串行请求,做了无谓的 TCP 连接建立和断开,增加了通信开销。

为了解决上述 TCP 连接问题,HTTP/1.1 提出了长连接的通信方式,也叫持久连接。这种方式的好处在于减少了 TCP 连接的重复建立和断开所造成的额外开销,减轻了服务器端的负载。

持久连接的特点是,只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。

  1. 管道网络传输

HTTP/1.1 采用了长连接的方式,这使得管道(pipeline)网络传输成为了可能。

即可在同一个 TCP 连接里面,客户端可以发起多个请求,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。

举例来说,客户端需要请求两个资源。以前的做法是,在同一个 TCP 连接里面,先发送 A 请求,然后等待服务器做出回应,收到后再发出 B 请求。那么,管道机制则是允许浏览器同时发出 A 请求和 B 请求,如下图:

但是服务器必须按照接收请求的顺序发送对这些管道化请求的响应

如果服务端在处理 A 请求时耗时比较长,那么后续的请求的处理都会被阻塞住,这称为「队头堵塞」。

所以,HTTP/1.1 管道解决了请求的队头阻塞,但是没有解决响应的队头阻塞

注意!!!

实际上 HTTP/1.1 管道化技术不是默认开启,而且浏览器基本都没有支持,所以后面所有文章讨论 HTTP/1.1 都是建立在没有使用管道化的前提。大家知道有这个功能,但是没有被使用就行了。

  1. 队头阻塞

「请求 - 应答」的模式会造成 HTTP 的性能问题。为什么呢?

因为当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一同被阻塞了,会招致客户端一直请求不到数据,这也就是「队头阻塞」,好比上班的路上塞车。

队头阻塞

总之 HTTP/1.1 的性能一般般,后续的 HTTP/2 和 HTTP/3 就是在优化 HTTP 的性能。

HTTP 版本演变

⽬前为⽌,HTTP 常⻅的版本有 HTTP/1.1 , HTTP/2.0 , HTTP/3.0 ,不同版本的 HTTP 特性是不⼀样的。

HTTP/0.9

HTTP/0.9 是最早的HTTP版本,在1991年就已经发布,只⽀持 GET ⽅法,也没有请求头,服务器只能返回 HTML 格式的内容。

HTTP/1.0

HTTP/1.0 是HTTTP 协议的第⼀个正式版本, 主要具有以下特性:

  • 支持 cache, MIME

  • 引⼊了请求头和响应头,引入了 POST 和 HEAD 命令,⽀持多种请求⽅法和状态码

  • 不⽀持持久连接,每次请求都需要建⽴新的连接

HTTP/1.1

  1. 长连接:引入长连接机制并被默认采用,只要客户端和 服务器任意⼀端没有明确提出断开连接,则保持TCP连接状态。

  2. 管道网络传输,在同⼀个 TCP 连接⾥⾯,客户端可以发起多个请求,只要第⼀个请求发出去了,不必等其回来,就可以发第⼆个请求出去,可以减少整体的响应时间,降低线路负载,提高传输速度

  3. 新增方法: PUT、PATCH、OPTIONS、DELETE

HTTP1.1 仍然存在着不少问题:

  1. 头部冗余:每个请求和响应都需要带有⼀定的头部信息,每次互相发送相同的⾸部造成的浪费较多;
  2. 服务器是按请求的顺序响应的,如果服务器响应慢,会招致客户端⼀直请求不到数据,也就是队头阻塞;
  3. 没有请求优先级控制;
  4. 请求只能从客户端开始,服务器只能被动响应。

HTTP/2.0

HTTP/2 协议是基于 HTTPS 的,所以 HTTP/2 的安全性也是有保障的。

HTTP/2 相比 HTTP/1.1 性能上的改进:

  • 头部压缩
  • 二进制格式
  • 并发传输
  • 服务器主动推送资源

  • 头部压缩:HTTP/2 使⽤ HPACK 压缩算法对请求和响应头部进⾏压缩,减少了传输的头部数据量,降低了延迟。
  • ⼆进制帧:HTTP/2 将数据分割成⼆进制帧进⾏传输,分为头信息帧(Headers Frame)和数据帧(Data Frame),增加了数据传输的效率。
  • 并发传输:引出了 Stream 概念,多个 Stream 复⽤在⼀条 TCP 连接,针对不同的 HTTP 请求⽤独⼀⽆⼆的 Stream ID 来区分,接收端可以通过 Stream ID 有序组装成 HTTP 消息,不同 Stream 的帧是可以乱序发送 的,因此可以并发不同的 Stream ,也就是 HTTP/2 可以并⾏交错地发送请求和响应。
  • 服务器推送:在 HTTP/2 中,服务器可以对客户端的⼀个请求发送多个响应,即服务器可以额外的向客户端 推送资源,⽽⽆需客户端明确的请求。

下面是扩展:

小林Coding

  1. 头部压缩

HTTP/2 会压缩头(Header)如果你同时发出多个请求,他们的头是一样的或是相似的,那么,协议会帮你消除重复的部分

这就是所谓的 HPACK 算法:在客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样字段了,只发送索引号,这样就提高速度了。

  1. 二进制格式

HTTP/2 不再像 HTTP/1.1 里的纯文本形式的报文,而是全面采用了二进制格式,头信息和数据体都是二进制,并且统称为帧(frame):头信息帧(Headers Frame)和数据帧(Data Frame)

实现多路复用(并发传输))的原理:

  1. HTTP2.0 引入头信息压缩机制(header compression),头信息使用 gzip 或 compress 压缩后再发送;

  2. 客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,不发送同样字段, 只发送索引号,提高速度

  3. HTTP 2.0 允许服务器未经请求,主动向客户端发送资源,即服务器推送

HTTP/2 存在的缺陷:

  • HTTP/2 仍然存在着队头阻塞的问题,只不过问题是在传输层。

HTTP/2 是基于 TCP 协议来传输数据的,TCP 是字节流协议,TCP 层必须保证收到的字节数据是完整且连续的,这样内核才会将缓冲区⾥的数据返回给 HTTP 应⽤,那么当「前 1 个字节数据」没有到达时,后收到的字节数据只能存放在内核缓冲区⾥,只有等到这 1 个字节数据到达时,HTTP/2 应⽤层才能从内核中拿到数据,这就是 HTTP/2 队头阻塞问题。

  • TCP与TLS的握⼿时延迟

发出HTTP请求时,需要经过TCP三次握⼿和TLS四次握⼿,共计3RTT的时延才能发出请求数据

  • ⽹络迁移需要重新连接

⼀个TCP连接由【源IP地址,源端⼝,⽬标IP地址,⽬标端⼝】确定。若IP地址或端⼝发⽣变暖,这需要重新进⾏连接。这不利于移动设备切换⽹络的场景。要解决该问题,就要修改传输层协议。在HTTP3中传输层协议修改为了 UDP

HTTP/3 做的优化

HTTP/1.1 和 HTTP/2 都有队头阻塞的问题:

  • HTTP/1.1 中的管道( pipeline)虽然解决了请求的队头阻塞,但是没有解决响应的队头阻塞,因为服务端需要按顺序响应收到的请求,如果服务端处理某个请求消耗的时间比较长,那么只能等响应完这个请求后, 才能处理下一个请求,这属于 HTTP 层队头阻塞。
  • HTTP/2 虽然通过多个请求复用一个 TCP 连接解决了 HTTP 的队头阻塞 ,但是一旦发生丢包,就会阻塞住所有的 HTTP 请求,这属于 TCP 层队头阻塞。

HTTP/2 队头阻塞的问题是因为 TCP,所以 HTTP/3 把 HTTP 下层的 TCP 协议改成了 UDP!

UDP 发送是不管顺序,也不管丢包的,所以不会出现像 HTTP/2 队头阻塞的问题。大家都知道 UDP 是不可靠传输的,但基于 UDP 的 QUIC 协议 可以实现类似 TCP 的可靠性传输。

UDP 发送是不管顺序,也不管丢包的,所以不会出现像 HTTP/2 队头阻塞的问题。大家都知道 UDP 是不可靠传输的,但基于 UDP 的 QUIC 协议 可以实现类似 TCP 的可靠性传输。

QUIC 有以下 3 个特点:

  • 无队头阻塞
  • 更快的连接建立
  • 连接迁移

  • 无队头阻塞

QUIC 协议也有类似 HTTP/2 Stream 与多路复用的概念,也是可以在同一条连接上并发传输多个 Stream,Stream 可以认为就是一条 HTTP 请求。

QUIC 有自己的一套机制可以保证传输的可靠性的。当某个流发生丢包时,只会阻塞这个流,其他流不会受到影响,因此不存在队头阻塞问题。这与 HTTP/2 不同,HTTP/2 只要某个流中的数据包丢失了,其他流也会因此受影响。

所以,QUIC 连接上的多个 Stream 之间并没有依赖,都是独立的,某个流发生丢包了,只会影响该流,其他流不受影响。

  1. 更快的连接建立

对于 HTTP/1 和 HTTP/2 协议,TCP 和 TLS 是分层的,分别属于内核实现的传输层、openssl 库实现的表示层,因此它们难以合并在一起,需要分批次来握手,先 TCP 握手,再 TLS 握手。

HTTP/3 在传输数据前虽然需要 QUIC 协议握手,但是这个握手过程只需要 1 RTT,握手的目的是为确认双方的「连接 ID」,连接迁移就是基于连接 ID 实现的。

但是 HTTP/3 的 QUIC 协议并不是与 TLS 分层,而是 QUIC 内部包含了 TLS,它在自己的帧会携带 TLS 里的“记录”,再加上 QUIC 使用的是 TLS/1.3,因此仅需 1 个 RTT 就可以「同时」完成建立连接与密钥协商,如下图:

甚至,在第二次连接的时候,应用数据包可以和 QUIC 握手信息(连接信息 + TLS 信息)一起发送,达到 0-RTT 的效果。

如下图右边部分,HTTP/3 当会话恢复时,有效负载数据与第一个数据包一起发送,可以做到 0-RTT(下图的右下角):

  1. 连接迁移

基于 TCP 传输协议的 HTTP 协议,由于是通过四元组(源 IP、源端口、目的 IP、目的端口)确定一条 TCP 连接。

那么当移动设备的网络从 4G 切换到 WIFI 时,意味着 IP 地址变化了,那么就必须要断开连接,然后重新建立连接。而建立连接的过程包含 TCP 三次握手和 TLS 四次握手的时延,以及 TCP 慢启动的减速过程,给用户的感觉就是网络突然卡顿了一下,因此连接的迁移成本是很高的。

而 QUIC 协议没有用四元组的方式来“绑定”连接,而是通过连接 ID 来标记通信的两个端点,客户端和服务器可以各自选择一组 ID 来标记自己,因此即使移动设备的网络变化后,导致 IP 地址变化了,只要仍保有上下文信息(比如连接 ID、TLS 密钥等),就可以“无缝”地复用原连接,消除重连的成本,没有丝毫卡顿感,达到了连接迁移的功能。

所以, QUIC 是一个在 UDP 之上的 TCP + TLS + HTTP/2 的多路复用的协议。

QUIC 是新协议,对于很多网络设备,根本不知道什么是 QUIC,只会当做 UDP,这样会出现新的问题,因为有的网络设备是会丢掉 UDP 包的,而 QUIC 是基于 UDP 实现的,那么如果网络设备无法识别这个是 QUIC 包,那么就会当作 UDP包,然后被丢弃。

HTTP/3 现在普及的进度非常的缓慢,不知道未来 UDP 是否能够逆袭 TCP。

总结,HTTP/3 协议的特点:

  • 零 RTT 连接建⽴:QUIC 允许在⾸次连接时进⾏零往返时间(Zero Round Trip Time)连接建⽴,从⽽减少 了连接延迟,加快了⻚⾯加载速度。
  • ⽆队头阻塞: QUIC 使⽤ UDP 协议来传输数据。⼀个连接上的多个stream之间没有依赖, 如果⼀个stream丢了 ⼀个UDP包,不会影响后⾯的stream,不存在 TCP 队头阻塞
  • 连接迁移:QUIC 允许在⽹络切换(如从 Wi-Fi 到移动⽹络)时,将连接迁移到新的 IP 地址,从⽽减少连接的 中断时间。
  • 向前纠错机制:每个数据包除了它本身的内容之外,还包括了部分其他数据包的数据,因此少量的丢包可以通 过其他包的冗余数据直接组装⽽⽆需重传。向前纠错牺牲了每个数据包可以发送数据的上限,但是减少了因为 丢包导致的数据重传

3. HTTP三个版本的区别

Http/1.0(无连接,无状态):

浏览器的每次请求都需要与服务器建立一个 TCP 连接,服务器处理 完成后立即断开 TCP 连接(无连接)。服务器不记录客户端的请求,比如客户端第二次登录,服 务端并不知道这是第二次,也就是服务端不记录客户端的登录状态(无状态)。

Http/1.1:

  • 长连接

HTTP 1.0 需要使用 keep-alive 参数来告知服务器端要建立一个长连接,而 Http/1.1 默认支持 长连接。

HTTP 是基于 TCP/IP 协议的,创建一个 TCP 连接是需要经过三次握手的,有一定的开销, 如果每次通讯都要重新建立连接的话,对性能有影响。因此最好能维持一个长连接,可以用个 长连接来发多个请求。 长连接:指在一个 TCP 连接上可以连续发送多个数据包,如果没有数据包发送了,需要双 方发送检测包以维持此连接。

短连接:指通信双方有数据交互时,就建立一个 TCP 连接,数据发送完成后,则断开此连 接。

  • 节约带宽,断点续传

HTTP 1.1 支持只发送 header 信息(不带任何 body 信息),如果服务器认为客户端有权限请求 服务器,则返回 100,否则返回 401。客户端如果接受到 100,才开始把请求 body 发送到服务 器。这样当服务器返回 401 的时候,客户端就可以不用发送请求 body 了,节约了带宽。

当客户端已经有一部分的资源后,只需要跟服务器请求另外的部分资源即可。这也是支持文件断点续传的基础。

  • HOST 域

Http/1.0 是没有 host 域的,Http/1.1 才支持这个参数。

在一台物理服务器上可以存在多个虚拟 主机,http/1.0 可以通过 IP 地址访问服务器,http/1.1 通过 ip 地址加 host 域可以访问服务器中的多个 虚拟机,这多个虚拟机共享同一个 ip。

HTTP/2.0:

  • 多路复用

HTTP/2.0 使用了多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数 量比 Http/1.1 大了好几个数量级。

当然 Http/1.1 也可以多建立几个 TCP 连接,来支持处理更多并发的请求,但是创建 TCP 连 接本身也是有开销的。

TCP 连接有一个预热和保护的过程,先检查数据是否传送成功, 一旦成功过,则慢慢加大传输速度。因此对应瞬时并发的连接,服务器的响应就会变慢。所以最好使用一个简历好的连接。

  • 数据压缩

HTTP/1.1 的首部带有大量信息,而且每次都要重复发送 ,

HTTP/2.0 要求客户端和服务器 同时维护和更新一个包含之前见过的首部字段表,从而避免了重复传输。不仅如此,HTTP/2.0 也使用 Huffman 编码对首部字段进行压缩。

  • 服务器推送

HTTP/2.0 在客户端请求一个资源时,会把相关的资源一起发送给客户端,客户端就不需要 再次发起请求了。例如客户端请求 page.html 页面,服务端就把 script.js 和 style.css 等与之相 关的资源一起发给客户端。 这种方式非常合适加载静态资源。

cookie:

​ HTTP 协议是无状态的,主要是为了让 HTTP 协议尽可能简单,使得它能够处理大量事务,HTTP/1.1 引入 Cookie 来保存状态信息。

​ Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器之后向同一服务器再次发起请求时被携带上,用于告知服务端两个请求是否来自同一浏览器。由于之后每次请求都会需要携带 Cookie 数据,因此会带来额外的性能开销(尤其是在移动环境下)。

作用:

  • 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
  • 个性化设置(如用户自定义设置、主题等)
  • 浏览器行为跟踪(如跟踪分析用户行为等)

session:

session 可以存储在服务器上的文件、数据库或者内存中。也可以将 Session 存储在 Redis 这种内存型数据库中,效率会更高。

工作原理:

​ session 的工作原理是客户端登录完成之后,服务器会创建对应的 session,session 创建完之后,会把 session 的 id 发送给客户端,客户端再存储到浏览器中。这样客户端每次访问服务器时,都会带着 sessionid,服务器拿到 sessionid 之后,在内存找到与之对应的 session 这样就可以正常工作了。

cookie和session的区别: (大致掌握, 能说出来几点就行)

  1. 存储的位置不同

  2. cookie:存放在客户端,

  3. session:存放在服务端。Session存储的数据比较安全

  4. 存储的数据类型不同,

  5. 两者都是key-value的结构,但针对value的类型是有差异的

  6. cookie:value只能是字符串类型,session:value是Object类型

  7. 存储的数据大小限制不同

  8. cookie:大小受浏览器的限制,很多是是4K的大小,

  9. session:理论上受当前内存的限制,

  10. 生命周期的控制

  11. cookie的生命周期是累计的,从创建时,就开始计时,20分钟后,cookie生命周期结束,

  12. session的生命周期是间隔的,从创建时,开始计时如在20分钟,没有访问session,那么session生命周期被销毁

应用场景:

  • Cookie 只能存储 ASCII 码字符串,而 Session 则可以存储任何类型的数据,因此在考虑数据复杂性时首选 Session;

  • Cookie 存储在浏览器中,容易被恶意查看。如果非要将一些隐私数据存在 Cookie 中,可以将 Cookie 值进行加密,然后在服务器进行解密;

  • 对于大型网站,如果用户所有的信息都存储在 Session 中,那么开销是非常大的,因此不建议将所有的用户信息都存储到 Session 中。

HTTPS (重点)

Http 主要有以下安全性问题:(Http 和 Https 的区别?)

  • 窃听风险,数据传输时,使用明文进行通信,内容可能会被监听;
  • 篡改风险, 数据传输时,不验证通信方的身份,通信方的身份有可能遭遇伪装;
  • 冒充风险,数据传输后,无法证明报文的完整性,报文有可能遭篡改。

1. HTTPS与HTTP的区别

  • HTTP 是超文本传输协议,信息是明文传输,存在安全风险的问题。HTTPS 则解决 HTTP 不安全的缺陷,在 TCP 和 HTTP 网络层之间加入了 SSL/TLS 安全协议,使得报文能够加密传输。
  • HTTP 连接建立相对简单, TCP 三次握手之后便可进行 HTTP 的报文传输。而 HTTPS 在 TCP 三次握手之后,还需进行 SSL/TLS 的握手过程,才可进入加密报文传输。
  • 两者的默认端口不一样,HTTP 默认端口号是 80,HTTPS 默认端口号是 443。
  • HTTPS 协议需要向 CA(证书权威机构)申请数字证书,来保证服务器的身份是可信的

2. HTTPS的特点

  1. 特点
  2. 信息加密:采⽤对称加密+⾮对称加密的混合加密的⽅式,对传输的数据加密,实现信息的机密性,解决了窃听的⻛险。
  3. 校验机制:⽤摘要算法为数据⽣成独⼀⽆⼆的「指纹」校验码,指纹⽤来校验数据的完整性,解决了被篡改的⻛险。
  4. 身份证书:将服务端的公钥放⼊到CA数字证书中,解决了服务端被冒充的⻛险。
  5. 优点
  6. 在数据传输过程中,使⽤秘钥加密,安全性更⾼
  7. 可认证⽤户和服务器,确保数据发送到正确的⽤户和服务器
  8. 缺点
  9. 握⼿阶段延时较⾼:在会话前还需进⾏SSL握⼿
  10. 部署成本⾼:需要购买CA证书;需要加解密计算,占⽤CPU资源,需要服务器配置或数⽬⾼

3. HTTPS 如何解决 HTTP 的安全性问题?

  • 混合加密的方式实现信息的机密性,解决了窃听的风险。
  • 摘要算法的方式来实现完整性,它能够为数据生成独一无二的「指纹」,指纹用于校验数据的完整性,解决了篡改的风险。
  • 将服务器公钥放入到数字证书中,解决了冒充的风险

混合加密

通过混合加密的方式可以保证信息的机密性,解决了窃听的风险。

混合加密

HTTPS 采用的是对称加密非对称加密结合的「混合加密」方式:

  • 在通信建立前采用非对称加密的方式交换「会话秘钥」,后续就不再使用非对称加密。
  • 在通信过程中全部使用对称加密的「会话秘钥」的方式加密明文数据。

采用「混合加密」的方式的原因:

  • 对称加密只使用一个密钥,运算速度快,密钥必须保密,无法做到安全的密钥交换。
  • 非对称加密使用两个密钥:公钥和私钥,公钥可以任意分发而私钥保密,解决了密钥交换问题但速度慢。

对称加密

对称加密也称为私钥加密,使⽤相同的密钥来进⾏加密和解密。

  • 在加密过程中,明⽂数据通过应⽤特定的算法和密钥进⾏加密,⽣成密⽂数据。
  • 解密过程则是将密⽂数据应⽤同样的算法和密钥进⾏解密,恢复为明⽂数据。
  • 由于加密和解密都使⽤相同的密钥,因此对称加密算法的速度通常较快,但密钥的安全性很重要。如果密钥泄漏,攻击者可以轻易地解密数据。

⾮对称加密

  • ⾮对称加密也称为公钥加密,使⽤⼀对不同但相关的密钥:公钥和私钥。

  • 公钥⽤于加密数据,私钥⽤于解密数据。如果使⽤公钥加密数据,只有拥有相应私钥的⼈才能解密数据;如果使⽤私钥加密数据,可以使⽤相应公钥解密。

  • 除了加密和解密,⾮对称加密还⽤于【数字签名】,可以验证消息的来源和完整性。

摘要算法 + 数字签名

为了保证传输的内容不被修改,可以将传输的内容计算出⼀个【指纹】,对⽅收到后,也把接收的内容计算出⼀个 【指纹】,然后进⾏对⽐,如果【指纹】相同,则说明内容没有被篡改,常常会使⽤摘要算法(哈希函数)来计算出内容的哈希值,通过摘要算法可以⽣成数据的唯⼀标识,从⽽验证数据的完整性。

但是摘要算法只能保证内容不被修改,不能保证发送者的身份,为了避免这种情况,计算机⾥会⽤⾮对称加密算法 来解决,共有两个密钥:公钥⽤于验证签名,私钥⽤于⽣成签名。私钥是由服务端保管,然后服务端会向客户端颁 发对应的公钥。如果客户端收到的信息,能被公钥解密,就说明该消息是由服务器发送的。

1.⽣成数字签名:

  • 发送者使⽤私钥对消息的摘要(通常是通过哈希函数计算得到)进⾏加密,⽣成数字签名。
  • 数字签名是消息的哈希值经过私钥加密的结果。

2. 发送消息和数字签名:

  • 发送者将原始消息和⽣成的数字签名⼀起发送给接收者。

3. 验证数字签名:

  • 接收者收到消息和数字签名后,使⽤发送者的公钥对数字签名进⾏解密,得到摘要值。
  • 接收者再次计算收到的消息的摘要(使⽤相同的哈希函数),将其与解密得到的摘要值进⾏⽐较。
  • 如果两个摘要值相同,说明消息未被篡改过,数字签名有效,消息来源可信。

数字证书

根据上⾯的介绍,我们已经知道可以通过摘要算法保证内容不被修改,以及通过私钥和公钥保证消息来源的可靠性,但是却缺少身份验证的环节,如果此时在客户端和服务器之间有⼀个中间⼈,中间⼈把原本双⽅通信的公钥篡 改成⾃⼰的公钥,这⼜该怎么办呢?这就需要使⽤到数字证书。

具体流程:

  1. 密钥⽣成:
  2. ⾸先,实体(例如服务器、个⼈或组织)需要⽣成⼀对密钥:公钥和私钥。
  3. 公钥是⽤于加密和验证的,可以被公开分享。
  4. 私钥⽤于解密和签名,必须保密,只有持有者知道。
  5. 证书请求(CSR - Certificate Signing Request):
  6. 实体⽣成⼀个证书请求,其中包含公钥、实体信息(如名称、电⼦邮件等)和签名。
  7. CSR是⼀个包含有关实体信息的⽂本块,可以被发送到 数字证书颁发机构(CA) 以获取数字证书。
  8. 证书颁发:
  9. 实体将证书请求发送给数字证书颁发机构(CA)。
  10. CA会验证请求者的身份,然后使⽤⾃⼰的私钥对请求中的信息进⾏签名,⽣成数字证书。
  11. 数字证书包括公钥、实体信息、CA的信息和签名等内容。
  12. 证书验证:
  13. 当实体收到数字证书时,它可以使⽤CA的公钥验证证书的签名,确保证书未被篡改且由合法的CA签发。
  14. 接收者可以检查证书中的实体信息以及CA的信息,确保证书的合法性。
  15. 数字证书使⽤:
  16. 接收者可以使⽤数字证书中的公钥来加密数据,然后发送给证书的持有者。
  17. 持有者使⽤私钥解密数据,保护数据的机密性。
  18. 持有者还可以使⽤私钥⽣成数字签名,接收者使⽤公钥验证签名,验证数据的来源和完整性

通过数字证书的方式保证服务器公钥的身份,解决冒充的风险。

证书信任链

证书信任链(Certificate Trust Chain)是数字证书的验证过程中的⼀部分,确保数字证书是由可信的证书颁发机 构(CA)签发的,并最终连接到根证书,形成⼀条链条。

  1. 根证书:信任链的顶端是根证书(Root Certificate),也称为根CA证书。根证书是数字证书体系中的根基, 它由操作系统或浏览器内置,作为信任的起点。
  2. 中间证书颁发机构(Intermediate CA):在信任链中,根证书下⽅可能有⼀个或多个中间CA,也称为中间证书颁发机构。这些中间CA的数字证书由根CA签发,它们作为信任链中的中继,⽤于签发其他证书。
  3. 服务器证书:在最底层是服务器的数字证书,它是由中间CA签发的,包含了服务器的公钥和相关信息。服务 器证书需要在客户端验证的过程中被验证。

证书验证过程中,客户端会依次验证服务器证书、中间CA的证书、根CA的证书。如果整个链条中的所有证书都可 以被成功验证,那么数字证书被视为合法和可信的。相反,如果任何⼀个证书⽆法验证,整个信任链就会被打断, 数字证书将被视为不合法或不可信的。

4. HTTPS是怎么建⽴连接的

SSL/TLS 协议基本流程: - 客户端向服务器索要并验证服务器的公钥。 - 双⽅协商⽣产「会话秘钥」。 - 双⽅采⽤「会话秘钥」进⾏加密通信。

具体流程如下: 1. ⾸先,客户端向服务器端发送加密通信请求,也就是 ClientHello 请求,请求与服务端建⽴连接。 2. 服务端产⽣⼀对公私钥,然后将⾃⼰的公钥发送给CA机构,CA机构也有⼀对公私钥,然后CA机构使⽤⾃⼰的 私钥将服务端发送过来的公钥进⾏加密,产⽣⼀个CA数字证书。 3. 服务端响应客户端的请求,也就是 SeverHello , 将CA机构⽣成的数字证书发送给客户端。 4. 客户端将服务端发送过来的数字证书进⾏解析(因为浏览器产商跟CA机构有合作,所以浏览器中已经保存了⼤ 部分CA机构的密钥,⽤于对服务端发送过来的数字证书进⾏解密),验证这个CA数字证书是否合法,如果不合法,会发送⼀个警告。如果合法,从数字证书中取出服务端⽣成的公钥。 5. 客户端取出公钥并⽣成⼀个随机码key(其实就是对称加密中的密钥) 6. 客户端将加密后的随机码key发送给服务端,作为接下来的对称加密的密钥 7. 服务端接收到随机码key后,使⽤⾃⼰的私钥对它进⾏解密,然后获得到随机码key。 8. 服务端使⽤随机码key对传输的数据进⾏加密,在传输加密后的内容给客户端 9. 客户端使⽤⾃⼰⽣成的随机码key解密服务端发送过来的数据,之后,客户端和服务端通过对称加密传输数 据,随机码Key作为传输的密钥。

5. HTTPS 的应用数据是如何保证完整性的?

传输层

2.TCP和UDP(重点)

网络层只把分组发送到目的主机,但是真正通信的并不是主机而是主机中的进程。运输层提供了进程间的逻辑通信,运输层向高层用户屏蔽了下面网络层的核心细节,使应用程序看起来像是在两个运输层实体之间有一条端到端的逻辑通信信道。

(1)UDP 和 TCP 的特点

  • 用户数据报协议 UDP(User Datagram Protocol)是无连接的,尽最大可能交付,没有拥塞控制,面向报文(对于应用程序传下来的报文不合并也不拆分,只是添加 UDP 首部),支持一对一、一对多、多对一和多对多的交互通信。例如:视频传输、实时通信
  • 传输控制协议 TCP(Transmission Control Protocol)是面向连接的,提供可靠交付,有流量控制,拥塞控制,提供全双工通信,面向字节流(把应用层传下来的报文看成字节流,把字节流组织成大小不等的数据块),每一条 TCP 连接只能是点对点的(一对一)。

(2)UDP 首部格式

首部字段只有 8 个字节,包括源端口、目的端口、长度、检验和。

(3)TCP 首部格式

  • 序号 seq :用于对字节流进行编号,例如序号为 301,表示第一个字节的编号为 301,如果携带的数据长度为 100 字节,那么下一个报文段的序号应为 401。[301,400]为序号301的数据长度,下一个则为401
  • 确认号 ack :期望收到的下一个报文段的序号。例如 B 正确收到 A 发送来的一个报文段,序号为 501,携带的数据长度为 200 字节,因此 B 期望下一个报文段的序号为 701,B 发送给 A 的确认报文段中确认号就为 701。
  • 数据偏移 :指的是数据部分距离报文段起始处的偏移量,实际上指的是首部的长度。
  • 确认 ACK :当 ACK=1 时确认号字段有效,否则无效。TCP 规定,在连接建立后所有传送的报文段都必须把 ACK 置 1。
  • 同步 SYN :在连接建立时用来同步序号。当 SYN=1,ACK=0 时表示这是一个连接请求报文段。若对方同意建立连接,则响应报文中 SYN=1,ACK=1。
  • 终止 FIN :用来释放一个连接,当 FIN=1 时,表示此报文段的发送方的数据已发送完毕,并要求释放连接。
  • 窗口 :窗口值作为接收方让发送方设置其发送窗口的依据。之所以要有这个限制,是因为接收方的数据缓存空间是有限的。

2.1 TCP三次握手

假设 A 为客户端,B 为服务器端。

  • 首先 B 处于 LISTEN(监听)状态,等待客户的连接请求。
  • A 向 B 发送连接请求报文段,SYN=1,ACK=0,并生成一个随机数x作为连接初始的序号, seq = x。
  • B 收到连接请求报文段,如果同意建立连接,会为该 TCP 分配变量和缓存,并向 A 发送连接确认报文段,SYN=1,ACK=1,确认号为 x+1,同时也生成一个随机的初始序列号 y 作为服务器的初始序列号, seq = y。
  • A 收到 B 的连接确认报文段后,也会为 TCP 分配缓存和变量。并且.还要向 B 发出确认,确认号为 ack = y+1,序号为 seq = x+1。(此时会把SYN置为0,在以后的每个传输的报文段都是 SYN0, 完成三次握手)
  • A 的 TCP 通知上层应用进程,连接已经建立。
  • B 收到 A 的确认后,连接建立。
  • B 的 TCP 收到主机 A 的确认后,也通知其上层应用进程:TCP 连接已经建立。

三次握手对应的socket编程函数

1.客户端调用connect()函数,此时客户端会向服务端发送SYN

2.服务端收到SYN后,会从listen()函数返回SYN+ACK

3.客户端收到connect()函数的返回,之后向服务端发送最后一个ACK

4.服务端收到最后一个ACK以后,将该连接请求从未完成连接队列放入已完成连接队列中,等待accept()从该队列中取出

(1)为什么TCP连接需要三次握手,两次不可以吗,为什么

  1. 第三次握手是为了防止已过期的连接请求到达服务器,让服务器再次打开连接。

客户端发送的连接请求如果在网络中滞留,超时以后,就会重新请求连接。但是这个滞留的连接 请求最后还是会到达服务器,如果不进行三次握手,那么服务器就会打开两个连接。如果有第三 次握手,客户端会忽略服务器发送的对滞留连接请求的连接确认,因此就不会再次打开连接。

  1. 避免发生死锁

假定客户端给服务端发送一个连接请 求报文,服务端收到了这个报文并发送了确认应答报文。按照两次握手的协定,服务端认为连接 已经成功地建立了,可以开始发送数据报文。可是,客户端在服务端的应答报文在传输中被丢失的情况下,不知道服务端建立什么样的序列号,客户端甚至怀疑服务端是否收到自己的连接请求 报文。在这种情况下,客户端认为连接还未建立成功,将忽略服务端发来的任何数据报文(因为 报文的确认号不连续了),只等待连接确认应答报文。而服务端在发出的数据报文超时后,重复 发送同样的报文。这样就形成了死锁。

(2) 第三次握手失败了怎么办?服务端和客户端会做些什么?

Server 端

​ 第三次的 ACK 在网络中丢失,那么 Server 端该 TCP 连接的状态为 SYN_RECV, 并且会根据 TCP 的超时重传机制,会等待 3 秒、6 秒、12 秒后重新发送 SYN+ACK 包,以便 Client 重新发送 ACK 包。

​ 而 Server 重发 SYN+ACK 包的次数,可以通过设置/proc/sys/net/ipv4/tcp_synack_retries 修改,默 认值为 5。 如果重发指定次数之后,仍然未收到 client 的 ACK 应答,那么一段时间后,Server 自动关闭这 个连接。

Client 端

​ client 在接收到 SYN+ACK 包,它的 TCP 连接状态就为 established (已连接),表示该连接 已经建立。那么如果 第三次握手中的 ACK 包丢失的情况下,Client 向 server 端发送数据(有可能 是攻击者的下一次三次握手的第一次握手报文),Server 端将以 RST 包响应,进入 CLOSED 状态, 关闭连接。

补充:

服务端直接发送 RST 报文段而不是重传报文,进入 CLOSED 状态,关闭连接。这样做的目的是为 了防止 SYN 泛洪攻击。

泛洪攻击:SYN 攻击利用的是 TCP 的三次握手机制,攻击端利用伪造的 IP 地址向服务端发出请求, 而服务端发出的确认报文将永远发送不到目的地,那么服务端在等待关闭这个连接的过程中消耗了 服务端的带宽和内存,如果有成千上万的这种连接,服务器资源将被耗尽,从而达到攻击的目的。

2.2 TCP 四次挥手

  1. 客户端向服务端发起FIN=1,序号seq=u; 此时客户端进入 FIN-WAIT-1(终止等待 1)状态。(TCP 规定,FIN 报 文段即使不携带数据,也要消耗一个序号)

  2. 服务器 B收到连接释放报文,立即发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号 seq=v, 此时,服务端就进入了 CLOSE-WAIT(关闭等待)状态。

TCP 服务器通知高层的应用进程,因而客户端 A 到服务器 B 这个方向的连接就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT 状态持续的时间。客户端A 收到服务器 的确认请求后,此时,客户端 就进入 FIN-WAIT-2(终止等待 2)状态,等待服务器发送连接释放报文。

  1. 服务器 B 将最后的数据发送完毕后,就向客户端 A 发送请求释放连接报文,终止控制位 FIN=1, ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为 seq=w,此时, 服务器就进入了 LAST-ACK(最后确认)状态,等待客户端的确认。

  2. 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,自己的序列号是 seq=u+1,此时,客户端就进入了 TIME-WAIT(时间等待)状态。

此时TCP连接还没有释放, 要等待两个Maximum Segment Lifetime(一般是2个30s,1min或2min)(最长报文时间后),才进入CLOSED状态。

服务器只要收到了客户端发 出的确认,立即进入 CLOSED 状态。

(1)为什么连接的时候是三次握手,关闭的时候却是四次挥手?(四次挥手的原因)

​ 因为四次挥手多了 CLOSE_WAIT 状态,要等待服务端向客户端传输完数据,不能马上发送请求关闭连接的报文到客户端,而在三次握手则服务端在接收到了客户端的请求连接报文以后,服务端可以马上发送请求连接的报文,所以这就多了这一次握手。

(2)为什么 TIME_WAIT 状态需要经过 2MSL(报文段最大生存时间)才能进入到 CLOSE 状态?

MSL 是 Maximum Segment Lifetime 英文的缩写,中文可以译为 “报文最大生存时间”,他是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。2MSL = 2*2mins = 4mins

  • 确保最后一个确认报文段能够到达。如果 B 没收到 A 发送来的确认报文段,那么就会重新发送连接释放请求报文段,A 等待一段时间就是为了处理这种情况的发生。
  • 等待一段时间是为了让本连接持续时间内所产生的所有报文段都从网络中消失,使得下一个新的连接不会出现旧的连接请求报文段。

(3)TCP如何保证可靠传输

  1. 应用数据被分割成 TCP 认为最适合发送的数据块。
  2. TCP 给发送的每一个包进行编号,接收方对数据包进行排序,把有序数据传送给应用层。
  3. 校验和: TCP 将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到段的检验和有差错,TCP 将丢弃这个报文段和不确认收到此报文段。
  4. TCP 的接收端会丢弃重复的数据。
  5. 流量控制: TCP 连接的每一方都有固定大小的缓冲空间,TCP 的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP 使用的流量控制协议是可变大小的滑动窗口协议。 (TCP 利用滑动窗口实现流量控制)
  6. 拥塞控制: 当网络拥塞时,减少数据的发送。
  7. ARQ 协议: 也是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认。在收到确认后再发下一个分组。
  8. 超时重传: 当 TCP 发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。

简单地说,就是通过报文确认,滑动窗口,超时重传,拥塞控制机制实现可靠传输

(4)TCP 滑动窗口

​ 窗口是缓存的一部分,用来暂时存放字节流。发送方和接收方各有一个窗口,接收方通过 TCP 确认报文段中的窗口字段告诉发送方自己的窗口大小,发送方根据这个值和其它信息设置自己的窗 口大小,从而控制发送速率。

​ 发送窗口内的字节都允许被发送,接收窗口内的字节都允许被接收。如果发送窗口左部的字节已经 发送并且收到了确认,那么就将发送窗口向右滑动一定距离,直到左部第一个字节不是已发送并且已确认的状态;接收窗口的滑动类似,接收窗口左部字节已经发送确认并交付主机,就向右滑动接 收窗口。

​ 接收方只会对窗口内最后一个按序到达的字节进行确认。

(5)TCP流量控制

​ TCP 链接的每一侧主机都为该 TCP 链接设置了接收缓存。当 TCP 链接收到正确、按续的字节 后,它就将数据放入接收缓存。相关联的应用进程就会从该缓存中读取数据,不是数据一道道就立 马读取数据。当发送送方发的太快了,太多了,很容易使接收方缓存溢出。这个时候就需要流量控 制来解决。他是一个速度匹配的服务。

​ 流量控制是点对点的,只关乎一个接收方和发送发。拥塞控 制是整个 IP 网络拥堵。他们两个完成控制的方法,都是维护一个滑动窗口,接收窗口给发送一个信号来表示接收方还有多少缓存可以用来接收数据。

​ 流量控制是为了控制发送方发送速率,保证接收方来得及接收。 接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。 将窗口字段设置为 0,则发送方不能发送数据。

​ 接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。 将窗口字段设置为 0,则发送方不能发送数据。

(6)TCP拥塞控制

​ 如果网络出现拥塞,分组将会丢失,此时发送方会继续重传,从而导致网络拥塞程度更高。因此当 出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。流量控制是为了 让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。

​ TCP 主要通过四个算法来进行拥塞控制:慢开始、拥塞避免、快重传、快恢复。

​ 发送方需要维护一个叫做拥塞窗口(cwnd)的状态变量,注意拥塞窗口与发送方窗口的区别:拥塞 窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口。

  1. 数据是单方向传送,而另一个方向只传送确认
  2. 接收方总是有足够大的缓存空间,因而发送发发送窗口的大小由网络的拥塞程度来决定
  3. 以 TCP 报文段的个数为讨论问题的单位,而不是以字节为单位

慢开始与拥塞避免:

​ 发送的最初执行慢开始,令 cwnd = 1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加 倍,因此之后发送方能够发送的报文段数量为:2、4、8 ...

​ 注意到慢开始每个轮次都将 cwnd 加倍,这样会让 cwnd 增长速度非常快,从而使得发送方发送的 速度增长速度过快,网络拥塞的可能性也就更高。设置一个慢开始门限 ssthresh,当 cwnd >= ssthresh 时,进入拥塞避免,每个轮次只将 cwnd 加 1

​ 如果出现了超时(即发送方没有接收到接收方传过来的确认报文),则令 ssthresh = cwnd / 2,然后重新执行慢开始。

说明:拥塞避免并非指完全能够避免拥塞,而是指在拥塞避免阶段将拥塞窗口控制为按线性规律增 长,使网络比较不容易出现拥塞。

快重传与快恢复

​ 所谓快重传,就是使发送方今早知道发生了个别报文段丢失,尽快重传,而不是等待重传计时器超时再重传。

​ 在接收方,要求每次接收到报文段都应该对最后一个已收到的有序报文段进行确认。例如已经接收到 M1 和 M2,此时收到 M4,应当发送对 M2 的确 认。

​ 在发送方,如果收到三个重复确认,那么可以知道下一个报文段丢失,此时执行快重传,立即重传 下一个报文段。例如收到三个 M2,则 M3 丢失,立即重传 M3。

​ 在这种情况下,只是丢失个别报文段,而不是网络拥塞。因此执行快恢复,令 cwnd =ssthresh = cwnd / 2 ,注意到此时直接进入拥塞避免

​ 慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1, 而快恢复 cwnd 设定为 ssthresh。

(7)如何区分流量控制和拥塞控制

  • 拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。
  • 拥塞控制是一个全局性的过程,涉及到所有的主机、所有的路由器,以及与降低网络传输性能有关的所有因素。
  • 流量控制往往指在给定的发送端和接收端之间的点对点通信量的控制。
  • 流量控制所要做的就是抑制发送端发送数据的速率,以便使接收端来得及接收。
  • 流量控制属于通信双方协商;拥塞控制涉及通信链路全局。
  • 流量控制需要通信双方各维护一个发送窗、一个接收窗,对任意一方,接收窗大小由自身决定,发送窗大小由接收方响应的 TCP 报文段中窗口值确定;拥塞控制的拥塞窗口大小变化由试探性发送一定数据量数据探查网络状况后而自适应调整。
  • 实际最终发送窗口 = min{流控发送窗口,拥塞窗口}。

(8) 可以解释一下RTO,RTT和超时重传分别是什么吗?

  • 超时重传:发送端发送报文后若长时间未收到确认的报文则需要重发该报文。可能有以下几种情况:
  • 发送的数据没能到达接收端,所以对方没有响应。
  • 接收端接收到数据,但是ACK报文在返回过程中丢失。
  • 接收端拒绝或丢弃数据。
  • RTO:从上一次发送数据,因为长期没有收到ACK响应,到下一次重发之间的时间。就是重传间隔。
  • 通常每次重传RTO是前一次重传间隔的两倍,计量单位通常是RTT。例:1RTT,2RTT,4RTT,8RTT......
  • 重传次数到达上限之后停止重传。
  • RTT:数据从发送到接收到对方响应之间的时间间隔,即数据报在网络中一个往返用时。大小不稳定。

(9) 服务器出现大量close_wait的连接的原因是什么?有什么解决方法?

close_wait状态是在TCP四次挥手的时候收到FIN但是没有发送自己的FIN时出现的,服务器出现大量close_wait状态的原因有两种:

  • 服务器内部业务处理占用了过多时间,都没能处理完业务;或者还有数据需要发送;或者服务器的业务逻辑有问题,没有执行close()方法
  • 服务器的父进程派生出子进程,子进程继承了socket,收到FIN的时候子进程处理但父进程没有处理该信号,导致socket的引用不为0无法回收

处理方法:

  • 停止应用程序
  • 修改程序里的bug

2.3 UDP

(1)UDP 不可靠为什么还要用它?UDP 怎么实现可靠传输?

  • 确认机制: UDP 要想可靠,就要接收方收到 UDP 之后回复个确认包
  • 超时重传 发送方有个机制,收不到确认包就要重新发送,每个包有递增的序号,接收方发现中间丢了包就要 发重传请求。
  • 滑动窗口: 当网络太差时候频繁丢包,防止越丢包越重传的恶性循环,要有个发送窗口的限制,发送窗口的大 小根据网络传输情况调整,调整算法要有一定自适应性。

(2)TCP和UDP的区别?

TCP 是面向连接的运输层协议,UDP 无连接。

TCP 提供可靠交付的服务,UDP 使用尽最大努力交付,即不保证可靠交付,同时也不使用拥塞控制。

TCP 提供全双工通信,UDP 支持一对一、一对多、多对一和多对多的交互通信。

TCP 是面向字节流,UDP 是面向用户数据报的。

TCP 传输慢,UDP 传输快。

TCP 所需资源多(比如:超时重传功能需要先缓存已发送的数据),UDP 所需资源少。

TCP 首部字节多(因为有 ACK,SYN,FIN 等字段以及滑动窗口等),UPD 首部字节少。

(3)粘包问题

​ TCP 传输的数据在发送缓冲区或接收缓冲区中堆积,因为 TCP 收发的灵活性,可能导致多条数 据被当做一条数据接收。简单来说,就是两条数据的黏连。

​ 本质原因:TCP 在传输层对数据的格式 并不关心,所以每条数据之间没有明显的边界区分,这样会造成粘包问题。

产生原因:

  • 由TCP连接复用造成的粘包问题。

  • 因为TCP默认会使用

Nagle算法

,此算法会导致粘包问题。

  • 只有上一个分组得到确认,才会发送下一个分组;
  • 收集多个小分组,在一个确认到来时一起发送。

  • 数据包过大造成的粘包问题。

  • 流量控制,拥塞控制也可能导致粘包。

  • 接收方不及时接收缓冲区的包,造成多个包接收

解决方案:

  1. Nagle算法问题导致的,需要结合应用场景适当关闭该算法

  2. 需要用户在应用层对数据进行边界管理:特殊字符间隔:对于定长数据,在 包与包之间采用明显的特殊字符间隔,HTTP 就采用这样的方法。

  3. 定长数据:每次按照数据长度大小读即可。
  4. 对于不定长数据,可以在应用层协议头中显式声明数据长度,这是最佳的方案,UDP 协 议就采用这种方案。

(4)如果已经建立了连接,但是客户端突然出现故障了怎么办?(了解)

​ TCP 设有一个保活计时器,服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通 常是设置为 2 小时,若客户端如果出现故障,两小时还没有收到客户端的任何数据,服务器就会 发送一个探测报文段,以后每隔 75 秒钟发送一次。若一连发送 10 个探测报文仍然没反应,服务 器就认为客户端出了故障,接着就关闭连接。

(5)4.2 TIME_WAIT 为什么是 2MSL,如何解决 TIME_WAIT 状态过多?(了解)

​ 因为第四次挥手,客户端发送了确认报文以后,服务端可以能没收到,在这个时间内,服务端超时 还可以再次发送第三次挥手,这样一去一回的最大时间就是 2MSL。1MSL 是报文在网络中的最大 存活时间。

TIME_WAIT 来源:

在高并发短连接的 TCP 服务器上,当服务器处理完请求后立刻主动正常关闭连接。这个场景下会出 现大量 socket 处于 TIME_WAIT 状态。如果客户端的并发量持续很高,此时部分客户端就会显示连 接不上,因为服务端端口已被用完。

解决方法:

  1. 将 time_wait 的时间缩短快速回收。
  2. 调整短连接为长链接长连接比短连接从根本上减少关闭连接的次数,减少了 TIME_WAIT 状态的产生数量

(5)TCP对应的应用层协议

FTP:定义了文件传输协议,使用21端口.

Telnet:它是一种用于远程登陆的端口,23端口

SMTP:定义了简单邮件传送协议,服务器开放的是25号端口。

POP3:它是和SMTP对应,POP3用于接收邮件。

(6)UDP对应的应用层协议

DNS:用于域名解析服务,用的是53号端口

SNMP:简单网络管理协议,使用161号端口

TFTP(Trival File Transfer Protocal):简单文件传输协议,69

网络层

3. IP协议(重点)

(1)IPV4 和 IPV6 协议的区别

  1. 地址长度:v4 具有 32 位地址长度;v6 具有 128 位地址长度
  2. 地址的表示方法:v4 是以小数表示的十进制数。V6 是以十六进制表示的二进制数。
  3. 数据包的区别:
  4. v4 数据包需要 576 个字节 ,v6 数据包需要 1280 个字节不会有碎片、
  5. v4 协议的包头长度为 20 个字节,v6 协议的包头的长度为 40 个字节。
  6. v4 协议的数据包碎片会由转发路由器和发送主机完成。v6 协议的数据包碎片仅由 发送主机完成。

(2)IP数据包切片

​ 一个链路层数据报能承载的最大的数据量称为最大传送单元 (MTU)。

​ IP 数据报被封装在链路层数据报中,因此链路层的 MTU 严格的限制着 IP 数据报 的长度,而且在 IP 数据报的从源地址到目的地址的路径上的各段可能使用不同的链路层协议,所以 MTU 可能不一样。

​ 当 IP 数据报的长度超过了 MTU 的时候,就需要进行 IP 数据报分片。这些片在目的地的网络层被重新组装。根据 ip 数据报中的偏移量的字段完成组装。

4. 网络地址转换(了解)

NAT 的基本工作原理:当私有网主机和公共网主机通信的 IP 包经过 NAT 网关时,将 IP 包中的源 IP 或目的 IP 在私有 IP 和 NAT 的公共 IP 之间进行转换。

应用:数据包伪装、平衡负载、端口转发和透明代理。

  • 数据包伪装: 可以将内网数据包中的地址信息更改成统一的对外地址信息,不让内网主机直接暴露在因特网上,保证内网主机的安全。同时,该功能也常用来实现共享上网。
  • 端口转发: 当内网主机对外提供服务时,由于使用的是内部私有 IP 地址,外网无法直接访问。 因此,需要在网关上进行端口转发,将特定服务的数据包转发给内网主机。
  • 负载平衡: 目的地址转换 NAT 可以重定向一些服务器的连接到其他随机选定的服务器。

5. 子网和子网掩码(了解)

​ 传统的 IP 地址分为两个部分,网络号和主机号。两级 IP 地址的缺点,有时候 IP 地址空间的利 用率很低(如何理解)、给每个物理网络分配一个网络号会使路由变得太大而使网络性能变坏;两 级 ip 地址不够灵活。

​ 所以出现了三级 ip 地址,子网划分。子网划分只是把 IP 地址的主机号这部分再进行划分,而不改变 IP 地址原来的网络号。因此,从一个地址本身或 IP 数据报的首部,无法判断源主机或者主机 所链接的网络是否进行了子网划分。

​ 因为子网掩码是告诉你,是从主机号的哪一个位置开始划分为子网 的。

​ 确定要借用几个比特来作为子网号。在一个网络中,子网数量是 2^n

问题:
1. 50 个主机需要的 n 是多少。

2.原来是 C 类地址,又多了 2 位的子网, 请确定子网的掩码。

答案: 子网掩码为: 24+2=26, 二进制表示: 11111111.11111111.11111111.11000000
十进制表示:255.255.255.192

6. 地址解析协议ARP

​ 无论网络层使用的是什么协议,在实际的链路上传输数据的时候,最终必须使用硬件地址。所以需要一种方法来完成 IP 地址到 MAC 地址的映射,这个就是 ARP 协议

​ 每个主机都设有一个 ARP 告诉缓存,用来存放本局域网上各主机和路由器的 IP 地址到 MAC 地址的映射表,称为 ARP 表。 ARP 工作在网络层。

ARP工作原理:

​ 主机A欲向本局域网上的某台主机B发送IP数据报 时,先在其ARP高速缓存中查看有无主机B的IP地址。如有,就可查出其对应的硬件地址,再 将此硬件地址写入MAC帧,然后通过局域网将该MAC帧发往此硬件地址。

​ 如果没有,那么就 通过使用目的MAC地址为FF-FF-FF-FF-FF-FF的帧来封装并广播ARP请求分组,使同一个局域 网里的所有主机收到ARP请求。主机B收到该ARP请求后,向主机A发出响应ARP分组,分组中包含主机B的IP与MAC地址的映射关系,主机A在收到后将此映射写入ARP缓存,然后 按查询到的硬件地址发送MAC帧。

​ ARP由于“看到了" IP地址,所以它工作在网络层,而NAT 路由器由于“看到了"端口,所以它工作在传输层。

注意:ARP用于解决同一个局域网上的主机或路由器的IP地址和硬件地址的映射问题.如 果所要找的主机和源主机不在同一个局域网上,那么就要通过ARP找到一个位于本局域网上的某个路由器的硬件地址,然后把分组发送给这个路由器,让这个路由器把分组转发给下一个网络. 剩下的工作就由下一个网络来做。

尽管ARP请求分组是广播发送的,但ARP响应分组是普通的 单播,即从一个源地址发送到一个目的地址。

ARP欺骗:

​ 在每台主机都有一个 ARP 缓存表,缓存表中记录了 IP 地址与 MAC 地址的对应关系,而局域网数据传输依靠的是 MAC 地址。

​ 假设主机 A 192.168.1.2, B 192.168.1.3, C 192.168.1.4; 网关 G 192.168.1.1; 在同一局域网,主机 A 和 B 通过网关 G 相互通信,就好比 A 和 B 两个人写信,由邮递员 G 送信,C 永远都不会知道 A 和 B 之间说了些什么话。

​ 但是并不是想象中的那么安全,在 ARP 缓存表机制存在一个缺陷,就是当 请求主机收到 ARP 应答包后,不会去验证自己是否向对方主机发送过 ARP 请求包,就直接把这个 返回包中的 IP 地址与 MAC 地址的对应关系保存进 ARP 缓存表中,如果原有相同 IP 对应关系,原有的则会被替换。

​ 这样 C 就有了偷听 A 和 B 的谈话的可能,继续思考上面的例子: C 假扮邮递员,首先要告诉 A 说:“我就是邮递员” (C 主机向 A 发送构造好的返回包,源 IP 为 G 192.168.1.1,源 MAC 为 C 自己的 MAC 地址),A 很轻易的相信了,直接把“C 是 邮递员”这个信息记在了脑子里;

​ C 再假扮 A,告诉邮递员:“我就是 A”(C 向网关 G 发送构造好的返回包,源 IP 为 A 192.168.1.2, 源 MAC 地址为自己的 MAC 地址),智商捉急的邮递员想都没想就相信了,以后就把 B 的来信送给了 C,C 当然就可以知道 A 和 B 之间聊了些什么。

补充:

RARP:

​ RARP是反向地址转换协议,网络层协议,RARP与ARP工作方式相反。

​ RARP使只知道自己硬件地址的主机能够知道其IP地址。RARP发出要反向解释的物理地址并希望返回其IP地址,应答包括能够提供所需信息的RARP服务器发出的IP地址。

原理:

  1. 网络上的每台设备都会有一个独一无二的硬件地址,通常是由设备厂商分配的MAC地址。主机从网卡上读取MAC地址,然后在网络上发送一个RARP请求的广播数据包,请求RARP服务器回复该主机的IP地址。
  2. RARP服务器收到了RARP请求数据包,为其分配IP地址,并将RARP回应发送给主机。
  3. PC1收到RARP回应后,就使用得到的IP地址进行通讯。

7. Socket编程(了解)

1、什么是 Socket

​ 在计算机通信领域,socket 被翻译为 “套接字”,它是计算机之间进行通信一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。   socket 起源于 Unix,而 Unix/Linux 基本哲学之一就是 “一切皆文件”,都可以用“打开 open –> 读写 write/read –> 关闭 close” 模式来操作。 ​ Socket 就是该模式的一个实现:即 socket 是一种特殊的文件,一些 socket 函数就是对其进行的操作(读 / 写 IO、打开、关闭)。   Socket() 函数返回一个整型的 Socket 描述符,随后的连接建立、数据传输等操作都是通过该 Socket 实现的。

既然 Socket 主要是用来解决网络通信的,那么我们就来理解网络中进程是如何通信的。

2、网络中进程如何通信

2.1、本地进程间通信

​ a、消息传递(管道、消息队列、FIFO)   b、同步(互斥量、条件变量、读写锁、文件和写记录锁、信号量)   c、共享内存(匿名的和具名的,eg:channel)   d、远程过程调用 (RPC)

2.2、网络中进程如何通信

我们要理解网络中进程如何通信,得解决两个问题:   a、我们要如何标识一台主机,即怎样确定我们将要通信的进程是在那一台主机上运行。   b、我们要如何标识唯一进程,本地通过 pid 标识,网络中应该怎样标识? 解决办法:   a、TCP/IP 协议族已经帮我们解决了这个问题,网络层的 “ip 地址” 可以唯一标识网络中的主机   b、传输层的 “协议 + 端口” 可以唯一标识主机中的应用程序(进程),因此,我们利用三元组(ip 地址,协议,端口)就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互

3、Socket如何通信

​ 网络中进程间即利用三元组【ip 地址,协议,端口】进行网络间通信,那我们应该怎么实现?因此,我们 socket 应运而生,它就是利用三元组解决网络通信的一个中间件工具。就目前而言,几乎所有的应用程序都是采用 socket,如 UNIX BSD 的套接字(socket)和 UNIX System V 的 TLI(已经被淘汰)。

​ Socket 通信的数据传输方式,常用的有两种:   a、SOCK_STREAM:表示面向连接的数据传输方式。数据可以准确无误地到达另一台计算机,如果损坏或丢失,可以重新发送,但效率相对较慢。常见的 http 协议就使用 SOCK_STREAM 传输数据,因为要确保数据的正确性,否则网页不能正常解析。   b、SOCK_DGRAM:表示无连接的数据传输方式。计算机只管传输数据,不作数据校验,如果数据在传输中损坏,或者没有到达另一台计算机,是没有办法补救的。也就是说,数据错了就错了,无法重传。因为 SOCK_DGRAM 所做的校验工作少,所以效率比 SOCK_STREAM 高。

4、编写一个网络应用程序,包含客户端和服务端,用TCP和UDP两种方式实现:

  • 客户端向服务端发送一个字符串

  • 服务端接收到该字符串后将其输出到命令行,然后向客户端返回字符串的长度

  • 客户端在命令行输出服务端返回的长度

TCP实现:

// 服务端
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {
    public static void main(String[] args) throws IOException {
        // 创建socket,并绑定到65000端口
        ServerSocket ss = new ServerSocket(65000);
        // 一直等待并处理客户端发送过来的请求
        while (true) {
            // 监听65000端口,直到客户端返回连接信息
            Socket socket = ss.accept();
            // 获取客户端的请求信息,执行业务逻辑
            new LengthCalculator(socket).start();
        }
    }
}

class LengthCalculator extends Thread {
    private Socket socket;

    public LengthCalculator(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            // 获取socket的输出流
            OutputStream out = socket.getOutputStream();
            // 获取socket的输入流
            InputStream in = socket.getInputStream();
            // 缓存输入内容
            byte[] buffer = new byte[1024];
            // 输入内容的长度
            int ch = in.read(buffer);

            // 将输入流的byte数组转成字符串,打印到控制台
            String content = new String(buffer, 0, ch);
            System.out.println(content);
            // 往输入流里写入获得的字符串的长度,发送给客户端
            out.write(String.valueOf(content.length()).getBytes());

            // 关闭输入输出流和socket
            in.close();
            out.close();
            socket.close();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
}
// 客户端
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class TCPClient {
    public static void main(String[] args) throws IOException {
        // 创建socket,连接到本机的65000端口
        Socket socket = new Socket("127.0.0.1", 65000);
        // 获取输入流
        InputStream in = socket.getInputStream();
        // 获取输出流
        OutputStream out = socket.getOutputStream();

        // 将要传递给服务端的字符串转换成byte数组,发送给服务端
        out.write(new String("Hello world!").getBytes());

        // 缓存输入内容
        byte[] buffer = new byte[1024];
        // 输入内容的长度
        int ch = in.read(buffer);

        // 从服务端发回的字符串的长度
        String content = new String(buffer, 0, ch);
        System.out.println(content);

        in.close();
        out.close();
        socket.close();
    }
}

UDP实现:

// 服务端
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UDPServer {
    public static void main(String[] args) throws Exception {
        // 监听端口号65001
        DatagramSocket socket = new DatagramSocket(65001);
        // 将客户端发送的内容封装进DatagramPacket对象中
        byte[] buffer = new byte[100];
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);

        // 接收客户端发送的内容
        socket.receive(packet);
        // 将获取到的数据转成字符串,打印到控制台
        byte[] data = packet.getData();
        String content = new String(data, 0, packet.getLength());
        System.out.println(content);

        // 从packet对象中获取数据的来源地址和端口号,给客户端发送数据
        byte[] sendContent = String.valueOf(content.length()).getBytes();
        DatagramPacket sendPacket = new DatagramPacket(sendContent,
                sendContent.length, packet.getAddress(), packet.getPort());
        socket.send(sendPacket);
    }
}
// 客户端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPClient {
    public static void main(String[] args) throws Exception{
        DatagramSocket socket = new DatagramSocket();
        // 封装数据报
        byte[] buffer = "Hello world!".getBytes();
        DatagramPacket packet = new DatagramPacket(buffer, buffer.length,
                InetAddress.getByName("127.0.0.1"), 65001);
        // 发送数据给服务端
        socket.send(packet);

        // 客户端接收从服务端发送过来的数据
        byte[] data = new byte[100];
        // 将接收到的数据存储到DatagramPacket对象中
        DatagramPacket receivedPacket = new DatagramPacket(data, data.length);
        socket.receive(receivedPacket);
        // 将接收到的数据打印到控制台
        String content = new String(receivedPacket.getData(), 0, receivedPacket.getLength());
        System.out.println(content);
    }
}

8. IO模型(了解)

一个读操作发生时,通常包括两个阶段:

  • 等待数据:等待数据准备好
  • 拷贝数据:从内核向进程复制数据

​ 对于一个套接字上的读操作,第一步通常涉及等待数据从网络中到达。当所等待数据到达时,它被 复制到内核中的某个缓冲区。第二步就是把数据从内核缓冲区复制到应用进程缓冲区。

​ 对这两个阶段的不同处理,Unix 可分为五种 I/O 模型:

  • 阻塞式 I/O
  • 非阻塞式 I/O
  • I/O 复用(select 和 poll)

  • 信号驱动式 I/O(SIGIO)

  • 异步 I/O(AIO)

1. 阻塞式 I/O

下图中,recvfrom() 用于接收 Socket 传来的数据,并复制到应用进程的缓冲区 buf 中。

​ 当用户进程调用了 recvfrom 函数(即进行读操作),内核就开始了 IO 的第一个阶段:准备数据。 对于 network io 来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的 UDP 包), 这个时候内核就要等待足够的数据到来。而在用户进程这边,整个进程会被阻塞。当内核一直等到 数据准备好了,它就会将数据从内核中拷贝到用户内存,然后内核返回结果,用户进程才解除阻塞的状态,重新运行起来。

​ 所以,阻塞 IO 的特点就是在 IO 执行的两个阶段(等待数据和拷贝数据两个阶段)都被阻塞了。

2. 非阻塞式 I/O

​ 从图中可以看出,当用户进程发出读操作(即调用 recvfrom 函数)时,如果内核中的数据还没有准 备好,那么它并不会阻塞用户进程,而是立刻返回一个 error。从用户进程角度讲 ,它发起一个 read 操作后,并不需要等待,而是马上就得到了一个结果。用户进程判断结果是一个 error 时,它就知道 数据还没有准备好,于是它可以再次发送 read 操作。一旦内核中的数据准备好了,并且又再次收到 了用户进程的 system call,那么它马上就将数据拷贝到了用户内存,然后返回。

所以,在非阻塞式 IO 中,用户进程其实是需要不断的主动询问内核数据准备好了没有。在拷贝数据阶段仍然会阻塞。

3. I/O 复用

​ 前面的阻塞 I/O 和非阻塞 I/O 中,每一个 Socket 连接都需要创建一个线程去处理。如果同时有几万 个连接,那么就需要创建相同数量的线程。I/O 复用不需要创建线程(也就没有了线程切换的开销), 系统开销更小。

​ IO multiplexing 这个词可能有点陌生,但是如果我说 select/epoll,大概就都能明白了。有些地方也 称这种 IO 方式为事件驱动 IO(event driven IO)。我们都知道,select/epoll 的好处就在于单个进程就可以同时处理多个网络连接的 IO。

​ 它的基本原理就是 select/epoll 这个 function 会不断的轮询所负责的所有 socket,当某个 socket 有数据到达了,就通知用户进程。它的流程如图:

​ 当用户进程调用了 select,那么整个进程会被阻塞,而同时,内核会“监视”所有 select 负责的 socket, 当任何一个 socket 中的数据准备好了,select 就会返回。这个时候用户进程再调用 recvfrom 函数(即 进行读操作),将数据从内核拷贝到用户进程,这个阶段也要阻塞。

​ 这个图和阻塞 IO 的图其实并没有太大的不同,事实上还更差一些。因为这里需要使用两个系统调用 (select 和 recvfrom),而阻塞 IO 只调用了一个系统调用(recvfrom)。但是,用 select 的优势在于它可 以同时处理多个 socket 连接。(select/epoll 的优势并不是对于单个连接能处理得更快,而是在于能处理更多的连接。)

4. 信号驱动 I/O

​ 应用进程使用 sigaction 系统调用,内核立即返回,应用进程可以继续执行,也就是说等待数据阶段应用进程是非阻塞的。内核在数据到达时向应用进程发送 SIGIO 信号,应用进程收到之后在信号处理程序中调用 recvfrom 将数据从内核复制到应用进程中。

​ 相比于非阻塞式 I/O 的轮询方式,信号驱动 I/O 的 CPU 利用率更高。

5. 异步 I/O

​ 用户进程调用了 aio_read 函数(读操作)以后,立刻就可以开始去做其它的事。而另一方面,从内核的角度,当它受到一个系统调用指令之后,首先它会立刻返回,所以不会对用户进程产生任何阻 塞。然后,内核会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,内核会给用户进程发送一个 signal,告诉它系统调用就完成了。

​ 异步 IO 是真正非阻塞的,它不会对请求进程产生任何的阻塞,因此对高并发的网络服务器实现 至关重要。

同步/非同步,阻塞/非阻塞

阻塞与非阻塞: 调用 阻塞 IO 会一直阻塞住对应的进程直到操作完成,而非阻塞 IO 在内核还在准备数据的情况下会立刻返回

同步和异步:

  • 同步 I/O:synchronous IO 做”IO operation”的时候会将 process 阻塞;

包括阻塞式 I/O、非阻塞式 I/O、I/O 复用和信号驱动 I/O ,它们的主要区别在第一个阶段。

  • 异步 I/O:进行io操作的时候应用进程不会阻塞。

12. 其他问题

1. 协议三要素

  1. 语义,语义是解释控制信息每个部分的意义。它规定了需要发出何种控制信息,以及完成的动作与做出什么样的响应。

  2. 语法,语法是用户数据与控制信息的结构与格式,以及数据出现的顺序。

  3. 时序,时序是对事件发生顺序的详细说明。

2. 层次路由的原理

将任意规模网络抽象为一个图计算路由过于理想化,标识所有路由器和“扁平”网络 在实际网络(尤其是大规模网络)中不可行,网络规模较大,路由表几乎无法存储,路由计算过程的信息(链路状态分组、DV)交换量巨大

解决办法:(层次路由的提出)

  • 管理自治,每个网络的管理可能都期望自主控制其网内的路由

  • 聚合路由器为一个区域, 该区域为一个自治系统AS(autonomous systems):

  • 同一AS内的路由器运行相同的路由协议,不同自治系统内的路由器可以运行不同的AS内部路由协议

  • 网关路由器(gateway router): 位于AS“边缘”,通过链路连接其他AS的网关路由器

3. 层次路由有哪些协议?(与自治系统相关的内部网关协议和外部网关协议)

内部网关协议:

  • RIRP

路由信息协议,属于最早的动态路由协议

优点:节约成本,对资源消耗较低,配置简单,对硬件要求低,占用CPU、内存低,所以在小型网络中还有使用到。

缺点:计算路由慢,链路变化了收敛慢,能够保存的路由表相对较小,最多只能支持15台设备的网络,只适用于小型网络

  • OSPF:

开放最短路径优先协议,企业网主要使用的协议

优点:技术成熟,碰到的问题基本上在资料上都能够查到,收敛快,由于cisco的力推,会使用的人多

缺点:收敛速度,安全性较ISIS差

  • ISIS协议:

中间系统到中间系统协议,传输网/运营商网络主要使用的协议

优点:算法与OSPF类似,收敛快,安全性高

缺点:异常处理资料不如OSPF丰富

边界网关协议: BGP

4. 单工、半双工和全双工通信

单工:

  • 数据只在一个方向上传输,不能实现双方通信。

半双工

  • 允许数据在两个方向上传输,但是同一时间数据只能在一个方向上传输,其实际上是切换的单工。

全双工

  • 允许数据在两个方向上同时传输。

5. 介绍香农公式

信道容量:指信道中信息无差错传输的最大速率。

信道模型中定义了两种广义信道,调制信道和编码信道。

  • 调制信道是一种连续信道,可以用连续信道的信道容量来表征;

  • 编码信道是一种离散信道,可以用离散信道的信道容量来表征。

香农公式,是香农(Shannon)提出并严格证明的“在被高斯白噪声干扰的信道中,计算最大信息传送速率C的公式。

香农公式表明,信道容量与信道带宽成正比,同时还取决于系统信噪比以及编码技术种类

6. 网络中的主机通信流程

(1)主机A和主机B在同一个二层网络,直接走二层交换

  1. 主机A查看ARP表,检查是否有主机B的IP到MAC的映射.如果有映射,构造报文,目的IP为主机B的IP,源IP为

主机A的IP,目的MAC为主机B的MAC,源MAC为主机A的MAC(这个过程是构造了一个网帧,交换机的数据包

传输是基于网帧的),将数据包传给交换机。

  1. 交换机进行MAC表学习,将主机A的MAC和报文端口号记录下来,然后交换机查看自己的MAC表,检查是否有

主机B的MAC到端口的映射,如果有,获取对应的端口,将数据包从端口转发出去,数据包到达主机B;如果

没有,主机A在局域网内进行广播找B的MAC地址,主机B收到后向A回复,交换机进行MAC表学习,将主机B

的MAC和端口号记录下来。

  1. 如果主机A没有主机B的arp映射,主机A需要发送ARP请求,以获取主机B的MAC,将报文发往交换机,交换机进行广播,主机B收到广播报文,在自己的ARP缓存表写入主机A的IP到MAC的映射,将自己的MAC封装到ARP回复报文中,主机A收到B的MAC,在自己的ARP缓存表写入主机B的IP到MAC的映射,构造报文给主机B,过程同上

(2)主机A和主机B不在同一个网络中,走三层路由

  1. 主机A查看自己的ARP缓存表,检查是否有路由器E的IP到MAC的映射,如果有映射,获取路由器E的MAC,构

造报文,目的IP为主机B的IP,源IP为主机A的IP,目的MAC为路由器E的MAC,源MAC为主机A的MAC,将报

文通过交换机C发往路由器E,过程同上。 (如果主机A没有路由器E的IP到MAC的映射,需要发送ARP请求,获取路由器E的MAC,过程同上。)

  1. 路由E收到报文,剥离报文的MAC头,查询路由表,路由器内部构造报文,目的IP为主机B的IP,源IP为主机A

的IP目的MAC为路由器E右端的MAC,源MAC为路由器左端的MAC(左端MAC和右端MAC是两个网

卡)

  1. 右端收到报文后,剥离报文的MAC头,查询路由表,路由器内部构造报文,目的IP为主机B的IP,源IP

为主机A的IP,右端查看自己的arp缓存表,如果有B的IP到MAC的映射关系,获取B的MAC,封报文MAC枕

头,目的MAC为主机B的MAC,源MAC为路由器E右端的MAC,将报文通过交换机D发往主机B。

​ 整个传输过程中,源ip与目的ip均没有变化,变化的是MAC地址

7. 停止等待协议

参考: https://blog.csdn.net/weixin_43584807/article/details/93800480

8. ARQ协议

参考: https://blog.csdn.net/weixin_43584807/article/details/93800480

9. URI和URL

URL(统一资源定位符)是Internet上资源的地址,可以定义为引用地址的字符串,用于指示资源的位置以及用于访问它的协议。

URI(统一资源标识符)是标识逻辑或物理资源的字符序列,也是一串字符。通过使用位置,名称或两者来标识Internet上的资源;它允许统一识别资源。

作用的区别:

  1. URI是一个相对来说更广泛的概念。URL是URI的一种,是URI命名机制的一个子集,能够说URI是抽象的,而详细要使用URL来定位资源。

  2. Web上的每一种资源如:图片、文档、视频等,都是由URI定位的。这里所谓的定位指的是web上的资源相对于主机server来说,存放在server上的详细路径。

  3. URL是internet上用来描写叙述信息资源文件的字符串。用在客户程序和server上,定位客户端连接server所须要的信息,它不仅定位了这个信息资源,并且定义了怎样找到这个资源。