计算机网络相关

张天宇 on 2020-07-06

计算机网络中的一些知识点总结。

计算机网络概述

计算机网络的组成

  • 从组成部分上看分为硬件、软件、协议
  • 从工作方式上看分为边缘部分和核心部分
  • 从功能组成上看分为通信子网和资源子网

计算机网络服务类型

  • 面向连接/无连接
  • 有应答/无应答
  • 可靠/不可靠

主机间通信方式

  • 客户-服务器 (C/S)
  • 对等 (P2P)

计算机网络体系结构

  • 应用层:通过应用进程间的交互来完成特定网络应用。应用层协议定义的是应用进程(进程:主机中正在运行的程序)间的通信和交互的规则。对于不同的网络应用需要不同的应用层协议。在互联网中应用层协议很多,如域名系统 DNS,支持万维网应用的 HTTP 协议,支持电子邮件的 SMTP 协议等等。我们把应用层交互的数据单元称为报文。
    • HTTP,超文本传输协议
    • DNS,域名系统,本质上是一个分布式数据库
    • FTP,文件传输协议
    • TFTP,简单文件传输协议
    • SMTP,简单邮件传输协议
  • 运输层:负责两个主机进程之间的通信服务,主要使用 TCP (Transmission Control Protocal,传输控制协议,面向连接,数据传输的单位是报文段,能够提供可靠的交付)和 UDP (User Datagram Protocol,用户数据包协议,无连接,数据传输单位是用户数据报,不保证提供可靠交付,只能提供“尽最大努力交付”)。
  • 网络层:为分组交换网上的不同主机提供通信服务,主要将数据链路层接受的数据进行IP地址的封装和解封装,工作设备为路由器,这一层的数据叫数据报。
  • 数据链路层:主要将从物理层接受的数据进行 Mac 地址(网卡地址)的封装和解封装,这一层工作的设备为交换机,这层的数据叫帧。
  • 物理层:主要定义物理设备标准,这层的数据叫做比特。

TCP / IP 网络体系结构

TCPIP网络体系结构

  • 数据链路层

    负责帧数据的传递。两个常用协议 ARP(地址解析协议) 和 RARP(逆地址解析协议),实现了 IP 地址和物理地址的相互转换。

  • 网络层

    实现数据包的选路和转发,网络层的任务就是选择中间节点,已确定两台主机之间的通讯路径。同时,网络层对上层协议隐藏了网络拓扑连接的细节,使得在传输层和网络应用程序看来,通讯的双方是直接相连的。

  • 传输层

    负责传输数据的控制(准确性、安全性), 传输层为两台主机上的应用程序提供端到端(end to end)的通信。与网络层使用的逐跳通信方式不同,传输层只关心通信的起始端和目的端,而不在乎数据包的中转过程。TCP 协议、UDP协议、SCTP协议。

  • 应用层

    负责数据的展示和获取。数据链路层、网络层、传输层负责处理网络通信细节,这部分必须既稳定又高效,因此它们都在内核空间中实现。而应用层则在用户空间中实现,因为它负责处理众多逻辑,比如文件传输、名称查询和网络管理等。如果应用层也在内核中实现,则会让内核变的十分庞大。当然,也有少数服务器程序是在内核中实现的,这样代码就无须在用户空间和内核空间来回切换(主要是数据的复制),极大地提高了工作效率。不过这种代码实现起来较复杂,不够灵活且不便于移植。

IP 层中的各种协议

IP:网际协议 IP 是 TCP/IP 体系中两个最主要的协议之一,是 TCP/IP 体系结构网际层的核心。配套的有:

  • 地址解析协议 ARP(Address Resolution Protocol)
  • 网际控制报文协议 ICMP(Internet Control Message Protocol)
  • 网际组管理协议 IGMP(Internet Group Management Protocol)

ARP:网络层实现主机之间的通信,而链路层实现具体每段链路之间的通信。因此在通信过程中,IP 数据报的源地址和目的地址始终不变,而 MAC 地址随着链路的改变而改变。

  • ARP 实现由 IP 地址得到 MAC 地址。 每个主机都有一个 ARP 高速缓存,里面有本局域网上的各主机和路由器的 IP 地址到 MAC 地址的映射表。如果主机 A 知道主机 B 的 IP 地址,但是 ARP 高速缓存中没有该 IP 地址到 MAC 地址的映射,此时主机 A 通过广播的方式发送 ARP 请求分组,主机 B 收到该请求后会发送 ARP 响应分组给主机 A 告知其 MAC 地址,随后主机 A 向其高速缓存中写入主机 B 的 IP 地址到 MAC 地址的映射。
  • ARP攻击第一步就是 ARP 欺骗。由上述“ARP协议的工作过程”我们知道,ARP 协议基本没有对网络的安全性做任何思考,当时人们考虑的重点是如何保证网络通信能够正确和快速的完成——ARP 协议工作的前提是默认了其所在的网络是一个善良的网络,每台主机在向网络中发送应答信号时都是使用的真实身份。不过后来,人们发现 ARP 应答中的 IP 地址和 MAC 地址中的信息是可以伪造的,并不一定是自己的真实 IP 地址和 MAC 地址,由此,ARP 欺骗就产生了。

ICMP:ICMP 是为了更有效地转发IP数据报和提高交付成功的机会,是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。它封装在 IP 数据报中,但是不属于高层协议。ICMP 报文分为差错报告报文和询问报文。Ping 是 ICMP 的一个重要应用,主要用来测试两台主机之间的连通性。Ping 的原理是通过向目的主机发送 ICMP Echo 请求报文,目的主机收到之后会发送Echo回答报文。Ping会根据时间和成功响应的次数估算出数据包往返时间以及丢包率。

IP 系列

Ping 命令做了什么?在哪一层?

Ping 是 ICMP 的一个重要应用,主要用来测试两台主机之间的连通性。Ping 的原理是通过向目的主机发送 ICMP Echo 请求报文,目的主机收到之后会发送Echo回答报文。Ping会根据时间和成功响应的次数估算出数据包往返时间以及丢包率。

网际控制报文协议——ICMP 可以更有效地转发IP数据报和提高交付成功的机会,是 TCP/IP 协议族的一个子协议,用于在 IP 主机、路由器之间传递控制消息。控制消息是指网络通不通、主机是否可达、路由是否可用等网络本身的消息。这些控制消息虽然并不传输用户数据,但是对于用户数据的传递起着重要的作用。它封装在 IP 数据报中,但是不属于高层协议。ICMP 报文分为差错报告报文和询问报文。

IP 地址和 Mac 物理地址

物理地址是数据链路层和物理层使用的地址;IP地址是网络层及其以上层使用的地址。

IP 地址:

  • 组成:网络号段和主机号段
  • 分为 ABC 三类

Mac 地址:

  • 位于数据链路层,通常是十二个十六进制位数

区别:

  • IP 地址是服务商给的,Mac 地址是网卡物理地址
  • IP 地址在局域网内可以随意更改,但是 Mac 地址一般不能更改
  • 长度不用,IP 地址为32位,Mac 地址为48位
  • 寻址协议层不同,IP 位于网络层,Mac 地址位语数据链路层

联系:

  • IP 的通讯依赖 MAC 地址,使用 ARP 协议凭借 MAC 地址进行通信

DHCP 工作原理

  • 寻找 Server

    当 DHCP 客户端第一次登录网络的时候,也就是客户发现本机上没有任何 IP 数据设定,它会向网络发出一个 DHCP DISCOVER 封包。因为客户端还不知道自己属于哪一个网络,所以封包的来源地址会为 0.0.0.0 ,而目的地址则为 255.255.255.255 ,然后再附上 DHCP discover 的信息,向网络进行广播。 在 Windows 的预设情形下,DHCP discover 的等待时间预设为 1 秒,也就是当客户端将第一个 DHCP discover 封包送出去之后,在 1 秒之内没有得到响应的话,就会进行第二次 DHCP discover 广播。若一直得不到响应的情况下,客户端一共会有四次 DHCP discover 广播(包括第一次在内),除了第一次会等待 1 秒之外,其余三次的等待时间分别是 9、13、16 秒。如果都没有得到 DHCP 服务器的响应,客户端则会显示错误信息,宣告 DHCP discover 的失败。之后,基于使用者的选择,系统会继续在 5 分钟之后再重复一次 DHCP discover 的过程。

  • 提供 IP 租用地址

  • 接受 IP 租约

  • 租约确认

TCP 和 UDP

面向无连接和面向连接的服务

TCP 提供面向连接的服务,在传送数据之前必须建立连接,数据传送结束后要释放连接。TCP 不提供组播或广播服务。由于 TCP 提供的是面向连接的可靠的传输服务,因此不可避免地增加了许多开销,如确认、流量控制、计时器以及连接管理等。这不仅使得协议数据单元的头部增大很多,还要占用很多处理机资源。因此 TCP 主要适用于可靠性要求高的场景,例如文件传输协议 FTP、超文本传输协议 HTTP、远程登录 TELNET 等。

UDP 是一个无连接的非可靠的传输层协议。它在 IP 之上只提供两个附加服务:分别是多路复用和对数据的错误检查。IP 知道怎么把分组投递到一台主机,但是不知道怎么把它们投递给主机上的具体的应用。UDP 在传送数据前不需要建立连接,远程主机的传输层接到 UDP 报文后也不需要给出任何确认。由于 UDP 协议比较简单,其执行速度快、实时性好。使用 UDP 的应用主要有 DNS、SNMP、RTP (实时协议)。

