集线器、网桥、交换机、路由器

  • 网线是物理层的硬件
  • 集线器(Hub)是物理层的硬件,连接所有的线路,广播所有信息
  • 网桥(Bridge)是数据链路层的硬件。网桥隔离两个端口,不同的端口形成单独的冲突域,减少网内冲突。网桥在不同或相同类型的 LAN 之间存储并转发数据帧,根据 MAC 头部来决定转发端口,显然是数据链路层的设备
  • 交换机(Switch)是数据链路层的硬件,相当于多端口的网桥。交换机内部存储 MAC 表,只会将数据帧发送到指定的目的地址
  • 路由器(Router)是网络层的硬件,根据 IP 地址进行寻址,不同子网间的数据传输隔离

TCP

  1. TCP头部20个字节,TCP的包是没有IP地址的,那是IP层上的事。但是有源端口和目标端口;UDP 协议头包含 4 个字段:源端口、目的端口、长度和校验码,其中每一个字段都占 2 字节,总长度 8 字节

三次握手

  1. 客户端请求建立连接,向服务端发送一个同步报文(SYN=1),同时选择一个随机数 seq = x 作为初始序列号
  2. 服务端收到连接请求报文后,如果同意建立连接,则向客户端发送同步确认报文(SYN=1,ACK=1),确认号为 ack = x + 1,同时选择一个随机数 seq = y 作为初始序列号
  3. 客户端收到服务端的确认后,向服务端发送一个确认报文(ACK=1),确认号为 ack = y + 1,序列号为 seq = x + 1

为什么是三次握手

  1. 通信的双方要互相通知对方自己的初始化的Sequence Number,这个号要作为以后的数据通信的序号,以保证应用层接收到的数据不会因为网络上的传输的问题而乱序
  2. 如果只有两次握手,那么服务端向客户端发送 SYN/ACK 报文后,就会认为连接建立。但是如果客户端没有收到报文,那么客户端是没有建立连接的。这就导致服务端会浪费资源。
  3. 第一次握手:服务端确认“服务端收、客户端发”报文功能正常; 第二次握手:客户端确认“客户端发、客户端收、服务端收、服务端发”报文功能正常,客户端认为连接已建立; 第三次握手:服务端确认“服务端发、客户端收”报文功能正常,此时双方均建立连接,可以正常通信;

SYN 攻击?

SYN 攻击属于 DOS 攻击的一种,它利用 TCP 协议缺陷,通过发送大量的半连接请求,耗费 CPU 和内存资源。

原理:

在三次握手过程中,服务器发送第二个包之后、收到客户端的第三个包之前的 TCP 连接称为半连接,此时服务器处于 SYN_RECV(等待客户端响应)状态。如果接收到客户端的第三个包,则 TCP 连接成功,如果未接受到,则会不断重发请求直至成功
SYN 攻击的攻击者在短时间内伪造大量不存在的 IP 地址,向服务器不断地发送第一个包,服务器回复第二个包,并等待客户的确认。由于源地址是不存在的,服务器需要不断的重发直至超时 这些伪造的第一个包将长时间占用未连接队列,影响了正常的 SYN,导致目标系统运行缓慢、网络堵塞甚至系统瘫痪。在Linux下,默认重试次数为5次,重试的间隔时间从1s开始每次都翻售,5次的重试时间间隔为1s, 2s, 4s, 8s, 16s,总共31s

检测:当在服务器上看到大量的半连接状态时,特别是源 IP 地址是随机的,基本上可以断定这是一次 SYN 攻击。

防范:主要有两大类,一类是通过***、路由器等过滤网关防护,另一类是通过加固 TCP/IP 协议栈防范,如增加最大半连接数,缩短超时时间。但 SYN 攻击不能完全被阻止,除非将 TCP 协议重新设计,否则只能尽可能的减轻 SYN 攻击的危害。

四次挥手

  • 第一次挥手:客户端向服务端发送连接释放报文(FIN=1,ACK=1),主动关闭连接,同时等待服务端的确认

    序列号 seq = u,即客户端上次发送的报文的最后一个字节的序号 + 1;

    确认号 ack = k, 即服务端上次发送的报文的最后一个字节的序号 + 1

  • 第二次挥手:服务端收到连接释放报文后,立即发出确认报文(ACK=1),序列号 seq = k,确认号 ack = u + 1

