计算机网络

slug
series-status
status
summary
date
series
type
password
icon
tags
category

计算机网络

notion image

1. TCP

1.0 TCP 报文头部

notion image
IP数据报头部
notion image

1.1 TCP三次握手过程

notion image
      1. 第一次握手:客户端将标志位SYN置为1,随机产生一个值序列号seq=x,并将该数据包发送给服务端,客户端 进入syn_sent状态,等待服务端确认。
      1. 第二次握手:服务端收到数据包后由标志位SYN=1知道客户端请求建立连接,服务端将标志位SYN和 ACK都置为1,ack=x+1,随机产生一个值seq=y,并将该数据包发送给客户端以确认连接请求,服务端进入syn_rcvd状态。
      1. 第三次握手:客户端收到确认后检查,如果正确则将标志位ACK为1,ack=y+1,并将该数据包发送给服务端,服务端进行检查如果正确则连接建立成功,客户端和服务端进入established状态,完成三次握手,随后客户端和服务端之间可以开始传输数据了。

为什么要有最后一次ACK?

客户端首先向服务器发送一个连接请求,但是可能这个连接请求走了远路,等了很长时间,服务器都没有收到,那么客户端可能再次发送,此时服务器端收到一个请求并回复了SYN、ACK;此时,另一个连接请求达到服务器,那么服务器也回复一个SYN、ACK;但是客户端已经受到过确认了,并不搭理这个回复,那么服务器可能陷入等待,如果这种情况多了,那么会导致服务器瘫痪,所以要发送第三个确认。

三次握手,最后一次ACK丢包,会发生什么?

  • 服务端:服务端该TCP连接的状态为SYN_RECV,并且会根据TCP的超时重传机制,会等待3秒、6秒、12秒后重新发送SYN+ACK包,以便客户端重新发送ACK包。
  • 客户端:客户端认为这个连接已经建立,如果客户端向服务端发送数据,服务端将以reset包响应。此时,客户端知道第三次握手失败。
PS:第一个包丢失:客户端重传,但有重传的最大次数。
第二个包丢失:客户端以为服务端没有收到第一次握手,就会出发重传机制,重发SYN报文。
服务端收不到第3次握手回传的包,也会触发重传机制,重传SYN-ACK包给客户端。

第 2 次握手传回了 ACK,为什么还要传回 SYN?

接收端传回发送端所发送的 ACK 是为了告诉客户端,我接收到的信息确实就是你所发送的信号了,这表明从客户端到服务端的通信是正常的。而回传 SYN 则是为了建立并确认从服务端到客户端的通信。”
SYN 同步序列编号(Synchronize Sequence Numbers) 是 TCP/IP 建立连接时使用的握手信号。在客户机和服务器之间建立正常的 TCP 网络连接时,客户机首先发出一个 SYN 消息,服务器使用 SYN-ACK 应答表示接收到了这个消息,最后客户机再以 ACK(Acknowledgement)消息响应。这样在客户机和服务器之间才能建立起可靠的 TCP 连接,数据才可以在客户机和服务器之间传递。

1.2. TCP四次挥手

notion image

四次挥手的过程

(1)客户端向服务器发送FIN控制报文段(首部中的 FIN 比特被置为1);
(2)服务端收到FIN,回复ACK。服务器进入关闭等待状态,发送FIN;
(3)客户端收到FIN,给服务器回复ACK,客户端进入等待状态(进入“等待”,以确保服务器收到ACK真正关闭连接);
(4)服务端收到ACK,链接关闭。

四次挥手的原因

服务器在收到客户端的 FIN 报文段后,可能还有一些数据要传输,所以不能马上关闭连接,但是会做出应答,返回 ACK 报文段。
接下来可能会继续发送数据,在数据发送完后,服务器会向客户单发送 FIN 报文,表示数据已经发送完毕,请求关闭连接。服务器的ACK和FIN一般都会分开发送,从而导致多了一次,因此一共需要四次挥手。
TCP协议是一种面向连接的、可靠的、基于字节流的运输层通信协议。TCP是全双工模式,这就意味着,
step1:当客户端发出FIN报文段时,只是表示客户端已经没有数据要发送了,客户端告诉服务器,它的数据已经全部发送完毕了;但是,这个时候客户端还是可以接受来自服务端的数据;
step2:当服务端返回ACK报文段时,表示它已经知道客户端没有数据发送了,但是服务端还是可以发送数据到客户端的;
step3:当服务端也发送了FIN报文段时,这个时候就表示服务端也没有数据要发送了,就会告诉客户端,我也没有数据要发送了。
step4:如果主动方及时发送ACK报文进行连接中断的确认,这时被动方就直接释放连接,进入可用状态。
简单地说,前 2 次挥手用于关闭一个方向的数据通道,后两次挥手用于关闭另外一个方向的数据通道。

第四次挥手的 ACK 丢了?

客户端在回复「ACK」后,会进入 TIME-WAIT 状态,开始长达 2MSL 的等待,服务端因为没有收到「ACK」的回复,会重试一段时间,直到服务端重试超时后主动断开。
或者等待新的客户端接入后,收到服务端重试的「FIN」消息后,回复「RST」消息,在收到「RST」消息后,复位服务端的状态。

客户端的Time-wait状态为什么要等2MSL?

  • 1)要等客户端发送的ACK报文能够到达服务端,从而使服务器正常关闭。 第四次挥手时,客户端第四次挥手的 ACK 报文不一定会到达服务端。服务端会超时重传 FIN/ACK报文,此时如果客户端已经断开了连接,那么就无法响应服务端的二次请求,这样服务端迟迟收不到 FIN/ACK 报文的确认,就无法正常断开连接。
  • 2)防止已失效的连接请求报文段出现在之后的连接中。 TCP 要求在 2MSL 内不使用相同的序列号。客户端在发送完最后一个 ACK 报文段后,再经过时间2MSL,就可以保证本连接持续的时间内产生的所有报文段都从网络中消失。这样就可以使下一个连接中不会出现这种旧的连接请求报文段。或者即使收到这些过时的报文,也可以不处理它。

说一说你对TIME_WAIT、CLOSE_WAIT的理解

CLOSE_WAIT状态发生在在Sever端收到Client的FIN消息之后。
TIME_WAIT状态发生在客户端主动关闭连接时,发送最后一个ack后;
出现CLOSE_WAIT的状态原因
假设最终的ACK丢失,server将重发FIN,client必须维护TCP状态信息以便可以重发最终的ACK,否则会发送RST,结果server认为发生错误。TCP实现必须可靠地终止连接的两个方向(全双工关闭),client必须进入 TIME_WAIT 状态,因为client可能面临重发最终ACK的情形。
出现 TIME_WAIT的状态原因
IME_WAIT状态之所以存在,是为了保证网络的可靠性。由于TCP连接是双向的,所以在关闭连接的时候,两个方向各自都需要关闭。先发FIN包的一方执行的是主动关闭,后发送FIN包的一方执行的是被动关闭。主动关闭的一方会进入TIME_WAIT状态,并且在此状态停留2MSL时长。如果Server端一直没有向client端发送FIN消息(调用close() API),那么这个CLOSE_WAIT会一直存在下去。

TIME-WAIT 状态过多会产生什么后果?怎样处理?