UDP 为什么实时性比较好?

UDP没有拥塞控制,因此网络中的拥塞也不影响到主机的发送频率。而某些实时应用要求以稳定的速率发送,能容忍一些数据的丢失,但是不允许有较大的时延,而UDP刚好满足这些应用的需求。

UDP 数据报的组成

UDP数据报包含两部分,分别是UDP首部和用户数据。整个UDP数据报作为IP数据报的数据部分封装在IP数据报中。UDP首部由8字节组成,分别是4个2字节的字段:

  • 16位源端口号
  • 16位目的端口号
  • 16位UDP长度
  • 16位UDP校验和

TCP 报文首部主要字段的含义

  • 源端口和目的端口,各占2个字节,分别写入源端口和目的端口;
  • 序号字段,占4字节。TCP连接中传送的数据流中的每个字节都有一个编号。序号字段的值指的是本报文段所发送的数据的第一个字节的序号。
  • 确认号(ack),占4个字节,是期望收到对方下一个报文的第一个数据字节的序号。例如,B收到了A发送过来的报文,其序列号字段是501,而数据长度是200字节,这表明B正确的收到了A发送的到序号700为止的数据。因此,B期望收到A的下一个数据序号是701,于是B在发送给A的确认报文段中把确认号置为701;
  • 数据偏移(即首部长度),占4位,它指出TCP报文段的数据起始处距离TCP报文段的起始处有多远当该字段值为15时,TCP首部达到最大的60字节;
  • 保留字段,占6位。保留今后使用,目前置零;
  • 紧急位URG,让URG为1时表明紧急指针字段有效。它告诉系统此报文段中有紧急数据,应当尽快传送
  • 确认ACK,仅当ACK=1时,确认号字段才有效。TCP规定,在连接建立后所有报文的传输都必须把ACK置1;
  • 推送PSH,当两个应用进程进行交互式通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应,这时候就将PSH=1;
  • 复位RST,当RST=1,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立连接;
  • 同步SYN,在连接建立时用来同步序号。当SYN=1,ACK=0,表明是连接请求报文,若同意连接,则响应报文中应该使SYN=1,ACK=1;
  • 终止FIN,用来释放连接。当FIN=1,表明此报文的发送方的数据已经发送完毕,并且要求释放;
  • 校验和,占两个字节,检验和字段检验的范围包括首部和数据两部分
  • 紧急指针字段,占16位,指出本报文段中有多少字节的紧急数据
  • 选项字段,长度可变,TCP最初只规定了一种选项,即最大报文段长度——MSS。MSS是TCP报文段中数据字段的最大长度
  • 填充字段,为了使首部长度为4字节的整数倍

TCP 可靠传输的实现

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

简单来说,TCP 的任务是在 IP 层的不可靠的、尽力而为服务的基础上建立一种可靠数据传输服务。TCP 提供的可靠数据传输服务就是要尽可能地保证接收方进程从缓存区读出的字节流与发送方发出的字节流完全一致。TCP 使用了校验、序号、确认、重传等机制来达到这个目的。其中,TCP 的校验机制与 UDP 的校验机制一致。

  • 序号:TCP 为每个字节进行编号,而序号字段的值表示整个报文段所发送数据的第一个字节的序号。
  • 确认:TCP 首部的确认号是指期望对方的下一个报文段的数据的第一个字节的序号。其中 TCP 使用的是累计确认,即 TCP 只确认数据流中至第一个丢失为止的字节。例如接收方接到 0-2,与 6-9 的报文段,由于某种原因接收方没接到 3-5 的报文段,此时接收方仍在等待字节 3 的数据,因此接收方的下一个报文段将确认号设为 3。
  • 超时重传:TCP 每发送一个报文段,便对整个报文段设置一次计时器。只要计时器设置的重传时间到期却没收到确认,就要重传这一报文段。
  • 冗余 ACK:TCP 规定,每当比期望序号大的报文段到达时,发送一个冗余 ACK,指明下一个期望字节的序号。当发送方收到对同一个报文段的3个冗余 ACK 时,就可以认为自己跟在这个被确认报文段之后的报文段已经丢失。

TCP 发送缓存与接收缓存中的内容

发送缓存:

  • 发送应用程序传给发送方 TCP 准备发送的数据
  • TCP 已经发送尚未收到确认的数据

接收缓存:

  • 按序到达的但是未被接收应用程序读取的数据
  • 不按序到达的数据

ARQ 协议

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

停等 ARQ 协议

停止等待协议是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认(回复 ACK)。如果过了一段时间(超时时间后),还是没有收到 ACK 确认,说明没有发送成功,需要重新发送,直到收到确认后再发下一个分组;在停止等待协议中,若接收方收到重复分组,就丢弃该分组,但同时还要发送确认。

连续 ARQ 协议

其实现的基础是建立在滑动窗口之上。而滑动窗口乃是 TCP 的精髓所在。连续 ARQ 规定,发送方每收到一个确认就将滑动窗口向前(时间增大方向)滑动一格。如上图表示收到一个确认。接受方采用累积确认的方式:接收方不必每收到一个消息,就发送一个确认。而是在收到几条消息后,对按序到达的最后一条消息发送确认。表示,这个消息之前的所有消息全部收到。

流量控制

TCP 提供了流量控制服务以消除发送方使接收方缓冲区溢出的可能性,因此可以说流量控制是一个匹配速度的服务,即匹配发送方的发送速率和接收方的接收速率。TCP 利用滑动窗口实现流量控制。具体实现如下:在通信中,接收方根据自己接收缓存的大小,动态地调整发送方的发送窗口大小,这就是接收窗口 rwnd,即调整TCP报文段首部中的窗口字段值,从而限制发送方向网络注入报文的速率。同时,发送方根据其对当前网络拥塞程序的估计而确定的窗口值,称为拥塞窗口 cwnd,其大小与网络的带宽和时延密切相关。实际上,发送方的窗口大小取值为 rwnd 与 cwnd 的最小值。若窗口字段设置为 0,则发送方不能发送数据。

拥塞控制

所谓拥塞控制就是防止过多的数据注入到网络中,这样就可以使得网络中的路由器或链路不致过载。当出现拥塞时,端点并不能了解到拥塞发生的细节,对于通信的端点来说,拥塞往往表现在通信时延的增加。为了更好地对传输层进行拥塞控制而采用了四种算法:

  • 慢开始
  • 拥塞避免
  • 快重传
  • 快恢复

慢开始算法开始运行后,每经过一个 RTT (报文段往返时间)时,拥塞窗口就会加倍,以指数趋势增长,直到达到阈值 S。

当采用慢开始后达到阈值 S 后,改用拥塞避免算法,每经过一个 RTT 后,拥塞窗口自增1,按照线性规律增长,当出现一次超时后,立刻将阈值S降为现在S的一半,也称乘法减,然后把拥塞窗口重置为1,开始执行慢开始算法。拥塞避免不可能完全实现网络拥塞,只是利用线性增长,降低出现拥塞的可能。

快重传和快恢复是对慢开始和拥塞避免算法的改进。快重传是当发送方连续收到3个重复的 ACK 报文时,直接重传对方未收到的报文段,而不必等待那个报文段设置的重传计时器超时。快恢复就是当发送方连续收到3个冗余 ACK 时,将阈值减半后,拥塞窗口并不直接设置为1,而是设置为阈值减半后的值,然后开始调用拥塞避免算法。值得注意的是,发送方窗口的大小由拥塞窗口和接收窗口共同决定。

拥塞控制和流量控制区别

拥塞控制是让网络能承受现有的网络负荷,是一个全局性的过程,涉及所有的主机、路由器,以及与降低网络传输性能有关的所有因素。相反,流量控制往往是指点对点的通信量控制,即接收端控制发送端,它所做的就是抑制发送端发送数据的速率,使得接收端来得及接收。

流量控制往往指在发送端和接收端之间的点对点通信量的控制。流量控制所要做的就是抑制发送端发送数据的速率,以便使接收端来得及接收。而拥塞控制必须确保子网能够传送待接收的数据,是一个全局性问题,涉及到网络中所有的主机、路由器以及导致网络传输能力下降的所有因素。

TCP 的三次握手

过程

  • TCP服务器进程先创建传输控制块TCB,时刻准备接受客户进程的连接请求,此时服务器就进入了LISTEN(监听)状态;
  • TCP客户进程也是先创建传输控制块TCB,然后向服务器发出连接请求报文,这是报文首部中的同部位SYN=1,同时选择一个初始序列号 seq=x ,此时,TCP客户端进程进入了 SYN-SENT(同步已发送状态)状态。TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号。
  • TCP服务器收到请求报文后,如果同意连接,则发出确认报文。确认报文中应该 ACK=1,SYN=1,确认号是ack=x+1,同时也要为自己初始化一个序列号 seq=y,此时,TCP服务器进程进入了SYN-RCVD(同步收到)状态。这个报文也不能携带数据,但是同样要消耗一个序号。
  • TCP客户进程收到确认后,还要向服务器给出确认。确认报文的ACK=1,ack=y+1,自己的序列号seq=x+1,此时,TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。
  • 当服务器收到客户端的确认后也进入ESTABLISHED状态,此后双方就可以开始通信了。

为什么需要三次握手?