这时 TCP 连接处于半关闭状态,即客户端到服务端的连接已经释放了,但是服务端到客户端的连接还未释放。这表示客户端已经没有数据发送了,但是服务端可能还要给客户端发送数据,服务端处于【close_wait】状态。

  • 第三次挥手:服务端向客户端发送连接释放报文(FIN=1,ACK=1),主动关闭连接,同时等待客户端的确认

    序列号 seq = w,即服务端上次发送的报文的最后一个字节的序号 + 1。如果半关闭状态,服务端没有发送数据,那么 w == k

    确认号 ack = u + 1,与第二次挥手相同,因为这段时间客户端没有发送数据

  • 第四次挥手:客户端收到服务端的连接释放报文后,立即发出确认报文(ACK=1),序列号 seq = u + 1,确认号为 ack = w + 1

此时,客户端就进入了 TIME-WAIT 状态。注意此时客户端到 TCP 连接还没有释放,必须经过 2*MSL(最长报文段寿命)的时间后,才进入 CLOSED 状态。而服务端只要收到客户端发出的确认,就立即进入 CLOSED 状态。

因为 TCP 是全双工的,一方关闭连接后,另一方还可以继续发送数据。所以四次挥手,将断开连接分成两个独立的过程。

####为什么第四次挥手,客户端的 TIME-WAIT 状态必须等待 2MSL 的时间才能返回到 CLOSED 状态?

  1. 确保 ACK 报文能够到达服务端,从而使服务端正常关闭连接。
  2. 防止已失效的连接请求报文段出现在之后的连接中

TCP 要求在 2MSL 内不使用相同的序列号。客户端在发送完最后一个 ACK 报文段后,再经过时间 2MSL,就可以保证本连接持续的时间内产生的所有报文段都从网络中消失。这样就可以使下一个连接中不会出现这种旧的连接请求报文段。或者即使收到这些过时的报文,也可以不处理它。

TCP重传机制

ACK & Selective Acknowledgment (SACK) & Duplicate SACK – 重复收到数据的问题

TCP的RTT算法 - Round Trip Time,也就是一个数据包从发出去到回来的时间

Jacobson / Karels 算法

SRTT = SRTT + α (RTT – SRTT) —— 计算平滑RTT

DevRTT = (1-β)DevRTT + β(|RTT-SRTT|) ——计算平滑RTT和真实的差距(加权移动平均)

RTO= µ * SRTT + ∂ *DevRTT —— 神一样的公式

其中:在Linux下,α = 0.125,β = 0.25, μ = 1,∂ = 4

TCP滑动窗口

TCP头里有一个字段叫Window,又叫Advertised-Window,这个字段是接收端告诉发送端自己还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。

对于发送端来说:

LastByteAcked 指向被接收端 ACK 的最后一个位置
LastByteSent 指向已发送但还未收到 ACK 的最后一个位置
LastByteWritten 指向上层应用写入但还未发送的最后一个位置

对于接收端来说:

LastByteRead 指向 TCP 缓冲区中读到的位置
NextByteExpected 指向收到的连续包的最后一个位置
LastByteRcvd 指向收到的包的最后一个位置

零窗口

TCP 使用来 ZWP(Zero Window Probe,零窗口探针)技术。具体是在发送端引入一个计时器,每当收到一个零窗口的应答后就启动该计时器。每间隔一段时间就主动发送报文,由接收端来 ACK 窗口大小。若接收者持续返回零窗口(一般是 3 次),则有的 TCP 实现会发送 RST 断开连接。

Nagle 算法:如果接收端处理过慢,每次 window 只能接收几个字节,那么当发送端每次都发送这几个字节时,会有大量带宽浪费在 TCP 和 IP 的首部上。因此 Nagle 提出了 Nagle 算法。Nagle 算法的工作方式是「缓存/累积」要发送的小数据,直到 window >= MSS 时再一并发送,避免对小的 window 作出响应。

TCP的拥塞处理

1)慢启动,2)拥塞避免,3)拥塞发生,4)快速恢复

  • 慢热启动算法