TIME_WAIT 是主动断开连接的一方会进入的状态,一般情况下,都是客户端所处的状态;服务器端一般设置不主动关闭连接。
  • 从服务器来讲,短时间内关闭了大量的Client连接,就会造成服务器上出现大量的TIME_WAIT连接,严重消耗着服务器的资源,此时部分客户端就会显示连接不上。
  • 从客户端来讲,客户端TIME_WAIT过多,就会导致端口资源被占用,因为端口就65536个,被占满就会 导致无法创建新的连接。
  • 解决:
    • 服务器可以设置 SO_REUSEADDR 套接字选项来避免 TIME_WAIT状态,此套接字选项告诉内核,即使此端口正忙(处于TIME_WAIT状态),也请继续并重用它。
  • 整系统内核参数,修改/etc/sysctl.conf文件,即修改 net.ipv4.tcp_tw_reuse 和tcp_timestamps
    • 强制关闭,发送 RST 包越过TIME_WAIT状态,直接进入CLOSED状态。
    如果已经建立了连接,但是客户端出现故障了怎么办?
    简而言之,通过定时器 + 超时重试机制,尝试获取确认,直到最后会自动断开连接。
    具体而言,TCP 设有一个保活计时器。服务器每收到一次客户端的数据,都会重新复位这个计时器,时间通常是设置为 2 小时。若 2 小时还没有收到客户端的任何数据,服务器就开始重试:每隔 75 分钟发送一个探测报文段,若一连发送 10 个探测报文后客户端依然没有回应,那么服务器就认为连接已经断开了。

    1.3. TCP和UDP的区别?

    notion image
    应用场景:TCP 用于在传输层有必要实现可靠传输的情况,UDP 用于对高速传输和实时性有较高要求的通信。TCP和 UDP 应该根据应用目的按需使用。
    notion image

    1.4 TCP 协议如何保证可靠传输

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

    1.5 ARQ 协议

    自动重传请求(Automatic Repeat-reQuest,ARQ)是 OSI 模型中数据链路层和传输层的错误纠正协议之一。它通过使用确认和超时这两个机制,在不可靠服务的基础上实现可靠的信息传输。如果发送方在发送后一段时间之内没有收到确认帧,它通常会重新发送。ARQ 包括停止等待 ARQ 协议和连续 ARQ 协议。

    停止等待 ARQ 协议

    停止等待协议是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认(回复 ACK)。如果过了一段时间(超时时间后),还是没有收到 ACK 确认,说明没有发送成功,需要重新发送,直到收到确认后再发下一个分组。
    在停止等待协议中,若接收方收到重复分组,就丢弃该分组,但同时还要发送确认。
    优缺点:
    • 优点: 简单
    • 缺点: 信道利用率低,等待时间长
    1) 无差错情况:
    发送方发送分组, 接收方在规定时间内收到, 并且回复确认. 发送方再次发送。
    2) 出现差错情况(超时重传):
    停止等待协议中超时重传是指只要超过一段时间仍然没有收到确认,就重传前面发送过的分组(认为刚才发送过的分组丢失了)。因此每发送完一个分组需要设置一个超时计时器,其重传时间应比数据在分组传输的平均往返时间更长一些。这种自动重传方式常称为自动重传请求 ARQ 。另外在停止等待协议中若收到重复分组,就丢弃该分组,但同时还要发送确认。连续 ARQ 协议* 可提高信道利用率。发送维持一个发送窗口,凡位于发送窗口内的分组可连续发送出去,而不需要等待对方确认。接收方一般采用累积确认,对按序到达的最后一个分组发送确认,表明到这个分组位置的所有分组都已经正确收到了。* 可提高信道利用率。发送维持一个发送窗口,凡位于发送窗口内的分组可连续发送出去,而不需要等待对方确认。接收方一般采用累积确认,对按序到达的最后一个分组发送确认,表明到这个分组位置的所有分组都已经正确收到了。
    3) 确认丢失和确认迟到
    • 确认丢失 :确认消息在传输过程丢失。当 A 发送 M1 消息,B 收到后,B 向 A 发送了一个 M1 确认消息,但却在传输过程中丢失。而 A 并不知道,在超时计时过后,A 重传 M1 消息,B 再次收到该消息后采取以下两点措施:1. 丢弃这个重复的 M1 消息,不向上层交付。 2. 向 A 发送确认消息。(不会认为已经发送过了,就不再发送。A 能重传,就证明 B 的确认消息丢失)。
    • 确认迟到 :确认消息在传输过程中迟到。A 发送 M1 消息,B 收到并发送确认。在超时时间内没有收到确认消息,A 重传 M1 消息,B 仍然收到并继续发送确认消息(B 收到了 2 份 M1)。此时 A 收到了 B 第二次发送的确认消息。接着发送其他数据。过了一会,A 收到了 B 第一次发送的对 M1 的确认消息(A 也收到了 2 份确认消息)。处理如下:1. A 收到重复的确认后,直接丢弃。2. B 收到重复的 M1 后,也直接丢弃重复的 M1。

    连续 ARQ 协议

    连续 ARQ 协议可提高信道利用率。发送方维持一个发送窗口,凡位于发送窗口内的分组可以连续发送出去,而不需要等待对方确认。接收方一般采用累积确认,对按序到达的最后一个分组发送确认,表明到这个分组为止的所有分组都已经正确收到了。
    优缺点:
    • 优点: 信道利用率高,容易实现,即使确认丢失,也不必重传。
    • 缺点: 不能向发送方反映出接收方已经正确收到的所有分组的信息。 比如:发送方发送了 5 条 消息,中间第三条丢失(3 号),这时接收方只能对前两个发送确认。发送方无法知道后三个分组的下落,而只好把后三个全部重传一次。这也叫 Go-Back-N(回退 N),表示需要退回来重传已经发送过的 N 个消息。

    1.6 滑动窗口和流量控制

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

    拥塞控制

    在某段时间,若对网络中某一资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏。这种情况就叫拥塞。拥塞控制就是为了防止过多的数据注入到网络中,这样就可以使网络中的路由器或链路不致过载。拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。拥塞控制是一个全局性的过程,涉及到所有的主机,所有的路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是点对点通信量的控制,是个端到端的问题。流量控制所要做到的就是抑制发送端发送数据的速率,以便使接收端来得及接收。
    为了进行拥塞控制,TCP 发送方要维持一个拥塞窗口(cwnd) 的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个。
    TCP 的拥塞控制采用了四种算法,即 慢开始 、 拥塞避免 、快重传 和 快恢复。在网络层也可以使路由器采用适当的分组丢弃策略(如主动队列管理 AQM),以减少网络拥塞的发生。
    • 慢开始: 慢开始算法的思路是当主机开始发送数据时,如果立即把大量数据字节注入到网络,那么可能会引起网络阻塞,因为现在还不知道网络的符合情况。经验表明,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是由小到大逐渐增大拥塞窗口数值。cwnd 初始值为 1,每经过一个传播轮次,cwnd 加倍。
    • 拥塞避免: 拥塞避免算法的思路是让拥塞窗口 cwnd 缓慢增大,即每经过一个往返时间 RTT 就把发送放的 cwnd 加 1.
    • 快重传与快恢复: 在 TCP/IP 中,快速重传和恢复(fast retransmit and recovery,FRR)是一种拥塞控制算法,它能快速恢复丢失的数据包。没有 FRR,如果数据包丢失了,TCP 将会使用定时器来要求传输暂停。在暂停的这段时间内,没有新的或复制的数据包被发送。有了 FRR,如果接收机接收到一个不按顺序的数据段,它会立即给发送机发送一个重复确认。如果发送机接收到三个重复确认,它会假定确认件指出的数据段丢失了,并立即重传这些丢失的数据段。有了 FRR,就不会因为重传时要求的暂停被耽误。 当有单独的数据包丢失时,快速重传和恢复(FRR)能最有效地工作。当有多个数据信息包在某一段很短的时间内丢失时,它则不能很有效地工作。

    1.7 TCP中的缓存有什么作用?

    TCP缓冲区是什么?

    每个 socket 被创建后,都会分配两个缓冲区,输入缓冲区和输出缓冲区。

    缓冲区的意义(作用)

    TCP的发送缓冲区是用来缓存应用程序的数据,发送缓冲区的每个字节都有序列号,被应答确认的序列号对应的数据会从发送缓冲区删除掉。
    notion image
    write()/send() 并不立即向网络中传输数据,而是先将数据写入缓冲区中,再由TCP协议将数据从缓冲区发送到目标机器。
    一旦将数据写入到缓冲区,函数就可以成功返回,不管它们有没有到达目标机器,也不管它们何时被发送到网络,这些都是TCP协议负责的事情。
    TCP协议独立于 write()/send() 函数,数据有可能刚被写入缓冲区就发送到网络,也可能在缓冲区中不断积压,多次写入的数据被一次性发送到网络,比如nagle算法,这取决于当时的网络情况、当前线程是否空闲等诸多因素,不由程序员控制。
    read()/recv() 函数也是如此,也从输入缓冲区中读取数据,而不是直接从网络中读取。

    I/O缓冲区特性

    (1)I/O缓冲区在每个TCP套接字中单独存在;
    (2)I/O缓冲区在创建套接字时自动生成;
    (3)即使关闭套接字也会继续传送输出缓冲区中遗留的数据;
    (4)关闭套接字将丢失输入缓冲区中的数据。

    1.8 tcp粘包、拆包的机制

    TCP粘包和拆包问题

    TCP是一个“流”协议,所谓流,就是没有界限的一长串二进制数据。TCP作为传输层协议并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的划分,所以在业务上认为是一个完整的包,可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题。

    产生TCP粘包和拆包的原因

    我们知道TCP是以流的方式传输数据的,传输的最小单位为一个报文段(Segment)。TCP Header中有个Options标识位。常见的标识位为MSS(Maximum Segment Size)指的是,连接层每次传输的数据有个最大限制MTU(Maximum Transmission Unit),一般是1500bit,超过这个量要分成多个报文段,MSS则是这个最大限制减去TCP的header,光是要传输的数据的大小,一般为1460bit。换算成字节,也就是180多字节。 TCP为提高性能,发送端会将需要发送的数据发送到缓冲区,等待缓冲区满了以后,再将缓冲中的数据发送到接收方。同理,接收方也有缓冲区这样的机制来接受数据。 发生TCP粘包、拆包主要是以下原因:
    • (1)应用程序写入数据大于套接字缓冲区大小,会发生拆包;
    • (2)应用程序写入数据小于套接字缓冲区大小,网卡将应用多次写入的数据发送到网络上,这将会发送粘包;
    • (3)进行MSS(最大报文长度)大小的TCP分段,当TCP报文长度——TCP header长度>MSS 的时候会发生拆包;
    • (4)接收方法不及时读取套接字缓冲区数据,这将发生粘包。

    如何处理粘包和拆包

    处理拆包:http连接是短连接,请求之后,收到回答,立马断开连接,不会出现粘包。 拆包现象是有可能存在的。
    (1)通过包头+包长+包体的协议形式,当服务器端获取到指定的包长时才说明获取完整。
    (2) 指定包的结束标识,这样当我们获取到指定的标识时,说明包获取完整。
    处理粘包的方法如下:
    (1)发送方对于发送方造成的粘包问题,可以通过关闭Nagle算法来解决,使用TCP_NODELAY选项来关闭算法。
    (2)接收方没有办法来处理粘包现象,只能将问题交给应用层来处理。应用层的解决办法简单可行,不仅能解决接收方的粘包问题,还可以解决发送方的粘包问题。解决办法:循环处理,应用程序从接收缓存中读取分组时,读完一条数据,就应该循环读取下一条数据,直到所有数据都被处理完成,判断每条数据的长度的方法有两种:
    a. 格式化数据:每条数据有固定的格式(开始符,结束符),这种方法简单易行,但是选择开始符和结束符时一定要确保每条数据的内部不包含开始符和结束符。
    b. 发送长度:发送每条数据时,将数据的长度一并发送,例如规定数据的前4位是数据的长度,应用层在处理时可以根据长度来判断每个分组的开始和结束位置。

    UDP会不会产生粘包问题呢?

    TCP为了保证可靠传输并减少额外的开销(每次发包都要验证),采用了基于流的传输,基于流的传输不认为消息是一条一条的,是无保护消息边界的(保护消息边界:指传输协议把数据当做一条独立的消息在网上传输,接收端一次只能接受一条独立的消息)。UDP则是面向消息传输的,是有保护消息边界的,接收方一次只接受一条独立的信息,所以不存在粘包问题。
    举个例子:有三个数据包,大小分别为2k、4k、6k,如果采用UDP发送的话,不管接受方的接收缓存有多大,我们必须要进行至少三次以上的发送才能把数据包发送完,但是使用TCP协议发送的话,我们只需要接受方的接收缓存有12k的大小,就可以一次把这3个数据包全部发送完毕。

    2. 长连接和短连接

    在HTTP/1.0中,默认使用的是短连接。也就是说,浏览器和服务器每进行一次HTTP操作,就建立一 次连接,但任务结束就中断连接。如果客户端浏览器访问的某个HTML或其他类型的 Web页中包含有其 他的Web资源,如JavaScript文件、图像文件、CSS文件等;当浏览器每遇到这样一个Web资源,就会建 立一个HTTP会话。
    从 HTTP/1.1起,默认使用长连接,用以保持连接特性。使用长连接的HTTP协议,会在响应头有: Connection:keep-alive​ .
    在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的 TCP连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接。长连接使得多数请求以管线化方式发送成为可能。从前发送请求后需要等待并收到响应,才能发送下一个请求,而在长连接下可以同时并行发送多个请求。
    Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。实现长连接要客户端和服务端都支持长连接。
    HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接。

    3. 如何让UDP可靠一些?

    1. 为什么需要可靠的UDP 在弱网(2G、3G、信号不好)环境下,使用 TCP 连接的延迟很高,影响体验。使用 UDP 是很好的解决方案,既然把 UDP 作为弱网里面的 TCP 来使用,就必须保证数据传输能像 TCP 一样可靠
    1. 如何实现可靠的UDP UDP它不属于连接型协议,因而具有资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。 传输层无法保证数据的可靠传输,只能通过应用层来实现了。实现的方式可以参照tcp可靠性传输的方式,只是实现不在传输层,实现转移到了应用层。关键在于两点,从应用层角度考虑:(1)提供超时重传,能避免数据报丢失。(2)提供确认序列号,可以对数据报进行确认和排序。 本端:首先在UDP数据报定义一个首部,首部包含确认序列号和时间戳,时间戳是用来计算RTT(数据报传输的往返时间),计算出合适的RTO(重传的超时时间)。然后以等-停的方式发送数据报,即收到对端的确认之后才发送下一个的数据报。当时间超时,本端重传数据报,同时RTO扩大为原来的两倍,重新开始计时。 对端:接受到一个数据报之后取下该数据报首部的时间戳和确认序列号,并添加本端的确认数据报首部之后发送给对端。根据此序列号对已收到的数据报进行排序并丢弃重复的数据报。

    4. HTTP和HTTPS

    • 安全性和资源消耗 :HTTP 协议运行在 TCP 之上,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份。HTTPS是运行在 SSL/TLS 之上的 HTTP 协议,SSL/TLS 运行在 TCP 之上。所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。所以说,HTTP 安全性没有 HTTPS 高,但是 HTTPS 比 HTTP 耗费更多服务器资源。
    • 端口号 :HTTP 默认是 80,HTTPS 默认是 443。
    • URL 前缀 :HTTP 的 URL 前缀是 http://​,HTTPS 的 URL 前缀是 https://​。

    HTTP和HTTPS的区别?

    HTTP
    HTTP(Hypertext Transfer Protocol)全称是超文本传输协议,它是一个无状态协议,也就是说服务器不维护任何有关客户端过去所发请求的消息。
    HTTP 是应用层协议,它以 TCP(传输层)作为底层协议,默认端口为 80。HTTP扩展性强、速度快、跨平台支持性好。
    HTTPS
    HTTPS 协议(Hyper Text Transfer Protocol Secure),是 HTTP 的加强安全版本。HTTPS 是基于 HTTP 的,也是用 TCP 作为底层协议,并额外使用 SSL/TLS 协议用作加密和安全认证。默认端口号是 443。
    HTTPS 协议中,SSL 通道通常使用基于密钥的加密算法,密钥长度通常是 40 比特或 128 比特。HTTPS保密性好、信任度高。

    HTTP系列的区别

    HTTP1.0和HTTP1.1的区别?

    1)长连接:HTTP 1.1支持长连接(Persistent Connection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,在HTTP1.1中默认开启Connection: keep-alive ,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。
    2)缓存处理:在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略,可供选择的缓存头来控制缓存策略。
    3)带宽优化及网络连接的使用:HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
    4)错误通知的管理:在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
    5)Host头处理:在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)

    HTTP1.1和 HTTP2.0的区别?

    HTTP2.0相比HTTP1.1支持的特性:
    新的二进制格式:HTTP1.1的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。
    多路复用,即连接共享,即每一个request都是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。
    头部压缩,HTTP1.1的头部(header)带有大量信息,而且每次都要重复发送;HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。
    服务端推送:服务器除了对最初请求的响应外,服务器还可以额外的向客户端推送资源,而无需客户端明确的请求。
    notion image

    HTTPS的秘钥交换过程

    HTTPS的密钥交换过程如下:
    1. 客户端要访问一个网站,向支持https的服务器发起请求。
    1. 客户端向服务器发送自己支持的秘钥交换算法列表。
    1. 服务器选取一种秘钥交换算法加上CA证书返回给客户端。(CA证书包含了服务器信息:域名、申请证书的公司、公共密钥等)。
    1. 客户端验证服务器是否合法,并生成一个随机数然后用协商好的加密算法加密生成随机秘钥,并用刚才从CA证书中拿到的公钥对其加密后发送给服务器。
    1. 服务器收到后用自己的私钥解密(中间人没有服务器的私钥,所以没有办法看到传输的数据,另外确认秘钥交换算法是在第一步,中间人是不知道秘钥交换算法(中间人是无法在第一步做手脚的,那等同于它自己就是一个真实客户端发起了一个新的请求,唯一一种情况攻击人有一个合法CA下发的证书,且客户端(一般为安卓设备)没有对CA下发的证书中的内容网站地址和当前请求地址做对比校验),就算攻击者有公钥,因为不知道协商协议,所以做不出来随机秘钥,顶多就是在传输过程中将报文拦截下来,乱改,但是给到服务器后,私钥是解不开乱改之后的密文的)。
    1. 服务器私钥解密之后,拿到对称秘钥,并且用它再加密一个信息,返回给浏览器。注意: 最关键的一步就是在客户端采用 RSA 或 Diffie-Hellman 等加密算法生成 Pre-master,这个随机秘钥是用来计算最终的对称秘钥的,用公钥加密之后攻击人是不知道这个这个随机秘钥的,只有服务器才能解的开。

    为什么HTTPS用到了非对称/对称加密两种

    • 在建立通信前使用非对称加密,保证了对称密钥的安全性;非对称加密使用两个密钥:公钥和私钥,公钥可以任意分发而私钥保密,解决了密钥交换问题但速度慢。
    • 在通信过程中使用对称加密。对称加密只使用一个密钥,运算速度快,密钥必须保密,但无法做到安全的密钥交换。
    因为对称加密的保密性完全依赖于密钥的保密性。在双方通信之前,需要商量一个用于对称加密的密钥。而网络通信的信道是不安全的,传输报文对任何人是可见的,密钥的交换肯定不能直接在网络信道中传输。因此,使用非对称加密,对对称加密的密钥进行加密,保护该密钥不在网络信道中被窃听。这样,通信双方只需要一次非对称加密,交换对称加密的密钥,在之后的信息通信中,使用绝对安全的密钥,对信息进行对称加密,即可保证传输消息的保密性。
    PS:关于非对称加密
    非对称加密算法来解决,共有两个密钥:
    • 一个是公钥,这个是可以公开给所有人的;
    • 一个是私钥,这个必须由本人管理,不可泄露。
    这两个密钥可以双向加解密的,比如可以用公钥加密内容,然后用私钥解密,也可以用私钥加密内容,公钥解密内容。
    流程的不同,意味着目的也不相同:
    • 公钥加密,私钥解密。这个目的是为了保证内容传输的安全,因为被公钥加密的内容,其他人是无法解密的,只有持有私钥的人,才能解密出实际的内容;
    • 私钥加密,公钥解密。这个目的是为了保证消息不会被冒充,因为私钥是不可泄露的,如果公钥能正常解密出私钥加密的内容,就能证明这个消息是来源于持有私钥身份的人发送的。
    一般我们不会用非对称加密来加密实际的传输内容,因为非对称加密的计算比较耗费性能的。
    所以非对称加密的用途主要在于通过「私钥加密,公钥解密」的方式,来确认消息的身份,我们常说的数字签名算法,就是用的是这种方式,不过私钥加密内容不是内容本身,而是对内容的哈希值加密。

    HTTPS的证书认证过程

    HTTPS的证书认证过程如下:
    1. 浏览器将自己支持的一套加密规则发送给网站。
    1. 网站从中选出一组加密算法与HASH算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。
    1. 浏览器获得网站证书之后浏览器要做以下工作: (1) 验证证书的合法性(颁发证书的机构是否合法,证书中包含的网站地址是否与正在访问的地址一致等),如果证书受信任,则浏览器栏里面会显示一个小锁头,否则会给出证书不受信的提示。 (2)如果证书受信任,或者是用户接受了不受信的证书,浏览器会生成一串随机数的密码,并用证书中提供的公钥加密。 (3)使用约定好的HASH算法计算握手消息,并使用生成的随机数对消息进行加密,最后将之前生成的所有信息发送给网站。
    1. 网站接收浏览器发来的数据之后要做以下的操作: (1) 使用自己的私钥将信息解密取出密码,使用密码解密浏览器发来的握手消息,并验证HASH是否与浏览器发来的一致。 (2) 使用密码加密一段握手消息,发送给浏览器。
    1. 浏览器解密并计算握手消息的HASH,如果与服务端发来的HASH一致,此时握手过程结束,之后所有的通信数据将由之前浏览器生成的随机密码并利用对称加密算法进行加密。

    为什么要有CA?

    为了非对称加密中公钥传输的信赖性问题,防止中间人攻击。
    CA 默认是受信任的第三方。CA 会给各个服务器颁发证书,证书存储在服务器上,并附有 CA 的电子签名。(为了防止郑虎被伪造,用到了数字签名。数字签名,是 CA 在给服务器颁发证书时,使用散列+加密的组合技术,在证书上盖个章,以此来提供验伪的功能。)
    PS:(CA 知道服务器的公钥,对该公钥采用散列技术生成一个摘要。CA 使用 CA 私钥对该摘要进行加密,并附在证书下方,发送给服务器。
    现在服务器将该证书发送给客户端,客户端需要验证该证书的身份。客户端找到第三方机构 CA,获知 CA 的公钥,并用 CA 公钥对证书的签名进行解密,获得了 CA 生成的摘要。
    客户端对证书数据(也就是服务器的公钥)做相同的散列处理,得到摘要,并将该摘要与之前从签名中解码出的摘要做对比,如果相同,则身份验证成功;否则验证失败。)

    SSL/STL

    SSL 指安全套接字协议(Secure Sockets Layer),TLS 是基于 SSL 之上的,但由于习惯叫法,通常把 HTTPS 中的核心加密协议混成为 SSL/TLS。
    SSL/STL的工作原理
    SSL/TLS 的核心要素是非对称加密。非对称加密采用两个密钥——一个公钥,一个私钥。在通信时,私钥仅由解密者保存,公钥由任何一个想与解密者通信的发送者(加密者)所知。非对称加密的公钥和私钥需要采用一种复杂的数学机制生成(密码学认为,为了较高的安全性,尽量不要自己创造加密方案)。公私钥对的生成算法依赖于单向陷门函数。
    使用 SSL/TLS 进行通信的双方需要使用非对称加密方案来通信,但是非对称加密设计了较为复杂的数学算法,在实际通信过程中,计算的代价较高,效率太低,因此,SSL/TLS 实际对消息的加密使用的是对称加密。(对称加密:通信双方共享唯一密钥 k,加解密算法已知,加密方利用密钥 k 加密,解密方利用密钥 k 解密,保密性依赖于密钥 k 的保密性。)

    HTTPS的三个随机数

    在HTTPS协议中,TLS(Transport Layer Security)是关键的安全组件。在TLS握手过程中,随机数起着重要的作用。TLS握手过程中涉及到以下随机数:
    1. 客户端随机数(Client Random):客户端生成一个32字节的随机数,发送给服务器。这个随机数由4字节的UNIX时间戳和28字节的随机数据组成。客户端随机数用于确保每次握手过程都是唯一的。
    1. 服务器随机数(Server Random):服务器生成一个32字节的随机数,类似于客户端随机数,它也包含4字节的UNIX时间戳和28字节的随机数据。服务器随机数同样用于确保握手过程的唯一性。
    1. Pre-Master Secret:客户端生成一个48字节的随机数,称为Pre-Master Secret。这个随机数用于后续计算主密钥(Master Secret)。在非前向保密(Non-Forward Secrecy)密码套件中,客户端会使用服务器的公钥对Pre-Master Secret进行加密,然后将其发送给服务器。在前向保密(Forward Secrecy)密码套件中,如ECDHE和DHE,客户端和服务器通过Diffie-Hellman(DH)密钥交换协议共同计算出Pre-Master Secret。
    1. 随机数生成器(Random Number Generator,RNG):在TLS中,许多加密操作依赖于随机数生成器。RNG用于生成上述随机数、初始化向量(IVs)、加密密钥等。RNG的安全性对整个加密过程至关重要。
    这些随机数在TLS握手过程中协同工作,确保通信的安全性和完整性。通过使用随机数,TLS可以防止重放攻击、中间人攻击等安全问题。
    现在我们来理清一下SSL建立的过程:
    1. 客户端通过发送Client Hello报文开始SSL通信。报文中包含客户端支持的SSL的指定版本、加密组件(Cipher Suite)列表(所使用的加密算法及密钥长度等)。 注意:客户端还会附加一个随机数,这里记为A。
    1. 服务器可进行SSL通信时,会以Server Hello报文作为应答。和客户端一样,在报文中包含SSL版本以及加密组件。服务器的加密组件内容是从接收到的客户端加密组件内筛选出来的。 注意:这里服务器同样会附加一个随机数,发给客户端,这里记为B。
    1. 之后服务器发送Certificate报文。报文中包含公开密钥证书。(具体的数字签名请看证书一节)
    1. 最后服务器发送Server Hello Done报文通知客户端,最初阶段的SSL握手协商部分结束。
    1. SSL第一次握手结束后,客户端会对服务器发过来的证书进行验证,如果验证成功,解密取出证书中的公钥。(具体查看证书一节) 接着,客户端以Client Key Exchange报文作为回应。报文中包含通信加密中使用的一种被称为Pre-master secret的随机密码串。该报文使用从证书中解密获得的公钥进行加密(其实就是服务器的公钥)。
    1. 客户端继续发送Change Cipher Spec报文。用于告知服务端,客户端已经切换到之前协商好的加密套件(Cipher Suite)的状态,准备使用之前协商好的加密套件加密数据并传输了。
    1. 客户端发送Finished报文。该报文包含连接至今全部报文的整体校验值(也就是HASH值),用来供服务器校验。
    1. 服务器接收到客户端的请求之后,使用私钥解密报文,把Pre-master secret取出来。接着,服务器同样发送Change Cipher Spec报文。
    1. 服务器同样发送Finished报文,用来供客户端校验。
    1. 服务器和客户端的Finished报文交换完毕之后,SSL连接就算建立完成。当然,通信会受到SSL的保护。从此处开始进行应用层协议的通信,即发送HTTP请求。
    1. 应用层协议通信,即发送HTTP响应。
    1. 最后由客户端断开连接。断开连接时,发送close_notify报文。上图做了一些省略,这步之后再发送TCP FIN报文来关闭与TCP的通信。

    5. HTTP 状态码 与 常见字段

    5.1 状态码

    分类
    分类描述
    1
    信息,服务器收到请求,需要请求者继续执行操作
    2
    成功,操作被成功接收并处理
    3
    重定向,需要进一步的操作以完成请求
    4
    客户端错误,请求包含语法错误或无法完成请求
    5
    服务器错误,服务器在处理请求的过程中发生了错误
    常见状态码:
    • 200:服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。
    • 201: 已创建。成功请求并创建了新的资源
    • 202 (Accepted/接受) (SC_ACCEPTED)告诉客户端请求正在被执行,但还没有处理完。
    • 203: 非授权信息。请求成功。但返回的meta信息不在原始的服务器,而是一个副本
    • 204: 无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档
    • 205: 重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域
    • 206: 部分内容。服务器成功处理了部分GET请求
    • 301: 永久移动。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替
    • 302:临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
    • 303: 查看其它地址。与301类似。使用GET和POST请求查看
    • 304: 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源
    • 305: 使用代理。所请求的资源必须通过代理访问
    • 306: 已经被废弃的HTTP状态码
    • 307: 临时重定向。与302类似。使用GET请求重定向
    • 400 :客户端请求的语法错误,服务器无法理解
    • 401 (Unauthorized/未授权)请求要求用户的身份认证 **401 ** (SC_UNAUTHORIZED)表示客户端在授权头信息中没有有效的身份信息时访问受到密码保护的页面。这个响应必须包含一个WWW-Authenticate的授权信息头。
    • 403 :服务器理解请求客户端的请求,但是拒绝执行此请求
    • 404 :服务器无法根据客户端的请求找到资源(网页)。
    • 405: 客户端请求中的方法被禁止
    • 406: 服务器无法根据客户端请求的内容特性完成请求
    • 407: 请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权
    • 408: 服务器等待客户端发送的请求时间过长,超时
    • 409: 服务器完成客户端的 PUT 请求时可能返回此代码,服务器处理请求时发生了冲突
    • 410: 客户端请求的资源已经不存在。410不同于404,如果资源以前有现在被永久删除了可使用410代码,网站设计人员可通过301代码指定资源的新位置
    • 411: 服务器无法处理客户端发送的不带Content-Length的请求信息
    • 412: 客户端请求信息的先决条件错误
    • 413: 由于请求的实体过大,服务器无法处理,因此拒绝请求。为防止客户端的连续请求,服务器可能会关闭连接。如果只是服务器暂时无法处理,则会包含一个Retry-After的响应信息
    • 414: 请求的URI过长(URI通常为网址),服务器无法处理
    • 415: 服务器无法处理请求附带的媒体格式
    • 416: 客户端请求的范围无效
    • 417: 服务器无法满足Expect的请求头信息
    • 500: (服务器内部错误) 服务器遇到错误,无法完成请求。
    • *501: ** 服务器不支持请求的功能,无法完成请求
    • 502 (Bad Gateway/错误的网关)作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应
    • 503 (Service Unavailable/服务无法获得)由于超载或系统维护,服务器暂时的无法处理客户端的请求。例如,如果某些线程或数据库连接池已经没有空闲则servlet会返回这个头信息。服务器可提供一个Retry-After头信息告诉客户端什么时候可以在试一次。
    • 504 (Gateway Timeout/网关超时)充当网关或代理的服务器,未及时从远端服务器获取请求
    • 505: 服务器不支持请求的HTTP协议的版本,无法完成处理

    5.2 HTTP 常见字段

    • Host:客户端发送请求时,用来指定服务器的域名。
    • Content-Length :服务器在返回数据时,会有Content-Length字段,表明本次回应的数据长度。
    • Connection: 最常用于客户端要求服务器使用 TCP 持久连接,以便其他请求复用。HTTP/1.1 版本的默认连接都是长连接,但为了兼容老版本的 HTTP,需要指定 Connection​ 首部字段的值为 Keep-Alive​。
      • Content-Type:用于服务器回应时,告诉客户端,本次数据是什么格式。
        • Content-Encoding:说明数据的压缩方法。表示服务器返回的数据使用了什么压缩格式。

          6. DNS域名解析

          1. 首先客户端位置是一台电脑或手机,在打开浏览器以后,比如输入http://www.zdns.cn的域名,它首先是由浏览器发起一个DNS解析请求,如果本地缓存服务器中找不到结果,则首先会向根服务器查询,根服务器里面记录的都是各个顶级域所在的服务器的位置,当向根服务器请求[http://www.zdns.cn]的时候,根服务器就会返回.cn服务器的位置信息;
          1. 递归服务器拿到.cn的权威服务器地址以后,就会寻问.cn的权威服务器,知不知道[http://www.zdns.cn]的位置。这个时候.cn权威服务器查找并返回[http://zdns.cn]服务器的地址;
          1. 继续向[http://zdns.cn]的权威服务器去查询这个地址,由[http://zdns.cn]的服务器给出了地址:202.173.11.10;
          1. 最终进入http的链接,顺利访问网站;
          补充说明:一旦递归服务器拿到解析记录以后,就会在本地进行缓存,如果下次客户端再请求本地的递归域名服务器相同域名的时候,就不会再这样一层一层查了,因为本地服务器里面已经有缓存了,这个时候就直接把[http://www.zdns.cn]的记录返回给客户端就可以了。
          一级/二级/三级域名什么区别?
          • 一级域名:又叫顶级域名,一般分为国家顶级域名(如.cn、.us等)和国际顶级域名(如.net)
          • 二级域名:在顶级域名之下的域名,可以按照不同类型(如.gov、.edu等)或者不同地域分(如省份)。
          • 三级域名:这个域名是让网站的制作者自己起的,三级域名是由字母、大小写以及连接符号三个部分组成的,网站制作者可以根据自己网站的特点进行选择。

          7. 输入一条url经历的过程?

          • 客户端获取URL - > DNS解析 - > TCP连接 - >发送HTTP请求 - >服务器处理请求 - >返回报文 - >浏览器解析渲染页面 - > TCP断开连接
          1. 域名解析(域名变为 ip 地址)。 (浏览器搜索自己的DNS缓存(维护一张域名与IP的对应表);若没有,则搜索操作系统的DNS缓存(维护一张域名与IP的对应表);若没有,则搜索操作系统的hosts文件(维护一张域名与IP的对应表)。 若都没有,则找 tcp/ip 参数中设置的首选 dns 服务器,即本地 dns 服务器(递归查询),本地域名服务器查询自己的dns缓存,如果没有,则进行迭代查询。将本地dns服务器将IP返回给操作系统,同时缓存IP。)
          1. 发起 tcp 的三次握手,建立 tcp 连接。浏览器会以一个随机端口(1024-65535)向服务端的web程序 80 端口发起 tcp 的连接。
          1. 建立 tcp 连接后发起 http 请求。
          1. 服务器响应 http 请求,客户端得到 html 代码。服务器 web 应用程序收到 http 请求后,就开始处理请求,处理之后就返回给浏览器 html 文件。
          1. 浏览器解析 html 代码,并请求 html 中的资源。
          1. 浏览器对页面进行渲染,并呈现给用户。
          1. 如果客户端没有需要的请求了,会通过四次挥手断开TCP连接

          8. get和post的请求的区别?

          get和post是http协议中的两种发送请求的方法。而http的底层是TCP/IP,所以get和post的底层也是TCP/IP,也就是都用的TCP连接。
          get和post的区别主要是浏览器和服务器对其使用上的约定不同。
          使用上的区别:
          • GET使用URL传参;而POST一般将数据放在Body中,也可以放在url参数中,这是HTTP协议用法的约定。
          • GET方式提交的数据有长度限制( 主要是浏览器/服务器做Chrome对url的长度限制是8192字符,IE2083字符);POST的数在理论上没有限制,Tomcat服务器默认2M的限制。
          • POST比GET安全,因为post在body传递数据时在地址栏上不可见的,也无法进行缓存。
          • 一些浏览器在设计的时候,post请求会产生两个tcp数据包(浏览器先发送header,服务器响应100 continue;浏览器再发送data,服务器响应200 ok);而get只产生一个tcp数据包。(firefox的post只产生一个数据包)
          • 后退/刷新时,get是是安全和幂等的,而post时 数据会被重新提交。在 HTTP 协议里,所谓的「安全」是指请求方法不会「破坏」服务器上的资源。所谓的「幂等」,意思是多次执行相同的操作,结果都是「相同」的。
          • 对参数的类型,get只接受ASCII字符,而post没有限制。
          本质区别
          • GET和POST最大的区别主要是GET请求是幂等性的,POST请求不是。这个是它们本质区别。
          • 幂等性是指一次和多次请求某一个资源应该具有同样的副作用。简单来说意味着对同一URL的多个请求应该返回同样的结果。

          9. HTTP缓存技术

          对于一些具有重复性的 HTTP 请求,比如每次请求得到的数据都一样的,我们可以把这对「请求-响应」的数据都缓存在本地,那么下次就直接读取本地的数据,不必在通过网络获取服务器的响应了,这样的话 HTTP/1.1 的性能肯定肉眼可见的提升。所以,避免发送 HTTP 请求的方法就是通过缓存技术。
          缓存技术有两种实现方式,分别是强制缓存和协商缓存。
          • 强制缓存
            • 强缓存指的是只要浏览器判断缓存没有过期,则直接使用浏览器的本地缓存,决定是否使用缓存的主动性在于浏览器这边。
              强缓存是利用Cache-Control​​和Expires​​这两个 HTTP 响应头部(Response Header)字段实现的,它们都用来表示资源在客户端缓存的有效期:
            • Cache-Control​, 是一个相对时间;
            • Expires​,是一个绝对时间;
            • 如果 HTTP 响应头部同时有 Cache-Control 和 Expires 字段的话,Cache-Control的优先级高于 Expires 。
              Cache-control 选项更多一些,设置更加精细,所以建议使用 Cache-Control 来实现强缓存。具体的实现流程如下:
            • 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response 头部加上 Cache-Control,Cache-Control 中设置了过期时间大小;
            • 浏览器再次请求访问服务器中的该资源时,会先通过请求资源的时间与 Cache-Control 中设置的过期时间大小,来计算出该资源是否过期,如果没有,则使用该缓存,否则重新请求服务器;
            • 服务器再次收到请求后,会再次更新 Response 头部的 Cache-Control。
          • 协商缓存
            • 协商缓存就是与服务端协商之后,通过协商结果来判断是否使用本地缓存。
              协商缓存可以基于两种头部来实现。
              第一种:请求头部中的 If-Modified-Since​ 字段与响应头部中的 Last-Modified​ 字段实现,这两个字段的意思是:
            • 响应头部中的 Last-Modified​:标示这个响应资源的最后修改时间;
            • 请求头部中的 If-Modified-Since​:当资源过期了,发现响应头中具有 Last-Modified 声明,则再次发起请求的时候带上 Last-Modified 的时间,服务器收到请求后发现有 If-Modified-Since 则与被请求资源的最后修改时间进行对比(Last-Modified),如果最后修改时间较新(大),说明资源又被改过,则返回最新资源,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 的优先级更高,也就是先会判断 Etag 是否变化了,如果 Etag 没有变化,然后再看 Last-Modified。
          【注意】,协商缓存这两个字段都需要配合强制缓存中 Cache-control 字段来使用,只有在未能命中强制缓存的时候,才能发起带有协商缓存字段的请求。
          notion image
          使用 ETag 字段实现的协商缓存的过程如下:
          • 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response 头部加上 ETag 唯一标识,这个唯一标识的值是根据当前请求的资源生成的;
          • 当浏览器再次请求访问服务器中的该资源时,首先会先检查强制缓存是否过期,如果没有过期,则直接使用本地缓存;如果缓存过期了,会在 Request 头部加上 If-None-Match 字段,该字段的值就是 ETag 唯一标识;
          • 服务器再次收到请求后,
            • 会根据请求中的 If-None-Match 值与当前请求的资源生成的唯一标识进行比较:
            • 如果值相等,则返回 304 Not Modified,不会返回资源;
            • 如果不相等,则返回 200 状态码和返回资源,并在 Response 头部加上新的 ETag 唯一标识;
          • 如果浏览器收到 304 的请求响应状态码,则会从本地缓存中加载资源,否则更新资源。

          10. cookie / session

          什么是 Cookie

          HTTP Cookie(也叫 Web Cookie或浏览器 Cookie)是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。通常,它用于告知服务端两个请求是否来自同一浏览器,如保持用户的登录状态。Cookie 使基于无状态的HTTP 协议记录稳定的状态信息成为了可能。
          Cookie 主要用于以下三个方面
          • 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
          • 个性化设置(如用户自定义设置、主题等)
          • 浏览器行为跟踪(如跟踪分析用户行为等)
          【PS:Cookie 被禁用怎么办?最常用的就是利用 URL 重写把 Session ID 直接附加在 URL 路径的后面】

          什么是 Session

          Session 代表着服务器和客户端一次会话的过程。Session 对象存储特定用户会话所需的属性及配置信息。这样,当用户在应用程序的 Web 页之间跳转时,存储在 Session 对象中的变量将不会丢失,而是在整个用户会话中一直存在下去。当客户端关闭会话,或者 Session 超时失效时会话结束。

          cookie和session是如何配合的?

          用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session ,请求返回时将此 Session 的唯一标识信息 SessionID 返回给浏览器,浏览器接收到服务器返回的 SessionID 信息后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名。
          当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 Cookie 信息,如果存在自动将Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。
          根据以上流程可知,SessionID是连接 Cookie 和 Session 的一道桥梁,大部分系统也是根据此原理来验证用户登录状态。

          Cookie和Session的区别?

          • 存储位置不同: Cookie 保存在客户端(浏览器),Session 保存在服务器端。
          • 存取方式的不同: Cookie 只能保存 ASCII,Session可以存任意数据类型,一般情况下我们可以在Session 中保持一些常用变量信息,比如说 UserId 等。
          • 有效期不同: Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭或者 Session 超时都会失效。
          • 隐私策略不同: Cookie 存储在客户端,比较容易遭到不法获取,早期有人将用户的登录名和密码存储在 Cookie 中导致信息被窃取;Session 存储在服务端,安全性相对 Cookie 要好一些。
          • 存储大小不同: 单个Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie。

          11. 分布式session

          一般会有以下几种解决方案:
          • 客户端存储:直接将信息存储在cookie中,cookie是存储在客户端上的一小段数据,客户端通过http协议和服务器进行cookie交互,通常用来存储一些不敏感信息
          • Nginx ip_hash 策略:服务端使用 Nginx 代理,每个请求按访问 IP 的 hash 分配,这样来自同一IP 固定访问一个后台服务器,避免了在服务器 A 创建 Session,第二次分发到服务器 B 的现象。
          • Session 复制:任何一个服务器上的 Session 发生改变(增删改),该节点会把这个 Session 的所有内容序列化,然后广播给所有其它节点。
          • 共享 Session:服务端无状态化,将用户的 Session 等信息使用缓存中间件(如Redis)来统一管理,保障分发到每一个服务器的响应结果都一致。
          • 建议采用共享 Session的方案

          11-plus. JWT

          JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在各种各样的网络应用中简洁、安全地传输信息。JWT 通常用于在客户端和服务器之间传递身份验证和授权信息。这些信息以 JSON 对象的形式存在,经过签名和编码处理后,具有较高的安全性。JWT 主要由三部分组成:头部(Header)、负载(Payload)和签名(Signature)。
          1. 头部(Header):头部包含了 token 的元数据,如加密算法、token 类型等。它是一个 JSON 对象,经过 Base64Url 编码后得到一个字符串。
          1. 负载(Payload):负载部分包含了实际需要传输的数据。这些数据被称为声明(Claims),分为三种:公共声明、私有声明和注册声明。负载也是一个 JSON 对象,经过 Base64Url 编码后得到一个字符串。
          1. 签名(Signature):签名是将头部和负载拼接后,使用服务器的密钥(Secret)进行签名。这一过程
          1. 确保了 JWT 的完整性和安全性。签名可以防止 JWT 数据被篡改,从而确保数据的真实性。签名过程通常使用 HMAC 算法或者 RSA 签名算法。
            1. 将这三部分用 "." 分隔连接起来,就构成了一个完整的 JWT。它的结构如下:
              当用户登录时,服务器会根据用户的凭据(如用户名和密码)验证用户的身份。如果验证成功,服务器将生成一个 JWT,并将其作为响应发送给客户端。之后,客户端可以在每次请求时携带这个 JWT。服务器收到请求后,会检查并验证 JWT。如果验证通过,服务器将允许客户端访问受保护的资源。

          JWT 的优势

          1. 状态无关:服务器不需要存储会话信息,因为 JWT 包含了验证用户身份所需的所有数据。
          1. 可扩展性:JWT 可以在多个服务器、域名和应
          1. 用之间轻松共享,有助于实现单点登录(SSO)和跨域身份验证。 3. 自包含:JWT 本身就包含了用户身份和权限信息,因此不需要额外查询数据库。
          1. 安全性:通过签名机制保证了 JWT 数据的完整性和安全性,防止数据被篡改。

          JWT 的缺点

          1. 一旦 JWT 被颁发,就无法主动废止。若要废止 JWT,必须等待其过期或者在服务器端维护一个黑名单。
          1. 由于 JWT 包含用户的信息,如果 JWT 泄露,可能导致用户信息的暴露。因此,应避免在 JWT 中存储敏感信息。
          1. JWT 相较于简单的会话ID,数据量较大,因此在网络传输时可能会带来额外的开销。

          JWT的越权问题

          JWT 本身不会导致严重的越权问题,但如果使用不当,确实可能引发安全隐患。以下是一些建议,以帮助您在使用 JWT 时避免越权问题:
          1. 不要在 JWT 负载(Payload)中存储敏感信息。因为 JWT 可以被解码为 JSON 对象,如果有人获取了 JWT,他们可以轻易地查看负载中的信息。
          1. 选择合适的签名算法。建议使用非对称加密算法(如 RSA 或 ECDSA),因为这样可以确保私钥只存在于服务器端,而公钥用于验证 JWT。相比之下,对称加密算法(如 HMAC)使用同一个密钥(Secret)进行签名和验证,如果该密钥泄露,攻击者可能伪造有效的 JWT。
          1. 保护 JWT 的传输安全。使用 HTTPS 来传输 JWT,以防止中间人攻击。同时,遵循最佳实践,将 JWT 存储在客户端的 HttpOnly Cookie 中,以降低 XSS 攻击的风险。
          1. 设置合适的过期时间。为 JWT 设置一个较短的过期时间可以降低因 JWT 泄露导致的安全风险。当然,设置过短的过期时间会影响用户体验,因此需权衡二者之间的关系。
          1. 验证 JWT 时注意细节。在验证 JWT 时,务必核对签名、检查过期时间,并确保正确处理无效或过期的 JWT。
          1. 对 JWT 的权限进行细粒度控制。为不同的用户组分配不同的权限,确保用户只能访问其允许的资源。可在 JWT 负载中包含角色和权限信息,以便在验证 JWT 时确定用户的访问权限。
          1. 定期旋转密钥。定期更换签名密钥(特别是对称加密算法的密钥)可以降低密钥泄露的风险。在使用非对称加密算法时,也要确保私钥的安全保管。
          1. 实施监控和安全审计。定期检查 JWT 的使用情况
          1. 和相关日志,以便及时发现异常行为、安全漏洞或未授权访问。安全审计可以帮助您了解应用程序的安全状况,并确保在使用 JWT 时采取了适当的安全措施。
          1. 考虑使用额外的安全措施。例如,可以使用双因素身份验证(2FA)或多因素身份验证(MFA)进一步提高安全性。这可以确保即使 JWT 泄露,攻击者也无法轻易获得用户的访问权限。
          1. 及时修复漏洞。跟踪 JWT 相关的库、框架和工具的更新,确保在发现漏洞时及时进行修复。
          虽然 JWT 可能存在一定的安全风险,但如果遵循最佳实践并采取适当的安全措施,可以降低这些风险。总之,在使用 JWT 时,务必注意安全性和风险防范。

          注意

          JWT 使用的是签名算法,而不是加密算法。签名算法用于生成 JWT 的签名部分,以确保 JWT 在传输过程中不被篡改。签名算法的主要目的是验证数据的完整性和来源,而非对数据进行加密。对于 JWT,签名算法主要有以下两类:
          1. 对称签名算法(如 HMAC):这类算法使用同一个密钥(Secret)进行签名和验证。服务器在创建 JWT 时,使用密钥对头部(Header)和负载(Payload)进行签名。在验证 JWT 时,服务器使用相同的密钥重新计算签名,并将其与 JWT 中的签名进行比较。如果签名匹配,则说明 JWT 是有效的且未被篡改。
          1. 非对称签名算法(如 RSA 或 ECDSA):这类算法使用一对公钥和私钥。服务器在创建 JWT 时,使用私钥对头部和负载进行签名。在验证 JWT 时,服务器使用公钥重新计算签名,并将其与 JWT 中的签名进行比较。如果签名匹配,则说明 JWT 是有效的且未被篡改。由于公钥和私钥是不同的,私钥泄露的风险相对较小。
          实际上,当我们谈论 JWT 使用哈希算法时,是指用于计算签名的哈希算法。例如,HMAC-SHA256 是一种常见的对称签名算法,它结合了哈希函数(SHA-256)和密钥(Secret)来生成签名。同样,在非对称签名算法中,例如 RSA 或 ECDSA,也会涉及到哈希函数的使用,以确保生成的签名具有较高的安全性和完整性。
          简而言之,JWT 使用哈希算法并非为了加密数据,而是为了生成签名以确保数据的完整性和来源。在使用 JWT 时,应根据具体的应用场景和安全需求,选择合适的签名算法。

          越权问题

          • 问题:那如果我作为恶意的攻击者,我拿到了一个jwt,然后如果它使用的是哈希算法作为签名算法,我是不是可以直接通过修改里边的例如用户名的参数然后手动计算对应的哈希值写入签名的部分就可以实现越权的操作了,如果是的话应该如何防御这种攻击
          • 答案:
            • 如果您作为恶意攻击者拿到了一个使用哈希算法(例如 HMAC-SHA256)作为签名算法的 JWT,您不能简单地通过修改负载(例如用户名)并重新计算哈希值来实现越权操作,因为您需要知道用于签名的密钥(Secret)。这个密钥在服务器端保管,不应该被泄露给外部。
              对于对称签名算法,例如 HMAC-SHA256,服务器会用密钥(Secret)对头部(Header)和负载(Payload)进行签名。如果您不知道密钥,就无法生成正确的签名。当服务器收到 JWT 时,会用相同的密钥重新计算签名,并与 JWT 中的签名进行比较。如果签名不匹配,服务器会拒绝此次请求。
              要防御这种攻击,应该采取以下措施:
              1. 保护签名密钥:确保服务器端的密钥(Secret)不会泄露。如果密钥被泄露,攻击者可以伪造有效的 JWT。密钥应该具有足够的长度和复杂度,以降低被猜测的风险。同时,定期更换密钥也是一个很好的做法。
              1. 考虑使用非对称签名算法:如 RSA 或 ECDSA。这类算法使用一对公钥和私钥。服务器在创建 JWT 时,使用私钥进行签名;在验证 JWT 时,使用公钥进行验证。由于公钥和私钥是不同的,私钥泄露的风险相对较小。
              1. 传输安全:使用 HTTPS 来传输 JWT,以防止中间人攻击。这可以确保 JWT 在传输过程中不会被截获。
              1. 存储安全:将 JWT 存储在客户端的 HttpOnly Cookie 中,以降低跨站脚本攻击(XSS)的风险。避免将 JWT 存储在易受攻击的位置,如 JavaScript 可访问的 Local Storage 或 Session Storage。

          密钥安全问题

          对于来自同一个服务端的 JWT,通常使用相同的密钥进行签名。这是因为对于每个分发出去的 JWT 都使用不同的密钥会导致密钥管理变得复杂。服务端需要存储每个 JWT 对应的密钥,并在验证 JWT 时找到正确的密钥,这会增加开发复杂度和服务器负担。
          当使用相同的密钥对来自同一服务端的 JWT 进行签名时,可以通过以下措施保证安全性:
          1. 保护签名密钥:确保密钥(Secret)不会泄露,密钥应具有足够的长度和复杂度,以降低被猜测的风险。定期更换密钥也是一个很好的做法。
          1. 限制 JWT 的有效期:为 JWT 设置一个较短的过期时间,以降低因密钥泄露导致的安全风险。较短的过期时间意味着攻击者即使窃取了有效的 JWT,也只能在有限的时间内使用。当然,设置过短的过期时间可能会影响用户体验,因此需要在安全性和用户体验之间进行权衡。
          1. 使用非对称签名算法:如 RSA 或 ECDSA。非对称签名算法使用一对公钥和私钥,服务端用私钥签名 JWT,用公钥进行验证。这样,即使公钥被泄露,攻击者仍无法伪造有效的 JWT。
          1. 强制使用 HTTPS:确保 JWT 在传输过程中不会被截获。通过使用 HTTPS,可以防止中间人攻击,保护 JWT 的传输安全。
          1. 存储安全:在客户端将 JWT 存储在 HttpOnly Cookie 中,以降低跨站脚本攻击(XSS)的风险。避免将 JWT 存储在易受攻击的位置,如 JavaScript 可访问的 Local Storage 或 Session Storage。
          1. 监控和安全审计:定期检查 JWT 的使用情况和相关日志,以发现异常行为、安全漏洞或未授权访问。安全审计可以帮助了解应用程序的安全状况,并确保在使用 JWT 时采取了适当的安全措施。
          1. 细粒度的权限控制:在 JWT 负载中包含角色和权限信息,以便在验证 JWT 时确定用户的访问权限。为不同的用户组分配不同的权限,确保用户只能访问其允许的资源。通过这种方式,即使攻击者窃取了一个有效的 JWT,他们也受到权限的限制,无法实现完全的越权操作。
          1. 使用额外的安全措施:例如双因素身份验证(2FA)或多因素身份验证(MFA)以提高安全性。这些措施可以确保即使攻击者窃取了有效的 JWT,他们也需要其他验证因素才能访问受保护的资源。
          总之,虽然使用相同的密钥对来自同一服务端的 JWT 进行签名可能存在一定的安全风险,但遵循最佳实践并采取适当的安全措施可以降低这些风险。在使用 JWT 时,务必关注安全性和风险防范。

          密钥更新问题

          在实际开发过程中,确实会建议定期更换密钥,以降低密钥泄露的风险。然而,在更换密钥后,确实会出现之前分发的 JWT 全部失效的情况。为了平衡安全性和用户体验,可以采取以下策略来处理这个问题:
          1. 平滑过渡:在一段时间内,允许旧密钥和新密钥共存。当更换密钥后,服务器可以同时接受用旧密钥签名的 JWT 和用新密钥签名的 JWT。这段时间内,已经分发的 JWT 仍然有效,用户不会受到影响。过渡期结束后,服务器只接受用新密钥签名的 JWT,此时旧密钥签名的 JWT 将失效。
          1. 短期 JWT 和刷新令牌:将 JWT 的过期时间设置得较短(例如 15 分钟),并使用刷新令牌(Refresh Token)机制。当 JWT 过期时,客户端可以使用刷新令牌向服务器请求新的 JWT。在更换密钥后,已经分发的 JWT 会在较短时间内过期,从而降低旧密钥泄露的风险。刷新令牌本身可以设置较长的有效期,并在必要时进行更换。
          1. 提醒用户重新登录:在更换密钥后,可以通过通知或提示让用户重新登录,以获取用新密钥签名的 JWT。虽然这会影响用户体验,但可以确保系统的安全性。
          总之,在处理密钥更新时,需要在安全性和用户体验之间找到一个平衡点。根据实际情况和安全需求,可以采取不同的策略来处理密钥更新和 JWT 的失效问题。

          刷新令牌

          刷新令牌(Refresh Token)是一种特殊的令牌,用于在访问令牌(Access Token,通常是 JWT)过期时获取新的访问令牌。刷新令牌通常具有较长的有效期,并且可以在服务器端进行撤销。
          刷新令牌的工作流程如下:
          1. 用户使用用户名和密码(或其他凭证)登录系统。
          1. 服务器验证用户凭证后,颁发访问令牌和刷新令牌。
          1. 客户端使用访问令牌来访问受保护的资源。
          1. 访问令牌过期时,客户端使用刷新令牌向服务器请求新的访问令牌。
          1. 服务器验证刷新令牌的有效性,如果有效,则颁发新的访问令牌和(可选)新的刷新令牌。
          使用刷新令牌可以提高安全性,因为访问令牌具有较短的有效期,降低了泄露风险。同时,刷新令牌可以在服务器端进行撤销,从而控制用户的访问权限。
          然而,刷新令牌本身也可能面临安全风险。为了确保刷新令牌的安全性,可以采取以下措施:
          1. 存储安全:将刷新令牌存储在客户端的安全位置,例如 HttpOnly Cookie,以降低跨站脚本攻击(XSS)的风险。
          1. 传输安全:使用 HTTPS 传输刷新令牌,以防止中间人攻击。
          1. 审计和监控:定期检查刷新令牌的使用情况,以发现异常行为、安全漏洞或未授权访问。
          1. 限制刷新令牌的有效期:虽然刷新令牌通常具有较长的有效期,但不应将其设置为永不过期。根据具体场景和安全需求设置合适的有效期。
          1. 提供撤销机制:为服务器端提供撤销刷新令牌的功能,以便在用户登出、权限变更或安全事件发生时撤销刷新令牌。

          12. 负载均衡算法有哪些?

          多台服务器以对称的方式组成一个服务器集合,每台服务器都具有等价的地位,能互相分担负载。
          • 轮询法:将请求按照顺序轮流的分配到服务器上。大锅饭,不能发挥某些高性能服务器的优势。
          • 随机法: 随机获取一台,和轮询类似。
          • 哈希法: 通过ip地址哈希化来确定要选择的服务器编号。好处是,每次客户端访问的服务器都是同一个服务器,能很好地利用session或者cookie。
          • 加权轮询:根据服务器性能不同加权。

          13. Telnet / SSH

          Telnet(远程登陆协议) 通过一个终端登陆到其他服务器,建立在可靠的传输协议 TCP 之上。Telnet 协议的最大缺点之一是所有数据(包括用户名和密码)均以明文形式发送,这有潜在的安全风险。这就是为什么如今很少使用Telnet并被一种称为SSH的非常安全的协议所取代的主要原因。
          SSH(Secure Shell 安全的网络传输协议) 是目前较可靠,专为远程登录会话和其他网络服务提供安全性的协议。利用 SSH 协议可以有效防止远程管理过程中的信息泄露问题。SSH 建立在可靠的传输协议 TCP 之上。
          Telnet 和 SSH 之间的主要区别在于 SSH 协议会对传输的数据进行加密保证数据安全性。

          14. URI 和 URL 的区别是什么?

          • URI(Uniform Resource Identifier) 是统一资源标志符,可以唯一标识一个资源。
          • URL(Uniform Resource Locator) 是统一资源定位符,可以提供该资源的路径。它是一种具体的 URI,即 URL 可以用来标识一个资源,而且还指明了如何 locate 这个资源。
          URI 的作用像身份证号一样,URL 的作用更像家庭住址一样。URL 是一种具体的 URI,它不仅唯一标识资源,而且还提供了定位该资源的信息。

          15. IO多路复用 select、poll、epoll

          I/O多路复用是指:通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
          简述
          (1)select==>时间复杂度O(n)
          它仅仅知道了,有I/O事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),我们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。
          (2)poll==>时间复杂度O(n)
          poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态, 但是它没有最大连接数的限制,原因是它是基于链表来存储的.
          (3)epoll==>时间复杂度O(1)
          epoll可以理解为event poll,不同于忙轮询和无差别轮询,epoll会把哪个流发生了怎样的I/O事件通知我们。所以我们说epoll实际上是事件驱动(每个事件关联上fd)的,此时我们对这些流的操作都是有意义的。(复杂度降低到了O(1))

          1)select

          它通过一个select()系统调用来监视多个文件描述符的数组,对socket进行扫描时是线性扫描,即采用轮询的方法,效率较低,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作。
          select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,事实上从现在看来,这也是它所剩不多的优点之一。
          缺点
          (1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大
          (2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大
          (3)select支持的文件描述符数量太小了,默认是1024(不过可以通过修改宏定义甚至重新编译内核的方式提升这一限制)
          另外,select()所维护的存储大量文件描述符的数据结构,随着文件描述符数量的增大,其复制的开销也线性增长。同时,由于网络响应时间的延迟使得大 量TCP连接处于非活跃状态,但调用select()会对所有socket进行一次线性扫描,所以这也浪费了一定的开销。

          2)poll

          poll的机制与select类似,与select在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是poll没有最大文件描述符数量的限制。poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。
          另外,select()和poll()将就绪的文件描述符告诉进程后,如果进程没有对其进行IO操作,那么下次调用select()和poll()的时候 将 再次报告这些文件描述符,所以它们一般不会丢失就绪的消息,这种方式称为水平触发(Level Triggered)。

          3)epoll

          epoll可以同时支持水平触发和边缘触发(Edge Triggered,只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发),理论上边缘触发的性能要更高一些,但是代码实现相当复杂。
          epoll同样只告知那些就绪的文件描述符,而且当我们调用epoll_wait()获得就绪文件描述符时,返回的不是实际的描述符,而是一个代表就绪描 述符数量的 值,你只需要去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里也使用了内存映射(mmap)技术,这样便彻底省掉了这些文件描述符在 系统调用时复制的开销。
          另一个本质的改进在于epoll采用基于事件的就绪通知方式。在select/poll 中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某 个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。

          4)总结

          (1)select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而epoll其实也需要调用epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪fd放入就绪链表中,并唤醒在epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的CPU时间。这就是回调机制带来的性能提升。
          (2)select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一次,而epoll只要一次拷贝,而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等待队列并不是设备等待队列,只是一个epoll内部定义的等待队列)。这也能节省不少的开销。

          17. 什么是阻塞IO / 非阻塞IO?

          IO操作包括:对硬盘的读写、对socket的读写以及外设的读写。
          当用户线程发起一个IO请求操作(下面以读请求操作为例),内核会去查看要读取的数据是否就绪,对于阻塞IO来说,如果数据没有就绪,则会一直在那等待,直到数据就绪;对于非阻塞IO来说,如果数据没有就绪,则会返回一个标志信息告知用户线程当前要读的数据没有就绪。当数据就绪之后,便将数据拷贝到用户线程,这样才完成了一个完整的IO读请求操作,也就是说一个完整的IO读请求操作包括两个阶段:
          1)查看数据是否就绪;
          2)进行数据拷贝(内核将数据拷贝到用户线程)。
          那么阻塞(blocking IO)和非阻塞(non-blocking IO)的区别就在于第一个阶段,如果数据没有就绪,在查看数据是否就绪的过程中是一直等待,还是直接返回一个标志信息。
          Java中传统的IO都是阻塞IO,比如通过socket来读数据,调用read()方法之后,如果数据没有就绪,当前线程就会一直阻塞在read方法调用那里,直到有数据才返回;
          而如果是非阻塞IO的话,当数据没有就绪,read()方法应该返回一个标志信息,告知当前线程数据没有就绪,而不是一直在那里等待。

          18. BIO、NIO、AIO的区别?

          BIO:同步阻塞,在服务器中实现的模式为一个连接一个线程。也就是说,客户端有连接请求的时候,服务器就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销。BIO一般适用于连接数目小且固定的架构,这种方式对于服务器资源要求比较高,而且并发局限于应用中,是JDK1.4之前的唯一选择,但好在程序直观简单,易理解。
          NIO:同步非阻塞,在服务器中实现的模式为一个请求一个线程,也就是说,客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到有连接IO请求时才会启动一个线程进行处理。NIO一般适用于连接数目多且连接比较短(轻操作)的架构,并发局限于应用中,编程比较复杂,从JDK1.4开始支持。
          AIO:异步非阻塞,在服务器中实现的模式为一个有效请求一个线程,也就是说,客户端的IO请求都是通过操作系统先完成之后,再通知服务器应用去启动线程进行处理。AIO一般适用于连接数目多且连接比较长(重操作)的架构,充分调用操作系统参与并发操作,编程比较复杂,从JDK1.7开始支持。
          应用场景:
          • BIO适⽤于连接数⼩,对服务器资源要求⾼,因为线程要开很多。
          • NIO适合于连接数多且数据连接⽐较短的架构,⽐如聊天服务器,弹幕系统等
          • AIO适⽤于连接数多且连接⽐较长的架构。充分调⽤操作系统参与并发操作。
          notion image
          notion image

          19. TCP拥塞控制

          传输过程中发送方维护一个叫做拥塞窗口的状态变量(cwnd),其值取决于网络拥塞程度,并动态变化。发送方将拥塞窗口作为发送窗口,即swnd=cwnd。传输时还有个慢开始门限ssthresh状态变量。
          当cwnd < ssthresh时,使用慢开始, 当cwnd > ssthresh时,停止使用慢开始改用拥塞避免, 当cwnd = ssthresh,既可以使用慢开始也可以使用拥塞避免算法。
          • 慢开始:每当过了一个RTT(数据报往返时间),cwnd = cwnd*2; 呈指数上升。当cwnd >= ssthresh时,就会进入“拥塞避免算法”。
          • 拥塞避免:当cwnd = ssthresh时,进入拥塞避免算法。每个传输轮次(RTT)cwnd只能线性加一,而不是像慢开始一样指数增长。 当网络发生拥塞时,把ssthresh值更新为拥塞前ssthresh的一半,cwnd重新设置为1,然后再执行慢开始算法。
          • 快重传:快重传要求接收方在收到一个失序的报文段后就立即发出重复确认,而不是等到自己发送数据时捎带确认。如果发送机接收到三个重复确认,它会立即重传丢失的数据段,而不是等该报文段的超时重传计时器超时再重传。
          • 快恢复:发送方一旦收到3个重复确认,就知道丢失了个别的报文段。于是不启动慢开始,而是执行快恢复算法。发送方将慢开始门限ssthresh值和拥塞窗口cwnd调整为当前拥塞窗口cwnd的一半,开始执行拥塞避免算法。也有的实现是将cwnd设置成ssthresh+3*MSS(3的意思是确认有3个数据包被收到了)

          20. ARP协议

          地址解析协议:根据IP地址获取MAC地址。
          工作原理
          ARP首先会发起一个请求数据包,数据包的首部包含了目标主机的IP地址,然后这个数据包会在链路层进行再次包装,生成以太网数据包,最终由以太网广播给子网内的所有主机,每一台主机都会接收到这个数据包,并取出标头里的IP地址,然后和自己的IP地址进行比较,如果相同就返回自己的MAC地址,如果不同就丢弃该数据包。ARP接收返回消息,以此确定目标机的MAC地址;与此同时,ARP还会将返回的MAC地址与对应的IP地址存入本机ARP缓存中并保留一定时间,下次请求时直接查询ARP缓存以节约资源。
          工作过程
          主机A的IP地址为192.168.1.1,MAC地址为0A-11-22-33-44-01;
          主机B的IP地址为192.168.1.2,MAC地址为0A-11-22-33-44-02;
          当主机A要与主机B通信时,地址解析协议可以将主机B的IP地址(192.168.1.2)解析成主机B的MAC地址,以下为工作流程:
          第1步:根据主机A上的路由表内容,IP确定用于访问主机B的转发IP地址是192.168.1.2。然后A主机在自己的本地ARP缓存中检查主机B的匹配MAC地址。
          第2步:如果主机A在ARP缓存中没有找到映射,它将询问192.168.1.2的硬件地址,从而将ARP请求帧广播到本地网络上的所有主机。源主机A的IP地址和MAC地址都包括在ARP请求中。本地网络上的每台主机都接收到ARP请求并且检查是否与自己的IP地址匹配。如果主机发现请求的IP地址与自己的IP地址不匹配,它将丢弃ARP请求。
          第3步:主机B确定ARP请求中的IP地址与自己的IP地址匹配,则将主机A的IP地址和MAC地址映射添加到本地ARP缓存中。
          第4步:主机B将包含其MAC地址的ARP回复消息直接发送回主机A。
          第5步:当主机A收到从主机B发来的ARP回复消息时,会用主机B的IP和MAC地址映射更新ARP缓存。本机缓存是有生存期的,生存期结束后,将再次重复上面的过程。主机B的MAC地址一旦确定,主机A就能向主机B发送IP通信了。

          21. 应用层报文怎么传输到另一个应用层?

          应用层数据(报文)向外发送时,数据是由最上面的应用层向下经过一层层封装后发送给物理层;而接收数据时,数据是由物理层向上经过一层层解封后发给应用层。**数据的封装和解封过程如下:
          数据的封装过程简介
          传输层及其以下的机制由内核提供, 应用层由用户进程提供, 应用程序对通讯数据的含义进行解释, 而传输层及其以下处理通讯的细节,将数据从一台计算机通过一定的路径发送到另一台计算机。 应用层数据通过协议栈发到网络上时,每层协议都要加上一个相对应的头部(header ),该过程称为封装封装过程如下图所示:
          notion image
          举例说明数据封装和解封装过程
          (1)从计算机 A 的应用层内网通软件向计算机 B 发出一个消息,生成数据;
          (2)请求从计算机 A 的应用层下到 计算机A 的传输层,传输层在上层数据前面加上 TCP报头,报头中包括目标端口以及源端口;
          (3)传输层数据下到网络层,计算机A 在网络层封装,源 IP地址为 计算机A地址,目标 IP地址为 计算机 B 地址;
          (4)计算机 A 将计算机B 的 IP 地址和子网掩码与自己做比对,可以发现 计算机 B 与自己处于相同的子网。所以数据传输不必经过网关设备;
          (5)数据包下到 计算机 A 的数据链路层进行封装,源 MAC 地址为 计算机A的 MAC地址,目标 MAC 地址查询自己的 ARP 表。
          (6)计算机 A 把帧转换成 bit 流,从物理接口网卡发出;
          (7)物理层接收到电信号,把它交给数据链路层进行查看帧的目标 MAC 地址,和自己是否相等,如果相等说明该帧是发送给自己的,于是将MAC帧头解开并接着上传到网络层;
          (8)网络层查看目标 IP 地址和自己是否匹配,如果匹配即解开 IP 头封装。然后再把数据上传到传输层;
          (9)传输层解开对应的包头之后,继续把数据传给应用层,计算机 B 即可接收到计算机 A 发的消息。
          数据包在不同层的称谓
          不 同 的 协 议 层 对 数 据 包 有 不 同的 称 谓 ,在 传 输 层 叫 做 段(segment ),在网络层叫做数据报( datagram) ,在链路层叫做帧(frame )。数据封装成帧后发到传输介质上,到达目的主机后,每层协议再剥掉相应的头部,最后将应用层数据交给应用程序处理,该过程称为解封。
          报文、报文段、分组、包、数据报、帧、数据流的概念区别如下:
          1. 报文(message) 我们将位于应用层的信息分组称为报文。报文是网络中交换与传输的数据单元,也是网络传输的单元。报文包含了将要发送的完整的数据信息,其长短不需一致。报文在传输过程中会不断地封装成分组、包、帧来传输,封装的方式就是添加一些控制信息组成的首部,那些就是报文头。
          1. 报文段(segment) 通常是指起始点和目的地都是传输层的信息单元。
          1. 分组/包(packet) 分组是在网络中传输的二进制格式的单元,为了提供通信性能和可靠性,每个用户发送的数据会被分成多个更小的部分。在每个部分的前面加上一些必要的控制信息组成的首部,有时也会加上尾部,就构成了一个分组。它的起始和目的地是网络层。
          1. 数据报(datagram) 面向无连接的数据传输,其工作过程类似于报文交换。采用数据报方式传输时,被传输的分组称为数据报。通常是指起始点和目的地都使用无连接网络服务的的网络层的信息单元。
          1. 帧(frame) 帧是数据链路层的传输单元。它将上层传入的数据添加一个头部和尾部,组成了帧。它的起始点和目的点都是数据链路层。
          1. 数据单元(data unit) 指许多信息单元。常用的数据单元有服务数据单元(SDU)、协议数据单元(PDU)。 SDU是在同一机器上的两层之间传送信息。PDU是发送机器上每层的信息发送到接收机器上的相应层(同等层间交流用的)。

          22. 重定向和请求转发有什么区别?

          请求转发
          客户首先发送一个请求到服务器端,服务器端发现匹配的servlet,并指定它去执行,当这个servlet执行完之后,它要调用getRequestDispacther()方法,把请求转发给指定的student_list.jsp,整个流程都是在服务器端完成的,而且是在同一个请求里面完成的,因此servlet和jsp共享的是同一个request,在servlet里面放的所有东西,在student_list中都能取出来,因此,student_list能把结果getAttribute()出来,getAttribute()出来后执行完把结果返回给客户端。整个过程是一个请求,一个响应。
          重定向
          客户发送一个请求到服务器,服务器匹配servlet,servlet处理完之后调用了sendRedirect()方法,立即向客户端返回这个响应,响应行告诉客户端你必须要再发送一个请求,去访问student_list.jsp,紧接着客户端收到这个请求后,立刻发出一个新的请求,去请求student_list.jsp,这里两个请求互不干扰,相互独立,在前面request里面setAttribute()的任何东西,在后面的request里面都获得不了。可见,在sendRedirect()里面是两个请求,两个响应。(服务器向浏览器发送一个302状态码以及一个location消息头,浏览器收到请求后会向再次根据重定向地址发出请求)
          二者区别
          (1)请求次数:重定向是浏览器向服务器发送一个请求并收到响应后再次向一个新地址发出请求,转发是服务器收到请求后为了完成响应跳转到一个新的地址;重定向至少请求两次,转发请求一次;
          (2)地址栏不同:重定向地址栏会发生变化,转发地址栏不会发生变化;
          (3)是否共享数据:重定向两次请求不共享数据,转发一次请求共享数据(在request级别使用信息共享,使用重定向必然出错);
          (4)跳转限制:重定向可以跳转到任意URL,转发只能跳转本站点资源;
          (5)发生行为不同:重定向是客户端行为,转发是服务器端行为。

          23. URI和URL的区别是什么?

          URI(Uniform Resource Identifier) 是统⼀资源标识符,可以唯⼀标识 ⼀个资源
          • URI的格式(协议方案名、服务器地址是必选)
          notion image
          URL(Uniform Resource Location) 是统⼀资源定位符,可以提供该资源的路径。它是⼀种具体的 URI,即 URL 可以⽤来标识⼀个资源⽽且还指明了如何 locate 这个资源。
          URI的作⽤像身份证号⼀样,URL的作⽤更像家庭住址⼀样。URL是⼀种具体的URI,它不仅唯 ⼀标识资源,⽽且还提供了定位该资源的信息

          24. HTTP系列

          1. HTTP请求和响应报文

          notion image
          notion image

          HTTP 的方法

          notion image

          25. IPV4首部

          notion image
          • 版本:由4比特构成,表示标识IP首部的版本号。IPv4的版本号即为4,因此在这个字段上的值也是“4”。
          • 首部长度:由4比特构成,表明IP首部的大小,单位为4字节(32比特)。对于没有可选项的IP包,首部长度则设置 为“5”。也就是说,当没有可选项时,IP首部的长度为20字节(4×5=20)。
          • 区分服务:由8比特构成,用来表明服务质量。用0、1、2这三位表示0~7的优先度。从0到7表示优先度从低到高。
          notion image
          • 总长度:表示IP首部与数据部分合起来的总字节数。该字段长16比特。因此IP包的最大长度为65535(=2^16 )字节。
          • 标识:由16比特构成,用于分片重组。同一个分片的标识值相同,不同分片的标识值不同。通常,每发送一个 IP包,它的值也逐渐递增。此外,即使ID相同,如果目标地址、源地址或协议不同的话,也会被认为是不同 的分片。
          • 标志:由3比特构成,表示包被分片的相关信息。
          notion image
          • 片偏移:由13比特构成,用来标识被分片的每一个分段相对于原始数据的位置。第一个分片对应的值为0。
          • 生存时间:由8比特构成,它最初的意思是以秒为单位记录当前包在网络上应该生存的期限。然而,在实际中它是 指可以中转多少个路由器的意思。每经过一个路由器,TTL会减少1,直到变成0则丢弃该包(TTL占8位,因 此可以表示0~255的数字。因此一个包的中转路由的次数不会超过2 8 =256个。由此可以避免IP包在网络内 无限传递的问题。) 。
          • 协议:由8比特构成,表示IP首部的下一个首部隶属于哪个协议。(TCP 6、UDP 17、IP 4)
          • 首部校验和:由16比特(2个字节)构成,也叫IP首部校验和。该字段只校验数据报的首部,不校验数据部分。它主要用来确保IP数据报不被破坏。
          • 源地址:32bit
          • 目标地址:32bit
          • 可选项:长度可变,通常只在进行实验或诊断时使用。该字段包含如下几点信息:安全级别、源路径、路径记录、时间戳。
          • 填充:也称作填补物。在有可选项的情况下,首部长度可能不是32比特的整数倍。为此,通过向字段填充0, 调整为32比特的整数倍。
          • 数据:存入数据。将IP上层协议的首部也作为数据进行处理。

          HTTPS中间人攻击有可能伪造证书么?

          在HTTPS中,中间人攻击(Man-in-the-Middle,MITM)指的是攻击者在客户端和服务器之间截获、篡改或者监听通信内容。为了进行中间人攻击,攻击者需要伪造一个有效的证书来欺骗客户端。然而,在现代安全机制下,成功伪造有效证书的难度非常高。
          数字证书通常由一个权威的第三方(称为证书颁发机构,CA)颁发给服务器。当客户端与服务器建立HTTPS连接时,服务器会提供一个由CA签名的数字证书。客户端会验证证书颁发者(CA)的签名以确保证书的合法性。大多数现代浏览器和操作系统都内置了受信任的CA列表,因此客户端能够确认颁发证书的CA是否值得信任。
          攻击者要伪造一个有效的证书,需要完成以下至少一项任务:
          1. 接管一个受信任的CA,用其签发伪造的证书。这在实践中非常困难,因为CA会采取严格的安全措施来保护其私钥不被泄露。
          1. 找到一个受信任的CA,并使其误签发一个针对目标网站的证书。这在实践中也很困难,因为CA有严格的认证程序和审核机制。
          1. 利用浏览器或操作系统的漏洞,使客户端接受伪造的证书。这种情况较为罕见,通常会在发现后被迅速修复。
          尽管伪造有效证书的难度很高,但HTTPS仍然可能受到MITM攻击。一些可能的攻击方式包括:
          1. 证书透明度(Certificate Transparency)问题:一些攻击者可能利用证书透明度问题来篡改证书。
          1. 客户端信任被攻击者控制的CA:如果攻击者能够控制客户端环境,比如在企业网络内部,他们可能将自己的CA添加到受信任的CA列表,从而进行MITM攻击。
          1. 社会工程手段:攻击者可能通过钓鱼攻击、DNS劫持等手段,欺骗用户访问伪造的网站。
          总的来说,伪造有效证书的难度非常高,然而,HTTPS仍然可能受到MITM攻击。因此,用户和网站管理员需要保持警惕并采取有效的安全措施。
          Loading...

          尚未开始
          更新中
          近期核心
          已完结
          已弃更

          © River 2021-2025