三次握手的目的是建立可靠的通信信道。所谓通信,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的。

  • 第一次握手:Client 什么都不能确认;Server 确认了对方发送正常
  • 第二次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己接收正常,对方发送正常
  • 第三次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送、接收正常

为什么需要第三次握手?

综述:主要防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。

如果只有两次握手的情况:假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于 TCP 的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的。但是,两次握手的机制将会让客户端不予理睬而服务器认为连接已经建立而白白等待发送发方发送数据,这将导致不必要的错误和资源的浪费。

如果是三次握手的情况:就算是失效的报文传突然送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端已经下线而且不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接,因此无法建立连接。

为什么要传回 SYN,传递了 SYN 又为什么还要传递 ACK?

SYN 是 TCP/IP 建立连接时使用的握手信号。在客户机和服务器之间建立正常的 TCP 网络连接时,客户机首先发出一个 SYN 消息,服务器使用 SYN-ACK 应答表示接收到了这个消息,最后客户机再以 ACK 消息响应。这样在客户机和服务器之间才能建立起可靠的 TCP 连接,数据才可以在客户机和服务器之间传递。

双方通信无误必须是两者互相发送信息都无误。传了 SYN,证明发送方到接收方的通道没有问题,但是接收方到发送方的通道还需要 ACK 信号来进行验证。

TCP 的四次挥手

过程

  • 客户端进程发出连接释放报文,并且停止发送数据。释放数据报文首部,FIN=1,其序列号为 seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
  • 服务器收到连接释放报文,发出确认报文,ACK=1,ack=u+1,并且带上自己的序列号 seq=v,此时,服务端就进入了 CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个 CLOSE-WAIT 状态持续的时间。 客户端收到服务器的确认请求后,此时,客户端就进入 FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
  • 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为 seq=w,此时,服务器就进入了 LAST-ACK(最后确认)状态,等待客户端的确认。
  • 客户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,而自己的序列号是 seq=u+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时 TCP 连接还没有释放,必须经过 2 *MSL(最长报文段寿命)的时间后,当客户端撤销相应的 TCB 后,才进入 CLOSED 状态。
  • 服务器只要收到了客户端发出的确认,立即进入 CLOSED 状态。同样,撤销 TCB 后,就结束了这次的 TCP 连接。可以看到,服务器结束 TCP 连接的时间要比客户端早一些。

为什么要四次挥手?

解释1:确保数据能够完成传输。当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。

解释2:任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了TCP连接。

为什么不采用三次挥手?

如果采用三次挥手,如果发送发不等待2MSL,则发送方发出的报文中途丢失后,连接方不能进入正常关闭状态,发送方由于已经关闭,也不可能重传报文使得接收方关闭。此外,发送方在发送最后一个报文后,在2MSL时间内本次连接所产生的所有报文一定会消失,这样接收方可能再发送前一次关闭的请求。

为什么建立连接是三次握手,关闭连接是四次挥手?

建立连接的时候, 服务器在 LISTEN 状态下,收到建立连接请求的 SYN 报文后,把 ACK 和 SYN 放在一个报文里发送给客户端。 而关闭连接时,服务器收到对方的 FIN 报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方后,再发送 FIN 报文给对方来表示同意现在关闭连接,因此,己方 ACK 和 FIN 一般都会分开发送,从而导致多了一次。

第四次挥手一直丢包怎么办?

第四次挥手失败,此时客户端的状态为 TIME_WAIT,会等待一段时间,服务器端状态仍然为 LAST_ACK,超时一段时间仍然没有响应的话,服务器端会再发起一次 FIN 包,告诉客户端服务器端也要断开连接的请求,客户端收到后会再次发生 ACK 包确认断开连接。所以 TIME_WAIT 状态就是用来重发可能丢失的 ACK 报文。

为什么最后客户端需要等待2MSL?

MSL (Maximum Segment Lifetime)—最大报文寿命,TCP 允许不同的实现可以设置不同的 MSL 值。

第一,保证客户端发送的最后一个 ACK 报文能够到达服务器,因为这个 ACK 报文可能丢失,站在服务器的角度看来,我已经发送了 FIN+ACK 报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个 2MSL 时间段内收到这个重传的报文,接着给出回应报文,并且会重启 2MSL 计时器。

第二,防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,在这个 2MSL 时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文。

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

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

UDP和TCP的区别

  • 是否面向连接
  • 传输是否可靠
  • UDP使用数据报文段传输;TCP使用字节流传输
  • UDP传输效率高,所需资源少;TCP传输效率慢,所需资源多
  • UDP首部字节为8;TCP首部字节为20-60
  • UDP可用于即时通信,例如QQ语音;TCP用于稳定通信,例如文件传输、邮件等
  • UDP支持1对1,1对多,多对1,多对多交互通信;TCP仅支持1对1通信(不支持广播、多播)

HTTP

HTTP 幂等性

HTTP方法的幂等性是指一次和多次请求某一个资源应该具有同样的副作用。

粘包和拆包

问题

TCP 是个“流”协议,所谓流,就是没有界限的一串数据。可以想想河里的流水,是连成一片的,其间并没有分界线。TCP 底层并不了解上层业务数据的具体含义,它会根据 TCP 缓冲区的实际情况进行包的划分,所以在业务上认为,一个完整的包可能会被 TCP 拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的 TCP 粘包和拆包问题。以两个数据包的发送为例,下面是可能发生的情况:

  • 服务端分两次读取到了两个独立的数据包,分别是 D1 和 D2,没有粘包和拆包
  • 服务端一次接收到了两个数据包,D1 和 D2粘合在一起,被称为TCP粘包
  • 服务端分两次读取到了两个数据包,第一次读取到了完整的 D1 包和 D2 包的部分内容,第二次读取到了 D2 包的剩余内容,这被称为 TCP 拆包
  • 服务端分两次读取到了两个数据包,第一次读取到了 D1 包的部分内容 D1_1,第二次读取到了 D1 包的剩余内容 D1_2 和 D2 包的整包

原因

  • 要发送的数据大于 TCP 发送缓冲区剩余空间大小,将会发生拆包
  • 待发送数据大于 MSS(最大报文长度),TCP 在传输前将进行拆包
  • 要发送的数据小于 TCP 发送缓冲区的大小,TCP 将多次写入缓冲区的数据一次发送出去,将会发生粘包
  • 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包

解决

  • 使用带消息头的协议、消息头存储消息开始标识及消息长度信息,服务端获取消息头的时候解析出消息长度,然后向后读取该长度的内容
  • 设置定长消息,服务端每次读取既定长度的内容作为一条完整消息
  • 设置消息边界,服务端从网络流中按消息编辑分离出消息内容
  • 使用更复杂的应用层协议

HTTP(Post) 传输大文件

一、数据压缩

通常浏览器在发送请求时都会带着“Accept-Encoding”头字段,里面是浏览器支持的压缩格式列表,例如gzip、deflate、br等,这样服务器就可以从中选择一种压缩算法,放进“Content-Encoding”响应头里,再把原数据压缩后发给浏览器。

缺点:gzip等压缩算法通常只对文本文件有较好的压缩率,而图片、音频视频等多媒体数据本身就已经是高度压缩的,再用gzip处理也不会变小(甚至还有可能会增大一点),所以它就失效了。

二、分块传输

大文件分解成多个小块,把这些小块分批发给浏览器,浏览器收到后再组装复原。

这种“化整为零”的思路在HTTP协议里就是“chunked”分块传输编码,在响应报文里用头字段“Transfer-Encoding: chunked”来表示,意思是报文里的body部分不是一次性发过来的,而是分成了许多的块(chunk)逐个发送。

分块传输也可以用于“流式数据”,例如由数据库动态生成的表单页面,这种情况下body数据的长度是未知的,无法在头字段“Content-Length”里给出确切的长度,所以也只能用chunked方式分块发送。

Transfer-Encoding: chunkedContent-Length这两个字段是互斥的,也就是说响应报文里这两个字段不能同时出现,一个响应报文的传输要么是长度已知,要么是长度未知(chunked)。

分块传输的编码规则:

1)每个分块包含两个部分,长度头和数据块;

2)长度头是以CRLF(回车换行,即\r\n)结尾的一行明文,用16进制数字表示长度;

3)数据块紧跟在长度头后,最后也用CRLF结尾,但数据不包含CRLF;

4)最后用一个长度为0的块表示结束,即“0\r\n\r\n”。

img

HTTP 头部文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Accept: 允许哪些媒体类型。
Accept-Charset: 允许哪些字符集。
Accept-Encoding: 允许哪些编码。
Accept-Language: 允许哪些语言。
Cache-Control: 缓存策略,如no-cache,详见官方文档。
Connection: 连接选项,例如是否允许代理。
Host: 请求的主机。
If-None-Match: 判断请求实体的Etag是否包含在If-None-Match中,如果包含,则返回304,使用缓存,见Etag。
If-Modified-Since: 判断修改时间是否一致,如果一致,则使用缓存,。 、
If-Match: 与If-None-Match相反。
If-Unmodified-Since: 与If-Modified-Since相反。
Referer: 表明这个请求发起的源头。
User-Agent: 这个大家相信应该很熟悉了,就是经常用来做浏览器检测的userAgent。
Cache-Control: 缓存策略,如max-age:100,详见官方文档。
Connection: 连接选项,例如是否允许代理。
Content-Encoding: 返回内容的编码,如gzip。
Content-Language: 返回内容的语言。
Content-Length: 返回内容的字节长度。
Content-Type: 返回内容的媒体类型,如text/html。
Data: 返回时间。
Etag: entity tag,实体标签,给每个实体生成一个单独的值,用于客户端缓存,与If-None-Match配合使用。
Expires: 设置缓存过期时间,Cache-Control也会相应变化。
Last-Modified: 最近修改时间,用于客户端缓存,与If-Modified-Since配合使用。
Pragma: 似乎和Cache-Control差不多,用于旧的浏览器。
Server: 服务器信息。
Vary: WEB服务器用该头部的内容告诉 Cache 服务器,在什么条件下才能用本响应所返回的对象响应后续的请求。假如源WEB服务器在接到第一个请求消息时,其响应消息的头部为:Content-Encoding: gzip; Vary: Content-Encoding那么 Cache 服务器会分析后续请求消息的头部,检查其 Accept-Encoding,是否跟先前响应的 Vary 头部值一致,即是否使用相同的内容编码方法,这样就可以防止 Cache 服务器用自己 Cache 里面压缩后的实体响应给不具备解压能力的浏览器。