连接建立时,初始化 cwnd = 1,表示可以传一个 MSS 大小的数据(最大报文段长度(MSS)是TCP协议的一个选项,用于在TCP连接建立时,收发双方协商通信时每一个报文段所能承载的最大数据长度(不包括文段头)。)
每收到一个 ACK 包,cwnd++
每经过一个 RTT,cwnd 会翻倍(指数增长)
当 cwnd >= ssthresh (slow start threshold) 时,进入拥塞避免阶段。

  • 拥塞避免

每收到一个 ACK 包,cwnd = cwnd + 1/cwnd
每经过一个 RTT,cwnd = cwnd + 1(加性增)

  • 超时重传

如果发送端超时还未收到 ACK 包,就可以认为网络出现了拥塞,需要解决拥塞:
把 sshthresh 设为原来的一半(乘性减)
cwnd 重置为 1,重新开始慢启动过程

  • 快速重传 & 快速恢复

快速重传:接收端收到乱序包时,会发送 duplicate ACK 通知发送端。当发送端收到 3 个 duplicate ACK 时,就立刻开始重传,而不必继续等待到计时器超时。
快速重传会配合快速恢复算法:
把 sshthresh 设为原来的一半(乘性减)
cwnd 重置为 sshthresh,重新开始拥塞避免过程

为什么快速重传不需要像超时重传那样,将 cwnd 重置为 1 重新开始慢启动呢?因为它认为如果网络出现拥塞的话,是不会收到好几个重复的 ACK 的,所以现在网络可能没有出现拥塞。

TCP和UDP对比

TCP UDP
连接性 面向连接 无连接
可靠性 可靠 不可靠
传输方式 面向字节流 面向报文(保留报文的边界)
传输速度
双工性 全双工 一对一、一对多、多对一、多对多
流量控制 / 拥塞控制
应用场景 对效率要求相对低,但是对准确性要求高的场景;或是要求有连接的场景。如文件传输、发送邮件等 对效率要求相对高,对准确性要求相对低的场景。如即时通信、直播等
应用层协议 SMTP(电子邮件)、TELNET(远程登录控制)、HTTP、FTP DNS、TFTP(文件传输)、DHCP(动态主机配置)...

HTTP

HTTP 请求方法

  • GET:获取服务器的指定资源
  • HEAD:与 GET 方法一样,都是发出一个获取服务器指定资源的请求,但服务器只会返回 Header 而不会返回 Body。用于确认 URI 的有效性及资源更新的日期时间等。一个典型应用是下载文件时,先通过 HEAD 方法获取 Header,从中读取文件大小 Content-Length;然后再配合 Range 字段,分片下载服务器资源
  • POST:提交资源到服务器 / 在服务器新建资源
  • PUT:替换整个目标资源
  • PATCH:替换目标资源的部分内容
  • DELETE:删除指定的资源

幂等的:一个 HTTP 方法是幂等的,指的是同样的请求执行一次与执行多次的效果是一样的。换句话说就是,幂等方法不应该具有副作用。
常见的幂等方法:GET,HEAD,PUT,DELETE,OPTIONS
常见的非幂等方法:POST

安全的:一个 HTTP 方法是安全的,指的是这是一个对服务器只读操作的方法,不会修改服务器数据。
常见的安全方法:GET,HEAD,OPTIONS
常见的不安全方法:PUT,DELETE,POST

GET POST
应用 获取服务器的指定数据 添加 / 修改服务器的数据
历史记录 / 书签 可保留在浏览器历史记录中,或者收藏为书签 不可以
Cacheable 会被浏览器缓存 不会缓存
幂等 幂等,不会改变服务器上的资源 非幂等,会对服务器资源进行改变
后退 / 刷新 后退或刷新时,GET 是无害的 后退或刷新时,POST 会重新提交表单
参数位置 query 中(直接明文暴露在链接中) query 或 body 中
参数长度 2KB(2048 个字符) 无限制