HTTP 请求报文的组成部分

  • 请求头
  • 请求行
  • 请求体

请求头由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。请求头部通知服务器有关于客户端请求的信息,典型的请求头有:

  • User-Agent:产生请求的浏览器类型
  • Accept:客户端可识别的内容类型列表
  • Host:请求的主机名,允许多个域名同处一个 IP 地址,即虚拟主机
  • Connection:指定与连接相关的属性,如 Connection:Keep-Alive
  • Accept-Encoding:通知服务端可以发送的数据压缩格式

请求行由请求方法字段、URL 字段和 HTTP 协议版本字段3个字段组成,它们用空格分隔。例如,GET /index.html HTTP/1.1。HTTP协议的请求方法有 GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT。

请求体通常不在 GET 方法中使用,而是在 POST 方法中使用。POST 方法适用于需要客户填写表单的场合。与请求数据相关的最常使用的请求头是 Content-Type 和 Content-Length。

HTTP 响应报文的组成部分

  • 响应行
  • 响应头
  • 响应体
1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 200 OK
Date: Sat, 31 Dec 2005 23:59:59 GMT
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 122
<html>
<head>
<title>Wrox Homepage</title>
</head>
<body>
<!-- body goes here -->
</body>
</html>

响应行通过提供一个状态码来说明所请求的资源情况,状态码见下文。

响应头部和请求头部类似,为响应报文添加了一些附加信息,例如

  • Content-Type:响应正文的类型(是图片还是二进制字符串)
  • Content-Charset:响应正文使用的编码
  • Content-Language:响应正文使用的语言

HTTP 状态码

1XX:通知

1XX系列响应代码仅在与 HTTP 服务器沟通时使用。

  • 100 Continue-继续,请求者应当继续提出请求。服务器返回此代码表示已收到请求的第一部分,正在等待其余部分
  • 101 Switching Protocols-切换协议,切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议,例如,切换到HTTP的新版本协议
2XX: 成功

2XX系列响应代码表明操作成功了。

  • 200 OK-成功,表示服务器成功执行了客户端所请求的动作
  • 201 Created-已创建,请求成功并且服务器创建了新的资源
  • 202 Accepted-已接受,服务器已接受请求,但尚未处理
  • 203 Non-Authoritative Information-非授权信息,服务器已成功处理了请求,但返回的信息可能来自另一来源
  • 204 No Content-无内容,服务器成功处理了请求,但没有返回任何内容
3XX 重定向

3XX系列响应代码表明:客户端需要做些额外工作才能得到所需要的资源。它们通常用于 GET 请求。他们通常告诉客户端需要向另一个 URI 发送 GET 请求,才能得到所需的表示。那个 URI 就包含在 Location 响应报头里。

  • 301 Moved Permanently-永久移动:被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URI 之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。除非额外指定,否则这个响应也是可缓存的。对于某些使用 HTTP/1.0 协议的浏览器,当它们发送的POST请求得到了一个 301 响应的话,接下来的重定向请求将会变成 GET 方式。
  • 302 Found-发现:临时移动,要求客户端执行临时重定向。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。只有在 Cache-Control 或 Expires 中进行了指定的情况下,这个响应才是可缓存的。
  • 303 See Other-查看其它地址。请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码。
  • 304 Not Modified-未修改。自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容
  • 305 Use Proxy-使用代理。所请求的资源必须通过代理访问
  • 307 Temporary Redirect-临时重定向。在这种情况下,请求应该与另一个 URI 重复,但后续的请求应仍使用原始的 URI。与302相反,当重新发出原始请求时,不允许更改请求方法。例如,应该使用另一个 POST 请求来重复 POST 请求
  • 308 Permanent Redirect-永久重定向。请求和所有将来的请求应该使用另一个URI重复。 307和308重复302和301的行为,但不允许HTTP方法更改。

301,302是http1.0的内容,303、307、308是http1.1的内容。301和302本来在规范中是不允许重定向时改变请求方法的(将POST改为GET),但是许多浏览器却允许重定向时改变请求方法(这是一种不规范的实现)。301表示搜索引擎在抓取新内容的同时也将旧的网址交换为重定向之后的网址;302表示旧地址A的资源还在(仍然可以访问),这个重定向只是临时地从旧地址A跳转到地址B,搜索引擎会抓取新的内容而保存旧的网址。

永久是指原来访问的资源已经永久删除啦,客户端应该根据新的URI访问重定向。临时是指访问的资源可能暂时先用location的URI访问,但旧资源还在的,下次你再来访问的时候可能就不用重定向了。

4XX:客户端错误

这些响应代码表明客户端出现错误。不是认证信息有问题,就是表示格式或HTTP库本身有问题。客户端需要自行改正。

  • 400 Bad Request 这是一个通用的客户端错误状态,当其他4XX响应代码不适用时,就采用400。此响应代码通常用于服务器不理解请求的语法
  • 401 Unauthorized-未授权,请求要求身份验证。对于需要登录的网页,服务器可能返回此响应
  • 403 Forbidden-禁止 服务器拒绝请求。该响应代码常用于一个资源只允许在特定时间段内访问,或者允许特定IP地址的用户访问的情况。403暗示了所请求的资源确实存在。
  • 404 Not Found-未找到。404表明服务器无法把客户端请求的URI转换为一个资源即找不到资源
  • 405 Method Not Allowed-方法禁用,禁用请求中指定的方法
  • 407 Proxy Authentication Required-请求要求代理的身份认证,与401类似,但请求者应当使用代理进行授权
  • 408 Request Time-out,服务器等候请求时发生超时
  • 409 Conflict-冲突,请求的操作会导致服务器的资源处于一种不可能或不一致的状态
  • 410 Gone-已删除,如果请求的资源已永久删除,服务器就会返回此响应
5XX 服务端错误

这些响应代码表明服务器端出现错误。一般来说,这些代码意味着服务器处于不能执行客户端请求的状态,此时客户端应稍后重试。有时,服务器能够估计客户端应在多久之后重试。并把该信息放在Retry-After响应报头里。

  • 500 Internal Server Error-内部错误,服务器遇到错误,无法完成请求。对于大多数web框架,如果在执行请求处理代码时遇到了异常,它们就发送此响应代码
  • 501 Not Implemented-尚未实施,服务器不具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此代码
  • 502 Bad Gateway-错误网关,服务器作为网关或代理,从上游服务器收到无效响应
  • 503 Service Unavailable-服务不可用,服务器目前无法使用(由于超载或停机维护)
  • 504 Gateway Time-out-网关超时,服务器作为网关或代理,但是没有及时从上游服务器收到请求
  • HTTP Version not supported-HTTP版本不受支持,服务器不支持请求中所用的 HTTP 协议版本

HTTP 中的 Content-type 类型

  • HTML:text/html
  • JSON:application/json
  • JPG:application/x-jpg
  • JSP:text/html

长连接和短链接

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

长连接可以省去较多的 TCP 建立和关闭的操作,减少浪费,节约时间。对于频繁请求资源的客户端适合使用长连接。在长连接的应用场景下,client 端一般不会主动关闭连接,当 client 与 server 之间的连接一直不关闭,随着客户端连接越来越多,server 会保持过多连接。这时候 server 端需要采取一些策略,如关闭一些长时间没有请求发生的连接,这样可以避免一些恶意连接导致 server 端服务受损;如果条件允许则可以限制每个客户端的最大长连接数,这样可以完全避免恶意的客户端拖垮整体后端服务。短连接对于服务器来说管理较为简单,存在的连接都是有用的连接,不需要额外的控制手段。但如果客户请求频繁,将在 TCP 的建立和关闭操作上浪费较多时间和带宽。

  • 长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。数据库的连接用长连接。
  • 短链接多用于WEB网站的http服务,因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源。并发量大,但每个用户无需频繁操作情况下需用短连好。

HTTP 的无状态特性

HTTP 是一种不保存状态,即无状态(stateless)协议。也就是说 HTTP 协议自身不对请求和响应之间的通信状态进行保存。

解决

使用 Session 和 Cookie。Session 机制的存在就是为了解决这个问题,Session 的主要作用就是通过服务端记录用户的状态。典型的场景是购物车,当你要添加商品到购物车的时候,系统不知道是哪个用户操作的,因为 HTTP 协议是无状态的。服务端给特定的用户创建特定的 Session 之后就可以标识这个用户并且跟踪这个用户了(一般情况下,服务器会在一定时间内保存这个 Session,过了时间限制,就会销毁这个Session)。在服务端保存 Session 的方法很多,最常用的就是内存和数据库(比如是使用内存数据库 redis 保存)。既然 Session 存放在服务器端,那么我们如何实现 Session 跟踪呢?大部分情况下,我们都是通过在 Cookie 中附加一个 Session ID 来方式来跟踪。

HTTP 中 Get 和 Post 的区别

  • GET 会将参数暴露在 url 上,某种程度上不安全,而 POST 将参数放在 Request body 中
  • GET 在 url 中传送的参数长度有限制,POST 在 Request body 没有长度、数量限制
  • GET 请求参数会被完整保留在浏览器历史记录里,而 POST 中的参数不会被保留
  • GET 只能进行url编码,而 POST 可以支持多种编码方式
  • GET 请求会被浏览器主动 cache,而 POST 不会,除非手动设置
  • GET 请求参数会被完整保留在浏览器历史记录里,而 POST 中的参数不会被保留
  • GET 在浏览器回退时是无害的,而 POST 会再次提交请求

GET 和 POS 本质上都是 TCP 连接,上述区别的根本原因在于,HTTP 的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同。例如,大多数浏览器通常都会限制 url 长度在2K个字节,而大多数服务器最多处理64K大小的 url。

此外,GET 产生一个 TCP 数据包;POST 产生两个 TCP 数据包。对于 GET 方式的请求,浏览器会把 http header 和 data 一并发送出去,服务器响应200(返回数据);而对于 POST,浏览器先发送 header,服务器响应100 continue,浏览器再发送 data,服务器响应200 ok(返回数据)。

因此,应该注意:

  • GET 与 POST 都有自己的语义,不能随便混用。 据研究,在网络环境好的情况下,发一次包的时间和发两次包的时间差别基本可以无视。而在网络环境差的情况下,两次包的 TCP 在验证数据包完整性上,有非常大的优点。
  • 并不是所有浏览器都会在 POST 中发送两次包,Firefox 就只发送一次。

GET/POST/PUT/DELETE

  • GET 用于信息获取,而且应该是安全的和幂等的。
  • POST 表示可能修改变服务器上的资源的请求。
  • 1、POST /url 创建
    2、DELETE /url/xxx 删除
    3、PUT /url/xxx 更新
    4、GET /url/xxx 查看

HTTP 和 HTTPS 的区别

  • HTTPS 协议需要到 CA 申请证书,一般免费证书很少,需要交费。
  • HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
  • HTTPS 可以有效的防止运营商劫持,解决了防劫持的一个大问题。
  • HTTP 协议运行在 TCP 之上,所有传输的内容都是明文,HTTPS 运行在 SSL/TLS 之上,SSL/TLS 运行在 TCP 之上,所有传输的内容都经过加密的。
  • HTTP 安全性没有 HTTPS 高,但是 HTTPS 比 HTTP 耗费更多服务器资源。

SSL与TSL

  • SSL:(Secure Socket Layer) 安全套接层
  • TLS:(Transport Layer Security)传输层安全性协议

上述两种技术的出现是为了解决HTTP中存在的安全性问题。例如,使用明文通信,内容被窃听;不验证身份,身份可伪装;无法验证报文完整性,容易被篡改。

HTTPS=HTTP+TSL,并使用TSL作为安全保障,对传输内容加密并做身份验证。

CA

证书:全称公钥证书(Public-Key Certificate, PKC),里面保存着归属者的基本信息,以及证书过期时间、归属者的公钥,并由认证机构(Certification Authority, CA)施加数字签名,表明,某个认证机构认定该公钥的确属于此人。数字证书认证机构(CA,Certificate Authority)是客户端与服务器双方都可信赖的第三方机构。

服务器的运营人员向 CA 提出公开密钥的申请,CA 在判明提出申请者的身份之后,会对已申请的公开密钥做数字签名,然后分配这个已签名的公开密钥,并将该公开密钥放入公开密钥证书后绑定在一起。进行 HTTPS 通信时,服务器会把证书发送给客户端。客户端取得其中的公开密钥之后,先使用数字签名进行验证,如果验证通过,就可以开始通信了。

HTTPS 发起请求的流程

  1. 客户端发出加密通信请求 ClientHello

    客户端提供:

    • 协议版本(如TSL1.0)
    • 随机数1(用于生成对话密钥)
    • 支持的加密方法(如RSA公钥加密)
    • 支持的压缩方法
  2. 服务器回应 SeverHello

    采用 HTTPS 协议的服务器必须要有一套数字证书,可以是自己制作或者 CA 证书。区别就是自己颁发的证书需要客户端验证通过,才可以继续访问,而使用 CA 证书则不会弹出提示页面。这套证书其实就是一对公钥和私钥。公钥给别人加密使用,私钥给自己解密使用。服务器回应内容:

    • 确认使用的加密通信协议版本(TSL1.0)
    • 随机数2(用于生成对话密钥)
    • 确认加密方法(RSA)
    • 服务器证书(包含非对称加密的公钥)
    • (可选)要求客户端提供证书的请求
  3. 客户端解析证书

    这个证书其实就是公钥,只是包含了很多信息,如证书的颁发机构,过期时间等。如果证书不是可信机构颁布,或证书域名与实际域名不符,或者证书已经过期,就会向访问者显示一个警告,是否继续通信。证书的解析工作由客户端自己执行。如果证书没有问题,那么就生成一个随即值,然后用证书对该随机值进行加密。

  4. 客户端传送加密信息

    这部分传送的是用证书加密后的随机值,目的就是让服务端得到这个随机值,以后客户端和服务端的通信就可以通过这个随机值来进行加密解密了。发送内容为:

    • 随机数3(pre-master key,此随机数用服务器公钥加密,防止被窃听)
    • 编码改变通知(表示随后的信息都将用双方商定的方法和密钥发送)
    • 客户端握手结束通知
  5. 服务端解密信息

    服务端用私钥解密后,得到了客户端传过来的随机值(私钥),然后把内容通过该值进行对称加密。所谓对称加密就是,将信息和私钥通过某种算法混合在一起,这样除非知道私钥,不然无法获取内容,而正好客户端和服务端都知道这个私钥,所以只要加密算法够彪悍,私钥够复杂,数据就够安全。

  6. 生成会话秘钥

    双方同时有了三个随机数,接着就用事先商定的加密方法,各自生成同一把“会话密钥” 服务器端用自己的私钥(非对称加密的)获取第三个随机数,会计算生成本次所用的会话密钥(对称加密的密钥)。

  7. 传输加密后的信息

    这部分信息是服务端用私钥加密后的信息,可以在客户端被还原。

  8. 客户端解密信息

    客户端用之前生成的私钥解密服务段传过来的信息,于是获取了解密后的内容。

1)客户端向服务器传输客户端的SSL协议版本号,支持的加密算法的种类,产生的随机数Key1及其他信息

2)服务器在客户端发送过来的加密算法列表中选取一种,产生随机数Key2,然后发送给客户端

3)服务器将自己的证书发送给客户端

4)客户端验证服务器的合法性,服务器的合法性包括:证书是否过期,发行服务器证书的CA是否可靠,发行者的公钥能否正确解开服务器证书的”发行者的数字签名”,服务器证书上的域名是否和服务器的实际域名相匹配,如果合法性验证没有通过,通信将断开,如果合法性验证通过,将继续向下进行;

5)客户端随机产生一个Pre-Master-Key,然后用服务器的公钥(从证书中获得)对其加密,然后将该Pre-Master-Key发送给服务器

6)服务器接收到Pre-Master-Key,则使用协商好的算法(H)计算出真正的用户通信过程中使用的对称加密密钥Master-Key=H(C1+S1+PreMaster);

7)至此为止,服务器和客户端之间都得到Master-Key,之后的通信过程就使用Master-Key作为对称加密的密钥进行安全通信;

中间人攻击原理

针对SSL的中间人攻击方式主要有两类,分别是SSL劫持攻击和SSL剥离攻击

SSL劫持攻击

SSL劫持攻击即SSL证书欺骗攻击,攻击者为了获得HTTPS传输的明文数据,需要先将自己接入到客户端和目标网站之间;在传输过程中伪造服务器的证书,将服务器的公钥替换成自己的公钥,这样,中间人就可以得到明文传输带Key1、Key2和Pre-Master-Key,从而窃取客户端和服务端的通信数据;

但是对于客户端来说,如果中间人伪造了证书,在校验证书过程中会提示证书错误,由用户选择继续操作还是返回,由于大多数用户的安全意识不强,会选择继续操作,此时,中间人就可以获取浏览器和服务器之间的通信数据。

SSL剥离攻击

这种攻击方式也需要将攻击者设置为中间人,之后见HTTPS范文替换为HTTP返回给浏览器,而中间人和服务器之间仍然保持HTTPS服务器。由于HTTP是明文传输的,所以中间人可以获取客户端和服务器传输数据。

HTTPS 抓包的实现

常用抓包工具Fiddler和Charles抓取Https包的原理是相近的,都是做了一个中间转发。以Fiddler为例,对于客户端来讲,Fiddler就是服务器,对服务器来讲,Fiddler伪装客户端。详情来讲,对于C中提到的四个过程,Fiddler既做客户端,又做服务器。如何做到让客户端和服务器均信任呢?关键在于证书,客户端需要安装Fiddler的证书,这样,从客户端发往Fiddler的命令就是全透明的,其c.3中加密随机数R3也是用Fiddler的证书中的公钥进行的加密。Fiddler拿到客户端明文信息之后,以客户端的格式向服务器进行通信,拿到服务器下发的客户端所关心的数据,再以Fiddler与客户端协商的加密算法发给客户端即可。