HTTP 状态码

  • 信息响应(100–199)
    • 100 Continue:表明到目前为止都很正常,客户端可以继续发送请求或者忽略这个响应
  • 成功响应(200–299)
    • 200 OK
    • 201 Created:该请求已成功,并因此创建了一个新的资源。这通常是在 POST 请求之后返回的响应
    • 204 No Content:该请求已成功处理,但是返回的响应报文不包含实体的主体部分。通常用于只需要从客户端往服务器发送信息,而不需要返回数据时
    • 206 Partial Content:服务器已经成功处理了部分 GET 请求,该请求必须包含 Range 头信息来指示客户端希望得到的内容范围。通常使用此类响应来实现断点续传,或者将一个大文档分为多个片段然后并行下载
  • 重定向(300–399)
    • 301 Moved Permanently:永久性重定向
    • 302 Found:临时性重定向。常见应用场景是通过 302 跳转将所有的 HTTP 流量重定向到 HTTPS
    • 303 See Other:和 302 有着相同的功能,但是 303 明确要求客户端应该采用 GET 方法获取资源
    • 304 Not Modified:如果客户端发送了一个带条件的 GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码。304 响应不包含消息体
    • 307 Temporary Redirect:临时重定向。307 与 302 之间的唯一区别在于,当发送重定向请求的时候,307 状态码可以确保请求方法和消息主体不会发生变化;而如果使用 302 响应状态码,一些旧客户端会错误地将请求方法转换为 GET
    • 返回的 Header 中有一个 Location 字段指向目标 URL,浏览器会重定向到这个 URL。
  • 客户端错误(400–499)
    • 400 Bad Request:请求报文中存在语法错误,或者参数有误
    • 401 Unauthorized:未认证(没有登录)
    • 403 Forbidden:没有权限(登录了但没有权限)
    • 404 Not Found
    • 405 Method Not Allowed
  • 服务器错误 (500–599)
    • 500 Internal Server Error:服务器遇到了不知道如何处理的情况
    • 502 Bad Gateway:网关错误,作为网关或代理角色的服务器,从上游服务器(如tomcat、php-fpm)中接收到的响应是无效的
    • 503 Service Unavailable:服务器无法处理请求,常见原因是服务器因维护或重载而停机

HTTP缓存

浏览器可以将已经请求过的资源(如图片、JS 文件)缓存下来,下次再次请求相同的资源时,直接从缓存读取。

浏览器采用的缓存策略有两种:强制缓存协商缓存。浏览器根据第一次请求资源时返回的 HTTP 响应头来选择缓存策略。

强制缓存:为资源设置一个过期时间,只要资源没有过期,就读取浏览器的缓存。强制缓存不需要向服务器发起请求,浏览器直接返回 200(from cache)。

协商缓存:浏览器携带缓存资源的元信息,向服务器发起请求,由服务器决定是否使用缓存。如果协商缓存生效,服务器返回 304 和 Not Modified;如果协商缓存失效,服务器返回 200 和请求结果。

协商缓存的原理是:服务端根据资源的元信息,判断浏览器缓存的资源在服务器上是否有改动。元信息有两种,一种是资源的上次修改时间,另一种是资源 Hash 值。前者实现简单,但是精确度低,文件修改时间只能以秒记;后者精确度高,但是性能低,需要计算哈希值。

强缓存由 Expires(和系统时间进行比较,超则失效)、Cache-Control 和 Pragma(只有 no-cache,不缓存)(优先级依次递增) 3 个 Header 属性共同来控制

协商缓存由:

ETag(响应头)/If-None-Match(请求头):hash

  • ETag 是服务器根据当前文件的内容,给文件生成的唯一标识,只要里面的内容有改动,这个值就会变。服务器通过响应头把这个值给浏览器。 浏览器接收到 ETag 的值,会在下次请求时,将这个值作为 If-None-Match 这个字段的内容,并放到请求头中,然后发给服务器。 服务器接收到 If-None-Match 后,会跟服务器上该资源的 ETag 进行比对:
  • 如果两者不一样,说明要更新了。返回新的资源,跟常规的HTTP请求响应的流程一样。
  • 否则返回304,告诉浏览器直接用本地缓存。