URI 和 URL 的区别

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

HTTP 1.0 和 HTTP 2.0

HTTP 2.0 优化内容

  • 二进制分帧

    将所有传输信息分割为更小的消息和帧,并对它们采用二进制格式的编码将其封装。

  • 多路复用 / 连接共享

    在http1.1中,浏览器客户端在同一时间,针对同一域名下的请求有一定数量的限制,超过限制数目的请求会被阻塞。这也是为何一些站点会有多个静态资源 CDN 域名的原因之一。

  • 头部压缩

    http1.x的头带有大量信息,而且每次都要重复发送。http/2使用encoder来减少需要传输的header大小,通讯双方各自缓存一份头部字段表,既避免了重复header的传输,又减小了需要传输的大小。

  • 请求优先级

    把http消息分为很多独立帧之后,就可以通过优化这些帧的交错和传输顺序进一步优化性能。每个流都可以带有一个31比特的优先值:0 表示最高优先级;2的31次方-1 表示最低优先级。

  • 服务端推送

    服务器可以对一个客户端请求发送多个响应,服务器向客户端推送资源无需客户端明确地请求。并且,服务端推送能把客户端所需要的资源伴随着index.html一起发送到客户端,省去了客户端重复请求的步骤。

性能瓶颈

启用http2.0后会给性能带来很大的提升,但同时也会带来新的性能瓶颈。因为现在所有的压力集中在底层一个TCP连接之上,TCP很可能就是下一个性能瓶颈,比如TCP分组的队首阻塞问题,单个TCP packet丢失导致整个连接阻塞,无法逃避,此时所有消息都会受到影响。未来,服务器端针对http 2.0下的TCP配置优化至关重要。

HTTP 2.0 多路复用

img

图中第一种请求方式,就是单次发送request请求,收到response后再进行下一次请求,显然是很低效的。

于是http1.1提出了管线化(pipelining)技术,就是如图中第二中请求方式,一次性发送多个request请求。

然而pipelining在接收response返回时,也必须依顺序接收,如果前一个请求遇到了阻塞,后面的请求即使已经处理完毕了,仍然需要等待阻塞的请求处理完毕。这种情况就如图中第三种,第一个请求阻塞后,后面的请求都需要等待,这也就是队头阻塞(Head of line blocking)。

为了解决上述阻塞问题,http2中提出了多路复用(Multiplexing)技术。http2中将多个请求复用同一个tcp链接中,将一个TCP连接分为若干个流(Stream),每个流中可以传输若干消息(Message),每个消息由若干最小的二进制帧(Frame)组成。也就是将每个request-response拆分为了细小的二进制帧Frame,这样即使一个请求被阻塞了,也不会影响其他请求,如图中第四种情况所示。

由于 HTTP 是一种无状态的协议,服务器单从网络连接上无从知道客户身份。怎么办呢?就给客户端们颁发一个通行证吧,每人一个,无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是 Cookie 的工作原理。

Cookie 实际上是一小段的文本信息。客户端请求服务器,如果服务器需要记录该用户状态,就使用 response 向客户端浏览器颁发一个 Cookie。客户端浏览器会把 Cookie 保存起来。当浏览器再请求该网站时,浏览器把请求的网址连同该 Cookie 一同提交给服务器。服务器检查该 Cookie,以此来辨认用户状态。服务器还可以根据需要修改 Cookie 的内容。

  • 客户端发送一个http请求到服务器端
  • 服务器端发送一个http响应到客户端,其中包含 Set-Cookie 头部
  • 客户端发送一个http请求到服务器端,其中包含 Cookie 头部
  • 服务器端发送一个http响应到客户端

当服务器返回给客户端一个 Http 响应信息时,其中如果包含 Set-Cookie 这个头部,说明:

  • 指示客户端建立一个 Cookie.
  • 在后续的Http请求中自动发送这个 Cookie 到服务器端,直到这个 Cookie 过期。
  • 如果 Cookie 的生存时间是整个会话期间的话,那么浏览器会将 Cookie 保存在内存中, 浏览器关闭时就会自动清除这个 Cookie。
  • 如果将 Cookie 保存在客户端的硬盘中,浏览器关闭的话,该 Cookie 也不会被清除,下次打开浏览器访问对应网站时,这个 Cookie 就会自动再次发送到服务器端。
  • Cookie 具有不可跨域名性。 根据 Cookie 规范,浏览器访问 Google 只会携带 Google 的 Cookie,而不会携带 Baidu 的 Cookie。 Google 也只能操作 Google 的 Cookie,而不能操作 Baidu 的 Cookie。
  • Cookie 不支持中文,需要编码。
如何保证安全性,比如防拷贝?
  • 设置 Cookie 的有效期不要太长
  • 设置 HTTPOnly 属性为 true
  • 设置复杂的 Cookie 并加密:cookie 的 key 使用uuid,随机生成;cookie 的 value 可以使用复杂组合
  • 用户第一次登录时,保存 ip+cookie 加密后的 token
  • 同时使用 Session 和 Cookie
  • 如果网站支持 https,尽可能使用 https
被禁用了怎么办?

最常用的就是利用 URL 重写把 Session ID 直接附加在 URL 路径的后面。

Session

Session 是一种记录客户状态的机制,不同于 Cookie 的是 Cookie 保存在客户端浏览器中,而 Session 保存在服务器上。客户端浏览器访问服务器的时候,服务器把客户端信息以某种形式记录在服务器上。这就是 Session。客户端浏览器再次访问时只需要从该 Session 中查找该客户的状态就可以了。

创建与使用过程

Session 在服务器端程序运行的过程中创建的,不同语言实现的应用程序有不同创建 Session 的方法, 在 Java 中是通过调用 HttpServletRequest 的 getSession 方法(使用true作为参数)创建的。当一个用户第一次访问某个网站时会自动创建 HttpSession,每个用户可以访问他自己的 HttpSession。 创建 Session 的同时,服务器会为该 Session 生成唯一的 session id, 这个 session id 在随后的请求中会被用来重新获得已经创建的 Session。

Session 被创建之后,就可以调用 Session 相关的方法往 Session 中增加内容了, 而这些内容只会保存在服务器中,发到客户端的只有 session id。可以通过 HttpServletRequest 对象的 getSession 方法获得 HttpSession。 通过 HttpSession 的 setAttribute 方法可以将一个值放在 HttpSession 中, 通过调用 HttpSession 对象的 getAttribute 方法,同时传入属性名就可以获取保存在 HttpSession 中的对象。

当客户端再次发送请求的时候,会将这个 session id 带上,服务器接受到请求之后就会依据 session id 找到相应的 Session,从而再次使用 Session。

生命周期

Session 保存在服务器端。为了获得更高的存取速度,服务器一般把 Session 放在内存中。每个用户都会有一个独立的 Session。这个 Session 使用一个 Session ID 来标识这个 Session。如果 Session 内容过于复杂,当大量客户访问服务器时可能会导致内存溢出。因此, Session 里的信息应该尽量精简。

Session 在用户第一次访问服务器的时候自动创建。需要注意只有访问 JSP、Servlet 等程序时才会创建 Session,只访问 HTML、IMAGE 等静态资源并不会创建 Session。 如果尚未生成 Session,也可以使用 request.getSession(true) 强制生成 Session。

Session 生成后,只要用户继续访问,服务器就会更新 Session 的最后访问时间,并维护该 Session。用户每访问服务器一次,无论是否读写 Session,服务器都认为该用户的 Session ”活跃(active)”了一次。

例如,当浏览器第二次发送请求,会将前一次服务器响应中的 Session ID 放在请求中一并发送到服务器上,服务器从请求中提取出 Session ID,并和保存的所有 Session ID 进行对比,找到这个用户对应的 Session。

有效期

由于会有越来越多的用户访问服务器,因此 Session 也会越来越多。为防止内存溢出,服务器会把长时间内没有活跃的 Session 从内存删除。这个时间就是 Session 的超时时间。如果超过了超时时间没访问过服务器,Session 就自动失效了。

Session 如何传递Session Id

  • 保存 session id 的方式可以采用 cookie,这样在交互过程中浏览器可以自动的按照规则把这个标识发送给服务器
  • 由于 cookie 可以被人为的禁止,必须有其它的机制以便在 cookie 被禁止时仍然能够把 session id 传递回服务器,经常采用的一种技术叫做 URL 重写,就是把 session id 附加在 URL 路径的后面,附加的方式也有两种,一种是作为 URL 路径的附加信息,另一种是作为查询字符串附加在 URL 后面。网络在整个交互过程中始终保持状态,就必须在每个客户端可能请求的路径后面都包含这个 session id。
  • 另一种技术叫做表单隐藏字段。就是服务器会自动修改表单,添加一个隐藏字段,以便在表单提交时能够把 session id 传递回服务器。

应用场景

  • Cookie 应用于记录上次访问时间;浏览记录
  • Session 应用于购物车

区别

  • Session 工作在 Server 中;Cookie 工作在 Client
  • 二者均工作在内存中,也可以做持久化
  • Session 会对服务器产生一定的性能压力,有条件可以组集群;Cookie 则不会产生高负载
  • 单个 Cookie 在客户端的限制是 4KB
  • 在以亿为单位的网站中,即使是小容量的 Cookie 也会带来巨大的网络流量
  • Session 相比 Cookie 支持更多的数据类型
  • Session 的生命周期长于 Cookie (关闭就没了)

分布式 Session

在搭建完集群环境后,不得不考虑的一个问题就是用户访问产生的session如何处理。如果不做任何处理的话,用户将出现频繁登录的现象,比如集群中存在A、B两台服务器,用户在第一次访问网站时,Nginx通过其负载均衡机制将用户请求转发到A服务器,这时A服务器就会给用户创建一个Session。当用户第二次发送请求时,Nginx将其负载均衡到B服务器,而这时候B服务器并不存在Session,所以就会将用户踢到登录页面。这将大大降低用户体验度,导致用户的流失,这种情况是项目绝不应该出现的。

  • 粘性 Session

    粘性 Session 是指将用户锁定到某一个服务器上,比如上面说的例子,用户第一次请求时,负载均衡器将用户的请求转发到了 A 服务器上,如果负载均衡器设置了粘性 Session 的话,那么用户以后的每次请求都会转发到 A 服务器上,相当于把用户和 A 服务器粘到了一块,这就是粘性 Session 机制。

    简单,不需要对 session 做任何处理。

    缺乏容错性,如果当前访问的服务器发生故障,用户被转移到第二个服务器上时,他的 session 信息都将失效。

  • 服务器 session 复制

    任何一个服务器上的 session 发生改变(增删改),该节点会把这个 session 的所有内容序列化,然后广播给所有其它节点,不管其他服务器需不需要 session,以此来保证 Session 同步。

    可容错,各个服务器间 session 能够实时响应。

    会对网络负荷造成一定压力,如果 session 量大的话可能会造成网络堵塞,拖慢服务器性能。

  • Session 共享机制

    使用分布式缓存方案比如 memcached、Redis,但是要求 Memcached 或 Redis 必须是集群。

    1. 粘性session处理方式

      不同的 tomcat指定访问不同的主memcached。多个Memcached之间信息是同步的,能主从备份和高可用。用户访问时首先在tomcat中创建session,然后将session复制一份放到它对应的memcahed上。memcache只起备份作用,读写都在tomcat上。当某一个tomcat挂掉后,集群将用户的访问定位到备tomcat上,然后根据cookie中存储的SessionId找session,找不到时,再去相应的memcached上去session,找到之后将其复制到备tomcat上。

    2. 非粘性 Session 处理方式

      memcached做主从复制,写入session都往从memcached服务上写,读取都从主memcached读取,tomcat本身不存储session

  • session持久化到数据库

    拿出一个数据库,专门用来存储session信息。保证session的持久化。

    服务器出现问题,session不会丢失。

    如果网站的访问量很大,把session存储到数据库中,会对数据库造成很大压力,还需要增加额外的开销维护数据库。

  • terracotta实现session复制

    Terracotta的基本原理是对于集群间共享的数据,当在一个节点发生变化的时候,Terracotta只把变化的部分发送给Terracotta服务器,然后由服务器把它转发给真正需要这个数据的节点。可以看成是对第二种方案的优化。

Session ID

sessionid 是一个会话的 key,浏览器第一次访问服务器会在服务器端生成一个 session,有一个 sessionid 和它对应。tomcat 生成的 sessionid 叫做 jsessionid。

session在访问tomcat服务器HttpServletRequest的getSession(true)的时候创建,tomcat的ManagerBase类提供创建sessionid的方法:随机数+时间+jvmid

存储在服务器的内存中,tomcat的StandardManager类将session存储在内存中,也可以持久化到file,数据库,memcache,redis等。客户端只保存sessionid到cookie中,而不会保存session,session销毁只能通过invalidate或超时,关掉浏览器并不会关闭session。

超时;程序调用HttpSession.invalidate();程序关闭;

场景

在浏览器中输入 URL 网址到显示主页的过程

  • DNS 解析
  • TCP 连接
  • 发送 HTTP 请求
  • 服务器处理请求
  • 服务器返回 HTTP 报文
  • 浏览器解析渲染页面
  1. DNS 解析的过程是递归的。以访问www.oogle.com为例,首先在本地域名服务器中查询 IP 地址,如果没有找到的话本地域名服务器会向根域名服务器发送一个请求,如果根域名也不存在该域名时,本地域名会向com顶级域名服务器发送一个请求,如果根域名服务器也不存在该域名时,本地域名会向com顶级域名服务器发送一个请求,依次类推下去。直到最后本地域名服务器得到google的IP地址并把它缓存到本地,供下次查询使用。
  2. HTTP 协议是使用 TCP 作为其传输层协议的,当 TCP 出现瓶颈时,HTTP 也会受到影响。
  3. 发送 HTTP 请求的过程就是构建 HTTP 请求报文并通过 TCP 协议中发送到服务器指定端口(HTTP协议80/8080, HTTPS协议443)。HTTP 请求报文是由三部分组成: 请求行, 请求报头和请求体。
  4. 后端从在固定的端口接收到 TCP 报文开始,这一部分对应于编程语言中的 socket。它会对 TCP 连接进行处理,对 HTTP 协议进行解析,并按照报文格式进一步封装成 HTTP Request 对象,供上层使用。HTTP 响应报文也是由三部分组成: 状态码, 响应报头和响应体。
  5. 浏览器是一个边解析边渲染的过程。首先浏览器解析 HTML 文件构建 DOM 树,然后解析 CSS 文件构建渲染树,等到渲染树构建完成后,浏览器开始布局渲染树并将其绘制到屏幕上。