Last-Modified(响应头)/If-Modified-Since(请求头):

  • 在浏览器第一次给服务器发送请求后,服务器会在响应头中加上 Last-Modified(最后修改时间) 这个字段。浏览器接收到后,会在下次请求时,将这个值作为 If-Modified-Since 这个字段的内容,并放到请求头中,然后发给服务器。服务器拿到请求头中的 If-Modified-Since 的字段后,其实会和这个服务器中该资源的最后修改时间对比:
  • 如果请求头中的这个值小于最后修改时间,说明是时候更新了。返回新的资源,跟常规的HTTP请求响应的流程一样。
  • 否则返回304,告诉浏览器直接用本地缓存。

优先级:

强制缓存 > 协商缓存 在协商缓存中,Hash 值 > 上次修改时间

使用缓存的好处主要有以下几点:

  1. 减少了冗余的数据传输,节省了网费。
  2. 缓解了服务器的压力, 大大提高了网站的性能
  3. 加快了客户端加载网页的速度

二者都是用来跟踪浏览器用户身份的会话方式。

Cookie:

存在浏览器里,可以设置过期时间 每次访问服务器时,浏览器会自动在 header 中携带 cookie 如果浏览器禁用了 cookie,可以使用 URL 重写机制,将信息保存在 URL 里

Session:

存在服务端,由服务器维护,一段时间后 session 就失效了
本质上,session 还是通过 cookie 实现的。浏览器的 cookie 中只保存一个 sessionId,所有其他信息均保存在服务端,由 sessionId 标识 Session 失效,其实是服务器设置了失效时间。如果用户长时间不和服务器交互(比如 30 分钟),那么 session 就会被销毁;交互的话,就会刷新 session

HTTP/1.0、HTTP/1.1、HTTP/2.0

HTTP/1.0
  • 在请求中新增了协议版本信息
  • 引入了 HTTP 头的概念
  • 在响应中新增了状态码
  • 默认使用短连接:浏览器每请求一个静态资源,就建立一次连接,任务结束就中断连接
HTTP/1.1
  • 默认支持长连接:在一个网页打开期间,所有网络请求都复用同一条已经建立的连接
    • Pros:性能更好,节省频繁建立 TCP 连接、慢启动、关闭连接等的时间,整体耗时更短
    • Cons:会占用服务器的资源
  • 引入管线化(Pipelining):从前发送请求后需等待并收到响应,才能发送下一个请求,现在允许客户端同时并行发送多个请求
    • Pros:在收到上一个请求的响应之前就可以发出下一个请求,能够节省请求到达服务器的时间,降低通信延迟
    • Cons:服务器要遵循 HTTP/1.1 协议,必须按照客户端发送的请求顺序返回响应,可能发生队头阻塞(HOL blocking)——若上一个请求的响应迟迟没有处理完毕,则后面的响应都会被阻塞
HTTP/2.0

Header 压缩、服务端推送、多路复用

  • Header 压缩

    HTTP/1.1 每次通信都会携带 Header 信息用于描述资源属性。但 headers 在一系列请求中常常是相似的。HTTP/2.0 中,对于 Header 中相同的数据,不会在每次通信中重新发送,而是采用追加或替换的方式。

    具体实现上,HTTP/2.0 在客户端和服务端之间共同维护一个 Header 表,存储之前发送的 key-value 对。Header 表在 HTTP/2.0 的连接期间始终存在。

    Header 压缩可以减少每次通信的数据量,提高传输速度。

  • 服务端推送

    服务器可以对一个客户端请求发送多个响应。服务器向客户端推送资源无需客户端明确的请求。

    服务端根据客户端的请求,提前推送额外的资源给客户端。比如在发送页面 HTML 时主动推送其它 CSS/JS 资源,而不用等到浏览器解析到相应位置,发起请求再响应。

    服务端推送可以减轻数据传输的冗余步骤,同时加快页面响应速度,提升用户体验。

  • 多路复用

    • 二进制分帧:HTTP/1.x 使用文本格式传输数据。HTTP/2.0 在将所有传输信息分割为若干个帧,采用二进制格式进行编码。具体实现上,是在应用层(HTTP)和传输层(TCP)之间增加一个二进制分帧层。

    • 多路复用: HTTP/1.x 有顺序和阻塞约束:

      顺序:服务端必须按照客户端请求到来的顺序,串行返回数据 ; 即使 HTTP/1.1 允许通过同一个连接发起多个请求,也无法真正并行传输
      阻塞:浏览器会限制每个域名下最多同时发起的 6 个连接,超过该数量的连接会被阻塞,以下是常见的优化方法:

      1. 使用多个域名(比如 CDN)来提高浏览器的下载速度
      2. 将多个 JS 文件、CSS 文件等打包成一个文件,将多个小图片合并为雪碧图,减少 HTTP 请求数

    HTTP/2.0 引入了多路复用,通过同一个连接发起多个请求,服务端可以并行地传输数据。基于二进制分帧层,HTTP/2.0 可以同时交错发送多个消息中的帧,接收端可以根据帧中的流标识符和顺序标识,重新组装数据。