[1. DHCP 配置主机信息](https://cyc2018.github.io/CS-Notes/#/notes/计算机网络 - 应用层?id=_1-dhcp-配置主机信息)

  • 假设主机最开始没有 IP 地址以及其它信息,那么就需要先使用 DHCP 来获取。
  • 主机生成一个 DHCP 请求报文,并将这个报文放入具有目的端口 67 和源端口 68 的 UDP 报文段中。
  • 该报文段则被放入在一个具有广播 IP 目的地址(255.255.255.255) 和源 IP 地址(0.0.0.0)的 IP 数据报中。
  • 该数据报则被放置在 MAC 帧中,该帧具有目的地址 FF:FF:FF:FF:FF:FF,将广播到与交换机连接的所有设备。
  • 连接在交换机的 DHCP 服务器收到广播帧之后,不断地向上分解得到 IP 数据报、UDP 报文段、DHCP 请求报文,之后生成 DHCP ACK 报文,该报文包含以下信息:IP 地址、DNS 服务器的 IP 地址、默认网关路由器的 IP 地址和子网掩码。该报文被放入 UDP 报文段中,UDP 报文段有被放入 IP 数据报中,最后放入 MAC 帧中。
  • 该帧的目的地址是请求主机的 MAC 地址,因为交换机具有自学习能力,之前主机发送了广播帧之后就记录了 MAC 地址到其转发接口的交换表项,因此现在交换机就可以直接知道应该向哪个接口发送该帧。
  • 主机收到该帧后,不断分解得到 DHCP 报文。之后就配置它的 IP 地址、子网掩码和 DNS 服务器的 IP 地址,并在其 IP 转发表中安装默认网关。

[2. ARP 解析 MAC 地址](https://cyc2018.github.io/CS-Notes/#/notes/计算机网络 - 应用层?id=_2-arp-解析-mac-地址)

  • 主机通过浏览器生成一个 TCP 套接字,套接字向 HTTP 服务器发送 HTTP 请求。为了生成该套接字,主机需要知道网站的域名对应的 IP 地址。
  • 主机生成一个 DNS 查询报文,该报文具有 53 号端口,因为 DNS 服务器的端口号是 53。
  • 该 DNS 查询报文被放入目的地址为 DNS 服务器 IP 地址的 IP 数据报中。
  • 该 IP 数据报被放入一个以太网帧中,该帧将发送到网关路由器。
  • DHCP 过程只知道网关路由器的 IP 地址,为了获取网关路由器的 MAC 地址,需要使用 ARP 协议。
  • 主机生成一个包含目的地址为网关路由器 IP 地址的 ARP 查询报文,将该 ARP 查询报文放入一个具有广播目的地址(FF:FF:FF:FF:FF:FF)的以太网帧中,并向交换机发送该以太网帧,交换机将该帧转发给所有的连接设备,包括网关路由器。
  • 网关路由器接收到该帧后,不断向上分解得到 ARP 报文,发现其中的 IP 地址与其接口的 IP 地址匹配,因此就发送一个 ARP 回答报文,包含了它的 MAC 地址,发回给主机。

[3. DNS 解析域名](https://cyc2018.github.io/CS-Notes/#/notes/计算机网络 - 应用层?id=_3-dns-解析域名)

  • 知道了网关路由器的 MAC 地址之后,就可以继续 DNS 的解析过程了。
  • 网关路由器接收到包含 DNS 查询报文的以太网帧后,抽取出 IP 数据报,并根据转发表决定该 IP 数据报应该转发的路由器。
  • 因为路由器具有内部网关协议(RIP、OSPF)和外部网关协议(BGP)这两种路由选择协议,因此路由表中已经配置了网关路由器到达 DNS 服务器的路由表项。
  • 到达 DNS 服务器之后,DNS 服务器抽取出 DNS 查询报文,并在 DNS 数据库中查找待解析的域名。
  • 找到 DNS 记录之后,发送 DNS 回答报文,将该回答报文放入 UDP 报文段中,然后放入 IP 数据报中,通过路由器反向转发回网关路由器,并经过以太网交换机到达主机。

[4. HTTP 请求页面](https://cyc2018.github.io/CS-Notes/#/notes/计算机网络 - 应用层?id=_4-http-请求页面)

  • 有了 HTTP 服务器的 IP 地址之后,主机就能够生成 TCP 套接字,该套接字将用于向 Web 服务器发送 HTTP GET 报文。
  • 在生成 TCP 套接字之前,必须先与 HTTP 服务器进行三次握手来建立连接。生成一个具有目的端口 80 的 TCP SYN 报文段,并向 HTTP 服务器发送该报文段。
  • HTTP 服务器收到该报文段之后,生成 TCP SYN ACK 报文段,发回给主机。
  • 连接建立之后,浏览器生成 HTTP GET 报文,并交付给 HTTP 服务器。
  • HTTP 服务器从 TCP 套接字读取 HTTP GET 报文,生成一个 HTTP 响应报文,将 Web 页面内容放入报文主体中,发回给主机。
  • 浏览器收到 HTTP 响应报文后,抽取出 Web 页面内容,之后进行渲染,显示 Web 页面。

提高向淘宝请求首页的性能

后端:

  • 页面静态化缓存

网络:

  • CDN 加速

影响一个 HTTP 网络请求的因素主要有两个:带宽和延迟。现在网络基础建设已经使得带宽得到极大的提升,只需要关心延迟,延迟共有三类,分别如下:

  • 浏览器阻塞(HOL blocking):浏览器会因为一些原因阻塞请求。浏览器对于同一个域名,同时只能有 4 个连接(这个根据浏览器内核不同可能会有所差异),超过浏览器最大连接数限制,后续请求就会被阻塞。
  • DNS 查询(DNS Lookup):浏览器需要知道目标服务器的 IP 才能建立连接。将域名解析为 IP 的这个系统就是 DNS。这个通常可以利用DNS缓存结果来达到减少这个时间的目的。
  • 建立连接(Initial connection):HTTP 是基于 TCP 协议的,浏览器最快也要在第三次握手时才能捎带 HTTP 请求报文,达到真正的建立连接,但是这些连接无法复用会导致每次请求都经历三次握手和慢启动。三次握手在高延迟的场景下影响较明显,慢启动则对文件类大请求影响较大。

路由器和交换机的区别

  • 工作层次不同:交换机工作在 OSI 开放式系统互联模型的数据链路层,也就是第二层,而路由器则工作在 OSI 模型的网络层,就是第三层。
  • 数据的转发对象不同: 交换机是根据 MAC 地址转发数据帧,而路由器则是根据 IP 地址来转发 IP 数据报/分组。
  • 分工:交换机主要是用于组建局域网,而路由器则是负责让主机连接外网。

DNS寻址过程

  • 在浏览器中输入www.qq.com域名,操作系统会先检查自己本地的 hosts 文件是否有这个网址映射关系,如果有,就先调用这个 IP 地址映射,完成域名解析。
  • 如果 hosts 里没有这个域名的映射,则查找本地 DNS 解析器缓存,是否有这个网址映射关系,如果有,直接返回,完成域名解析。
  • 如果 hosts 与本地 DNS 解析器缓存都没有相应的网址映射关系,首先会找 TCP/ip 参数中设置的首选 DNS 服务器,在此我们叫它本地 DNS 服务器,此服务器收到查询时,如果要查询的域名,包含在本地配置区域资源中,则返回解析结果给客户机,完成域名解析,此解析具有权威性。
  • 如果要查询的域名,不由本地 DNS 服务器区域解析,但该服务器已缓存了此网址映射关系,则调用这个 IP 地址映射,完成域名解析,此解析不具有权威性。
  • 如果本地 DNS 服务器本地区域文件与缓存解析都失效,则根据本地 DNS 服务器的设置(是否设置转发器)进行查询,如果未用转发模式,本地 DNS 就把请求发至13台根 DNS,根 DNS 服务器收到请求后会判断这个域名(.com)是谁来授权管理,并会返回一个负责该顶级域名服务器的一个 IP。本地 DNS 服务器收到IP信息后,将会联系负责 .com 域的这台服务器。这台负责 .com 域的服务器收到请求后,如果自己无法解析,它就会找一个管理 .com 域的下一级 DNS 服务器地址(qq.com)给本地 DNS 服务器。当本地 DNS 服务器收到这个地址后,就会找qq.com域服务器,重复上面的动作,进行查询,直至找到www.qq.com主机。
  • 如果用的是转发模式,此 DNS 服务器就会把请求转发至上一级 DNS 服务器,由上一级服务器进行解析,上一级服务器如果不能解析,或找根 DNS 或把转请求转至上上级,以此循环。不管是本地 DNS 服务器用是是转发,还是根提示,最后都是把结果返回给本地 DNS 服务器,由此 DNS 服务器再返回给客户机。

负载均衡

  • HTTP 重定向实现负载均衡

    当用户向服务器发起请求时,请求首先被集群调度者截获;调度者根据某种分配策略,选择一台服务器,并将选中的服务器的IP地址封装在HTTP响应消息头部的Location字段中,并将响应消息的状态码设为302,最后将这个响应消息返回给浏览器。

    当浏览器收到响应消息后,解析Location字段,并向该URL发起请求,然后指定的服务器处理该用户的请求,最后将结果返回给用户。

    在使用HTTP重定向来实现服务器集群负载均衡的过程中,需要一台服务器作为请求调度者。用户的一项操作需要发起两次HTTP请求,一次向调度服务器发送请求,获取后端服务器的IP,第二次向后端服务器发送请求,获取处理结果。

    • 随机分配策略

    • 轮询策略

      调度服务器需要维护一个值,用于记录上次分配的后端服务器的IP。那么当新的请求到来时,调度者将请求依次分配给下一台服务器。

  • DNS 负载均衡

    我们知道,数据包采用IP地址在网络中传播,而为了方便用户记忆,我们使用域名来访问网站。那么,我们通过域名访问网站之前,首先需要将域名解析成IP地址,这个工作是由DNS完成的。也就是域名服务器。

    我们提交的请求不会直接发送给想要访问的网站,而是首先发给域名服务器,它会帮我们把域名解析成IP地址并返回给我们。我们收到IP之后才会向该IP发起请求。

    那么,DNS服务器有一个天然的优势,如果一个域名指向了多个IP地址,那么每次进行域名解析时,DNS只要选一个IP返回给用户,就能够实现服务器集群的负载均衡。

  • 动态 DNS 均衡

    动态DNS能够让我们通过程序动态修改DNS服务器中的域名解析。从而当我们的监控程序发现某台服务器挂了之后,能立即通知DNS将其删掉。

  • 反向代理均衡

    反向代理服务器是一个位于实际服务器之前的服务器,所有向我们网站发来的请求都首先要经过反向代理服务器,服务器根据用户的请求要么直接将结果返回给用户,要么将请求交给后端服务器处理,再返回给用户。

    • 隐藏后端服务器
    • 故障转移
    • 合理分配任务

高并发短连接问题

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

高并发可以让服务器在短时间范围内同时占用大量端口,而端口有个0~65535的范围,并不是很多,刨除系统和其他服务要用的,剩下的就更少了。

在这个场景中,短连接表示“业务处理+传输数据的时间 远远小于 TIMEWAIT超时的时间”的连接

这里有个相对长短的概念,比如取一个web页面,1秒钟的http短连接处理完业务,在关闭连接之后,这个业务用过的端口会停留在TIMEWAIT状态几分钟,而这几分钟,其他HTTP请求来临的时候是无法占用此端口的(占着茅坑不拉翔)。单用这个业务计算服务器的利用率会发现,服务器干正经事的时间和端口(资源)被挂着无法被使用的时间的比例是 1:几百,服务器资源严重浪费。(说个题外话,从这个意义出发来考虑服务器性能调优的话,长连接业务的服务就不需要考虑TIMEWAIT状态。同时,假如你对服务器业务场景非常熟悉,你会发现,在实际业务场景中,一般长连接对应的业务的并发量并不会很高

综合这两个方面,持续的到达一定量的高并发短连接,会使服务器因端口资源不足而拒绝为一部分客户服务。同时,这些端口都是服务器临时分配,无法用SO_REUSEADDR选项解决这个问题。

简单解决,打开系统的 TIMEWAIT 重用和快速回收。

服务器保持了大量 CLOSE_WAIT 状态,在对方关闭连接之后服务器程 序自己没有进一步发出ack信号。换句话说,就是在对方连接关闭之后,程序里没有检测到,或者程序压根就忘记了这个时候需要关闭连接,于是这个资源就一直 被程序占着。

拥塞控制

发送方维持一个叫拥塞窗口,cwnd,拥塞窗口的大小取决于网络的拥塞程度,并且在动态地变化,发送方让自己的发送窗口等于拥塞窗口,另外考虑到接受方的接受能力,发送窗口可能小于拥塞窗口。

慢开始算法的思路就是,不要一开始就发送大量的数据,先探测一下网络的拥塞程度,也就是说由小到大逐渐增加拥塞窗口的大小。

拥塞控制

拥塞避免