HTTP Strict Transport Security(HSTS)

HSTS 的实现也很简单,通过 HTTPS 协议访问你站点的时候,在相应头中加上这样一段信息:

Strict-Transport-Security "max-age=63072000; includeSubdomains;"

Strict-Transport-Security 是 Header 字段名, 后面的 max-age 代表 HSTS 在客户端的生效时间。 includeSubdomains 表示对所有子域名生效。
用户的浏览器一旦得到了 HSTS 的信息,下次再访问站点的时候客户端浏览器就会强制使用 HTTPS。 无论你在地址栏里输入什么,都会以 HTTPS 访问。

DNS原理

整体流程:浏览器搜索自身的 DNS 缓存、搜索操作系统的 DNS 缓存、读取本地的 Host 文件和向本地 DNS 服务器进行查询等。

DNS 查询共有两类:递归查询和迭代查询。

  1. 递归查询是指,当 A 向 B 查询某个域名的 IP 地址时,如果 B 不知道被查询的域名的 IP 地址,那么 B 会替 A 向更上层的服务器发起查询,将查询结果返回 A。
  2. 迭代查询是指,当 A 向 B 查询某个域名的 IP 地址时,如果 B 不知道被查询的域名的 IP 地址,B 会告诉 A 下一步应该向哪个服务器查询,由 A 自己去查。

一般来说,主机(也就是我们的电脑)向本地域名服务器的查询是递归查询,而本地域名服务器向根域名服务器的查询是迭代查询。

  • 根域名服务器(Root DNS Server):管理顶级域名服务器,返回“com”“net”“cn”等顶级域名服务器的 IP 地址。
  • 顶级域名服务器(Top-level DNS Server):管理各自域名下的权威域名服务器,比如 com 顶级域名服务器可以返回 apple.com 域名服务器的 IP 地址。
  • 权威域名服务器(Authoritative DNS Server):管理自己域名下主机的 IP 地址,比如 apple.com 权威域名服务器可以返回 www.apple.com 的 IP 地址。

从输入一个 URL 到页面加载完成的过程

  1. 每打开一个标签页,就创建了一个独立的浏览器渲染进程。用户在某个标签页输入 URL 并回车后,浏览器主进程会新开一个网络线程,发起 HTTP 请求
  2. 浏览器会进行 DNS 查询,将域名解析为 IP 地址
  3. 浏览器获得 IP 地址后,向服务器请求建立 TCP 连接
  4. 浏览器向服务器发起 HTTP 请求
  5. 服务器处理请求,返回 HTTP 响应
  6. 浏览器的渲染进程解析并绘制页面
  7. 如果遇到 JS/CSS/图片 等静态资源的引用链接,重复上述过程,向服务器请求这些资源
  8. 当我们再次发起GET请求,请求还未发出时,浏览器会首先检查 是否有缓存 ,如果 存在缓存 ,是否为强缓存,强缓存不需要发送请求到服务器,直接取浏览器本地缓存即可,如果不存在强缓存或者强缓存失效,则走协商缓存。

浏览器的渲染流程:

  1. 解析 HTML 文件,构建 DOM 树,同时下载 CSS 等静态资源
  2. CSS 文件下载完成后,解析 CSS 文件,形成 CSSOM 树
  3. DOM 与 CSSOM 合并得到渲染树 Render Tree
  4. 计算渲染树中各个元素的尺寸、位置,这个过程称为回流 Reflow
  5. 浏览器将各个图层的信息发送给 GPU,GPU 绘制页面

参考:https://imageslr.com/2020/07/08/tech-interview.html