八股文之计算机网络

概览

OSI七层模型

得分点 协议栈自上而下依次为:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层。
在计算机网络中要做到正确的数据交换,就必须提前约定好相应的规则。OSI七层模型是一个协议栈,就是为了统一计算机网络标准,方便数据的交换。它自上而下依次为:

  1. 应用层

    应用层是体系结构中的最高层,是应用进程间通信和交互的规则,进程指计算机中运行的程序。也是用户与应用程序之间的一个接口,操作程序(软件,Web应用),进而触发更下层的服务。 协议:HTTP、HTTPS、FTP、TFTP、SMTP等;

  2. 表示层

    对从应用层获取到的数据报文数据进行格式处理、安全处理和压缩处理。 格式:JPEG、ASCll、加密格式等;

  3. 会话层

    对当前主机进程和目标主机进程会话的建立、管理和终止行为

  4. 传输层

    对两台主机进程也就是应用层提供数据传输服务。定义了传输数据的进程端口号,负责数据包的排序、差错检验和流量控制等。 协议:UDP、TCP

  5. 网络层

    主要进行逻辑地址的查询。 协议:ICMP、IGMP、IP(IPv4、IPv6)

  6. 数据链路层

    建立相邻节点的逻辑连接,进行逻辑地址寻址、差错校验等。 协议:ARP、RARP、PPP 等

  7. 物理层

    物理层上数据的单位是Bit比特,数据的传输都是通过0(或1)比特流来实现的,而0(或1)比特流与电压的高低有关。负责了最底层数据传输的建立、传输和断开。

请你说说 TCP/IP 五层模型

协议栈自上而下依次为:应用层、传输层、网络层、数据链路层、物理层。
五层协议体系结构结合了OSI模型和TCP/IP模型的优点,既简洁又能将每一层描述清楚。在计算机网络中要做到正确的数据交换,就必须提前约定好相应的规则。它是一个协议栈,就是为了统一计算机网络标准,方便数据的交换。它自上而下依次为:

  1. 应用层

    应用层是体系结构中的最高层,定义了应用进程间通信和交互的规则。本层任务就是通过应用进程间的信息数据流通完成特定的网络应用(软件、Web应用等)。因为不同的应用程序都需要不同的应用层协议,所以应用层协议较多,如万维网应用的HTTP协议、电子邮件的SMTP协议、文件传送的FTP协议等。请将应用层交互的数据称为报文,以免产生概念的混淆。 协议:HTTP、HTTPS、FTP、TFTP、SMTP等;

  2. 传输层

    运输层的任务是负责向两个计算机中进程之间的通信提供一种通用的数据传输服务,应用层通过运输层可以传输报文。通用是指不会针对特定的应用层协议进行详细的划分,多种应用层协议公用同一个运输层服务,所以运输层有复用的功能。当然也有分发的功能,指将接受到的信息分别交付到应用层不同的进程中。 协议:UDP、TCP

  3. 网络层

    网络层的任务是负责为网络上不同的主机提供通信服务。在发送数据时,网络层将运输层产生的报文段或者用户数据报封装成分组或者包(packet)进行传送。由于网络层使用IP协议,所以分组或包(packet)也叫IP数据报,简称数据报。网络层还需要寻找合适的路由路线,让源主机运输层发送下来的数据报能通过路由器找到目的主机。 协议:ICMP、IGMP、IP(IPv4、IPv6)、ARP、RARP(七层模型中在数据链路层)

  4. 数据链路层

    数据链路层简称链路层。两个节点传输数据时,链路层将网络层交下来的数据报组装成帧,在链路上传送帧。每一帧都包括数据和控制信息(同步信息、地址信息、差错控制等)。

  5. 物理层

    物理层上数据的单位是Bit比特,数据的传输都是通过0(或1)比特流来实现的,而0(或1)比特流与电压的高低有关。物理层中比特流的传输不再加控制信息,需要注意的是比特流应从首部开始传送。

TCP/IP 四层模型

  1. 应用层

    TCP/IP 模型将 OSI 参考模型中的会话层、表示层和应用层的功能合并到一个应用层实现,通过不同的应用层协议为不同的应用提供服务。
    应用层只需要专注于为用户提供应用功能,比如 HTTP、FTP、Telnet、DNS、SMTP等。
    而且应用层是工作在操作系统中的用户态,传输层及以下则工作在内核态。

  2. 传输层

    该层对应于 OSI 参考模型的传输层,为上层实体提供源端到对端主机的通信功能。传输层定义了两个主要协议:传输控制协议(TCP)和用户数据报协议(UDP)。其中面向连接的 TCP 协议保证了数据的传输可靠性,面向无连接的 UDP 协议能够实现数据包简单、快速地传输。

  3. 网络层

    网络层对应 OSI 参考模型的网络层,主要负责相同或不同网络中计算机之间的通信。在网际互联层, IP 协议提供的是一个不可靠、无连接的数据报传递服务。网络层负责网络包的封装、分片、路由、转发。根据数据报报头中的目的地址将数据传送到目的地址,在这个过程中 IP 负责选择传送路线。除了 IP 协议外,该层另外两个主要协议是互联网组管理协议(IGMP)和互联网控制报文协议(ICMP)。

  4. 网络接口层

    网络接入层的功能对应于 OSI 参考模型中的物理层和数据链路层,它负责监视数据在主机和网络之间的交换。事实上,TCP/IP 并未真正描述这一层的实现,而由参与互连的各网络使用自己的物理层和数据链路层协议,然后与 TCP/IP 的网络接入层进行连接,因此具体的实现方法将随着网络类型的不同而有所差异。

OSI 模型和 TCP/IP 模型异同比较

  1. 相同点

    1. OSI 参考模型与 TCP/IP 参考模型都采用了层次结构。
    2. 都能够提供面向连接无连接两种通信服务机制。
  2. 不同点

    1. OSI 采用的七层模型; TCP/IP 是四层结构。
    2. TCP/IP 参考模型没有对网络接口层进行细分,只是一些概念性的描述; OSI 参考模型对服务和协议做了明确的区分。
    3. OSI 先有模型,后有协议规范,适合于描述各种网络;TCP/IP 是先有协议集然后建立模型,不适用于非 TCP/IP 网络。
    4. TCP/IP 一开始就提出面向连接和无连接服务,而 OSI 一开始只强调面向连接服务,直到很晚才开始制定无连接的服务标准。
    5. OSI 参考模型虽然被看好,但将网络划分为七层,实现起来较困难;相反,TCP/IP 参考模型虽然有许多不尽人意的地方,但作为一种简化的分层结构还是比较成功的。

OSI 和 TCP/IP 协议之间的对应关系

OSI 和 TCP/IP 协议之间的对应关系

为什么 TCP/IP 去除了表示层和会话层

OSI 参考模型在提出时,他们的理想是非常好的,但实际上,由于会话层、表示层、应用层都是在应用程序内部实现的,最终产出的是一个应用数据包,而应用程序之间是几乎无法实现代码的抽象共享的,这也就造成 OSI 设想中的应用程序维度的分层是无法实现的,例如,我们几乎不会认为数据的压缩、加密算法算是一种协议,而会话的概念则更为抽象,难以用协议来进行描述,所以在后来的 TCP/IP 协议框架的设计中,便将表示层和会话层与应用层整合在一起,让整个过程更为清晰明了。

数据如何在各层之间传输【数据的封装过程】

数据的封装过程

  • 传输层,给应用数据前面增加了 TCP 头;
  • 网络层,给 TCP 数据包前面增加了 IP 头;
  • 网络接口层,给 IP 数据包前后分别增加了帧头和帧尾;

发送网络数据的时候,涉及几次内存拷贝操作?

  1. 第一次,调用发送数据的系统调用的时候,内核会申请一个内核态的 sk_buff 内存,将用户待发送的数据拷贝到 sk_buff 内存,并将其加入到发送缓冲区。
  2. 第二次,在使用 TCP 传输协议的情况下,从传输层进入网络层的时候,每一个 sk_buff 都会被克隆一个新的副本出来。副本 sk_buff 会被送往网络层,等它发送完的时候就会释放掉,然后原始的 sk_buff 还保留在传输层,目的是为了实现 TCP 的可靠传输,等收到这个数据包的 ACK 时,才会释放原始的 sk_buff 。
  3. 第三次,当 IP 层发现 sk_buff 大于 MTU 时才需要进行。会再申请额外的 sk_buff,并将原来的 sk_buff 拷贝为多个小的 sk_buff。

从输入URL到网页显示,期间发生了什么

image.png

image.png

image.png

  1. 浏览器解析URL,URL中包含:访问数据协议,Web服务器名称和资源文件路径;浏览器对URL进行解析之后,确定Web服务器文件名
  2. 浏览器通过DNS获取到Web服务器的IP地址之后生成HTTP请求报文;
  3. HTTP基于TCP传输协议,浏览器向服务器发送HTTP请求前先三次握手建立连接;
  4. 判断浏览器本地是否存在HTTP缓存,如果是强制缓存且在有效期内,则不发送HTTP请求,如果是HTTP协商缓存,则向服务器与服务器协商,若采用本地缓存,则服务器返回304;如果不在有效期内,发送HTTP请求
  5. 如果请求参数有问题,服务器端返回404,如果服务器端出错返回500;
  6. 如果返回正常HTML文件,浏览器载入HTML代码,开始渲染页面同时获取HTML页面中图片、音频、视频、CSS、JS,在这期间获取到JS文件之后,会直接执行JS代码,阻塞浏览器渲染,因为渲染引擎和JS引擎互斥,不能同时工作,所以通常把Script标签放在body标签的底部,另外可以把多个访问小文件的请求合并成一个大的请求,减少HTTP请求;
  7. 渲染过程就是先将HTML转换成dom树,再将CSS样式转换成stylesheet,根据dom树和stylesheet创建布局树,对布局树进行分层,为每个图层生成绘制列表,再将图层分成图块,紧接着光栅化将图块转换成位图,最后合成绘制生成页面。

域名解析(DNS)工作流程

  1. 客户端发起DNS请求,向本地DNS服务器请求该域名的ip地址;
  2. 本地DNS服务器接收到客户端的请求后,如果缓存列表找到该域名的ip地址,则直接返回该ip地址给客户端;如果没有,本地DNS服务器会向根域名服务器请求;
  3. 根域名服务器收到请求之后,会将请求解析域名的顶级域名服务器地址返回给本地DNS服务器;
  4. 本地DNS服务器收到响应后,转而向顶级域名服务器请求,顶级域名服务器返回权威DNS服务器地址;
  5. 本地DNS服务器向该权威DNS服务器发出请求,权威DNS服务器查询后将对应的ip地址告诉本地DNS服务器;
  6. 最后本地DNS服务器将该ip地址返回给客户端

什么是端口

  1. 概念:端口用一个16位端口号进行标识。即一个电脑允许有65535个不同的端口号,端口只具有本地意义,不同计算机上相同端口代表的服务是不同的;
  2. 常用默认端口:

DNS:43
HTTP:80
HTTPS: 443
TCP:80
SQL:3306
redis:6379
SSH:22
tomcat:8080

  1. 端口的作用:实现TCP、UDP的复用与分用
    1. 不同的应用进程通过端口将数据传递给TCP和UDP,实现传输层的复用
    2. TCP和UDP接受到数据之后,根据端口号分发给不同的进程,实现传输层的分用
  2. 为什么不引入进程标识来代替端口?因为进程标识是动态创建与撤销的,时刻在变化,发送方不好确定该指定哪个进程标识符,而且不同操作系统的进程标识符的格式也不同。

应用层

HTTP是什么

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

HTTP状态码

分为5类

  1. 1xx,一般是表示请求成功,继续等待下一步请求 , 例如 100
  2. 2xx:一般表示请求成功
    1. 200:请求成功,并返回请求体, 此请求一般会缓存
    2. 204:请求成功,不返回请求体 (一般是put/delete请求),默认情况下可以被缓存,响应头中会有一个Etag(强校验器)
  3. 3xx:一般表示重定向
    1. 301:永久重定向,资源永久性的移到新网址,浏览器自动会进行url跳转,并且爬虫会收录新的url,但是URl跳转过程中可能会改变跳转方式
    2. 308:永久重定向,资源永久性的移到新网址,浏览器自动会进行url跳转,并且爬虫会收录新的url,与301相比,不会改变跳转请求方式
    3. 302:临时重定向,资源暂时性的移到新网址,进行url跳转,但是爬虫不会收录新的url,跳转时可能改变url请求方式
    4. 307:临时重定向,资源暂时性的移到新网址,进行url跳转,但是爬虫不会收录新的url,与302相比,不会改变跳转时请求url方法
    5. 304:表示资源未改变,可以使用本地缓存
  4. 4xx:一般表示客户端错误
    1. 400:表示url请求错误
    2. 401:表示客户端身份认证不正确
    3. 402:表示rul请求方法错误
    4. 403:请求正确,但是服务器拒绝提供服务,一般是访问权限不足
    5. 404:访问的资源可能永久或者暂时的丢失,一般成为坏链或死链
  5. 5xx:一般表示服务器出错
    1. 500:服务器遇到意外情况
    2. 503:服务器处于不可接收状态,一般是服务器达到连接上限,停机维护
    3. 505:表示服务器使用的http版本不同,服务器不支持该版本

HTTP常见字段

  1. Host 字段

    客户端发送请求时,用来指定服务器的域名

  2. Content-Length 字段

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

  3. Connection 字段(Keep-Alive)

    Connection 字段最常用于客户端要求服务器使用 TCP 持久连接,以便其他请求复用

  4. Content-Encoding 字段

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

GET和POST的区别

  1. GET 的语义是从服务器获取指定的资源,POST 的语义是根据请求负荷(报文body)对指定的资源做出处理
  2. get有url长度限制(2048字节)而post没有;
  3. get的参数是显式的,get的参数会附加在url之 中,以 “ ? “分割url和传输数据,多个参数用 “&”连接;而post是隐式的,post是放在请求体中;
  4. get请求会保存在浏览器历史记录中,也可以保存在web服务器日志中;
  5. get在浏览器回退时是无害的,而post会再次提交请求;
  6. get请求只能进行url编码,而post支持多种编码方式;
  7. get请求的参数数据类型只接受ASCII字符,而post没有限制。

HTTP缓存技术

HTTP缓存技术有哪些

对于一些具有重复性的 HTTP 请求,比如每次请求得到的数据都一样的,我们可以把这对「请求-响应」的数据都缓存在本地,那么下次就直接读取本地的数据,不必在通过网络获取服务器的响应了,这样的话 HTTP/1.1 的性能肯定肉眼可见的提升。
所以,避免发送 HTTP 请求的方法就是通过缓存技术,HTTP 设计者早在之前就考虑到了这点,因此 HTTP 协议的头部有不少是针对缓存的字段。
HTTP 缓存有两种实现方式,分别是强制缓存协商缓存

强制缓存

强缓存指的是只要浏览器判断缓存没有过期,则直接使用浏览器的本地缓存,决定是否使用缓存的主动性在于浏览器这边
返回的是 200 状态码,但在 size 项中标识的是from disk cache,就是使用了强制缓存。
强缓存是利用下面这两个 HTTP 响应头部(Response Header)字段实现的,它们都用来表示资源在客户端缓存的有效期:

  • 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。

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

ETag 主要能解决 Last-Modified 几个比较难以解决的问题:

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

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

HTTP/1.1的特性

  1. 优点
    1. 简单,header + body,易于理解
    2. 灵活和易于扩展,允许自定义和扩充
    3. 应用广泛和跨平台
  2. 缺点
    1. 无状态,它在完成有关联性的操作时会非常麻烦(解决方法:cookie)
    2. 明文传输
    3. 不安全
      1. 明文传输
      2. 不验证通信放信息
      3. 无法验证报文完整性
  3. HTTP1.1 性能
    1. 长连接
    2. 队头阻塞

HTTP与HTTPS

  1. 区别

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

    1. 窃听风险
    2. 篡改风险
    3. 冒充风险
  3. HTTPS 在 HTTP 与 TCP 层之间加入了 SSL/TLS 协议,可以很好的解决了上述的风险

    1. 窃听风险:混合加密算法实现了机密性
    2. 篡改风险:摘要算法实现完整性
    3. 冒充风险:数字证书解决了冒充风险

混合加密

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

摘要算法+数字签名

image.png

哈希获取摘要,私钥加密摘要获取数字签名。

数字证书
由CA颁布

HTTPS如何建立连接

  1. TCP三次握手
  2. SSL/TLS 四次握手

SSL/TLS 协议基本流程:

  • 客户端向服务器索要并验证服务器的公钥。
  • 双方协商生产「会话秘钥」。
  • 双方采用「会话秘钥」进行加密通信。

SSL/TLS 协议建立的详细流程:

  1. ClientHello

    首先,由客户端向服务器发起加密通信请求,也就是 ClientHello 请求。
    在这一步,客户端主要向服务器发送以下信息:

    1. 客户端支持的 SSL/TLS 协议版本,如 TLS 1.2 版本。
    2. 客户端生产的随机数(Client Random),后面用于生成「会话秘钥」条件之一。
    3. 客户端支持的密码套件列表,如 RSA 加密算法。
  2. SeverHello

    服务器收到客户端请求后,向客户端发出响应,也就是 SeverHello。服务器回应的内容有如下内容:

    1. 确认 SSL/ TLS 协议版本,如果浏览器不支持,则关闭加密通信。
    2. 服务器生产的随机数(Server Random),也是后面用于生产「会话秘钥」条件之一。
    3. 确认的密码套件列表,如 RSA 加密算法。
    4. 服务器的数字证书
  3. 客户端回应
    客户端收到服务器的回应之后,首先通过浏览器或者操作系统中的 CA 公钥,验证服务器的数字证书的真实性。
    如果证书没有问题,客户端会从数字证书中取出服务器的公钥,然后使用它加密报文,向服务器发送如下信息:

      1. 一个**随机数(pre-master key)**。该随机数会被服务器公钥加密。
      2. 加密通信算法改变**通知**,表示随后的信息都将用**「会话秘钥」**加密通信。
      3. 客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个**摘要,用来供服务端校验**。
    

    上面第一项的随机数是整个握手阶段的第三个随机数,会发给服务端,所以这个随机数客户端和服务端都是一样的。
    服务器和客户端有了这三个随机数(Client Random、Server Random、pre-master key),接着就用双方协商的加密算法,各自生成本次通信的「会话秘钥」

  4. 服务器的最后回应

    服务器收到客户端的第三个随机数(pre-master key)之后,通过协商的加密算法,计算出本次通信的「会话秘钥」。
    然后,向客户端发送最后的信息:

    1. 加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。
    2. 服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供客户端校验。

至此,整个 SSL/TLS 的握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的 HTTP 协议,只不过用「会话秘钥」加密内容。

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

TLS 在实现上分为握手协议记录协议两层:

  • TLS 握手协议就是我们前面说的 TLS 四次握手的过程,负责协商加密算法和生成对称密钥,后续用此密钥来保护应用程序数据(即 HTTP 数据);
  • TLS 记录协议负责保护应用程序数据并验证其完整性和来源,所以对 HTTP 数据加密是使用记录协议;

TLS 记录协议主要负责消息(HTTP 数据)的压缩,加密及数据的认证,过程如下图:
image.png
具体过程如下:

  1. 首先,消息被分割成多个较短的片段,然后分别对每个片段进行压缩
  2. 接下来,经过压缩的片段会被加上消息认证码(MAC 值,这个是通过哈希算法生成的),这是为了保证完整性,并进行数据的认证。通过附加消息认证码的 MAC 值,可以识别出篡改。与此同时,为了防止重放攻击,在计算消息认证码时,还加上了片段的编码
  3. 再接下来,经过压缩的片段再加上消息认证码会一起通过对称密码进行加密
  4. 最后,上述经过加密的数据再加上由数据类型、版本号、压缩后的长度组成的报头就是最终的报文数据。

HTTP/1.1、HTTP/2、HTTP/3的演变

HTTP/1.1 与 HTTP/1.0

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

  • 使用长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。
  • 支持管道(pipeline)网络传输,只要第一个请求发出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。

但 HTTP/1.1 还是有性能瓶颈:

  • 请求 / 响应头部(Header)未经压缩就发送,首部信息越多延迟越大。只能压缩 Body 的部分;
  • 发送冗长的首部。每次互相发送相同的首部造成的浪费较多;
  • 服务器是按请求的顺序响应的,如果服务器响应慢,会招致客户端一直请求不到数据,也就是队头阻塞
  • 没有请求优先级控制
  • 请求只能从客户端开始,服务器只能被动响应

HTTP/2.0

HTTP/2 协议是基于 HTTPS 的,所以 HTTP/2 的安全性也是有保障的。
image.png
HTTP/2 相比 HTTP/1.1 性能上的改进:

  • 头部压缩,HTTP/2 会压缩头(Header)如果你同时发出多个请求,他们的头是一样的或是相似的,那么,协议会帮你消除重复的部分
  • 二进制格式,头信息和数据体都是二进制,并且统称为帧(frame)。
  • 并发传输引出了 Stream 概念,多个 Stream 复用在一条 TCP 连接。针对不同的 HTTP 请求用独一无二的 Stream ID来区分,接收端可以通过 Stream ID 有序组装成 HTTP 消息,不同 Stream 的帧是可以乱序发送的,因此可以并发不同的 Stream ,也就是 HTTP/2 可以并行交错地发送请求和响应。
  • 服务器主动推送资源,服务端不再是被动地响应,可以主动向客户端发送消息。客户端和服务器双方都可以建立 Stream, Stream ID 也是有区别的,客户端建立的 Stream 必须是奇数号,而服务器建立的 Stream 必须是偶数号。

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

HTTP/3.0

HTTP/3 把 HTTP 下层的 TCP 协议改成了 UDP,基于 UDP 的 QUIC 协议 可以实现类似 TCP 的可靠性传输。

  • 无队头阻塞,每个Stream有独立的滑动窗口
  • 更快的连接建立,QUIC 协议并不是与 TLS 分层,而是QUIC 内部包含了 TLS
  • 连接迁移,基于连接ID而非四元组

HTTP/1.1如何优化

  1. 尽量避免发送 HTTP 请求:缓存技术

  2. 在需要发送 HTTP 请求时,考虑如何减少请求次数:

    1. 减少重定向次数,由代理服务器完成重定向并进行缓存
    2. 合并请求,减少重复发送的HTTP头部,合并资源
    3. 延迟发送请求,懒加载
  3. 减少服务器的 HTTP 响应的数据大小:

    1. 有损压缩:音频、视频、图片
    2. 无损压缩:文本文件、程序可执行文件、程序源代码

HTTP/2.0

  1. 头部压缩

    HTTP/1.1body可以使用content-encoding字段指定压缩方式,但是没有针对header的优化手段
    存在的问题:

    1. 固定字段
    2. 重复字段
    3. ASCII编码,效率低下

    HPACK 算法主要包含三个组成部分:

    1. 静态字典,写入到 HTTP/2 框架里的,不会变化的;
    2. 动态字典,在编码解码的时候随时更新;
    3. Huffman 编码(压缩算法);
  2. 二进制编码

    将 HTTP/1 的文本格式改成二进制格式传输数据,极大提高了 HTTP 传输效率,而且二进制数据使用位运算能高效解析。

  3. 并发传输

    引入Stream ,多个 Stream 复用一条 TCP 连接,达到并发的效果,解决了 HTTP/1.1 队头阻塞的问题,提高了 HTTP 传输的吞吐量
    在 HTTP/2 连接上,不同 Stream 的帧是可以乱序发送的(因此可以并发不同的 Stream ),因为每个帧的头部会携带 Stream ID 信息,所以接收端可以通过 Stream ID 有序组装成 HTTP 消息,而同一 Stream 内部的帧必须是严格有序的。

  4. 服务器推送

    客户端发起的请求,必须使用的是奇数号 Stream,服务器主动的推送,使用的是偶数号 Stream。服务器在推送资源时,会通过PUSH_PROMISE帧传输 HTTP 头部,并通过帧中的 Promised Stream ID 字段告知客户端,接下来会在哪个偶数号 Stream 中发送包体。

HTTP/3.0

HTTP/2 虽然具有多个流并发传输的能力,但是传输层是 TCP 协议 ,于是存在以下 缺陷

  • 队头阻塞,HTTP/2 多个请求跑在一个 TCP 连接中,如果序列号较低的 TCP 段在网络传输中丢失了,即使序列号较高的 TCP 段已经被接收了,应用层也无法从内核中读取到这部分数据,从 HTTP 视角看,就是多个请求被阻塞了;
  • TCP 和 TLS 握手时延,TCL 三次握手和 TLS 四次握手,共有 3-RTT 的时延;
  • 连接迁移需要重新连接,移动设备从 4G 网络环境切换到 WIFI 时,由于 TCP 是基于四元组来确认一条 TCP 连接的,那么网络环境变化后,就会导致 IP 地址或端口变化,于是 TCP 只能断开连接,然后再重新建立连接,切换网络环境的成本高;

HTTP/3 就将传输层从 TCP 替换成了 UDP,并在 UDP 协议上开发了 QUIC 协议,来保证数据的可靠传输。
QUIC 协议的特点:

  • 无队头阻塞,QUIC 连接上的多个 Stream 之间并没有依赖,都是独立的,也不会有底层协议限制,某个流发生丢包了,只会影响该流,其他流不受影响
  • 建立连接速度快,因为QUIC 内部包含 TLS1.3,因此仅需 1 个 RTT 就可以「同时」完成建立连接与 TLS 密钥协商,甚至在第二次连接的时候,应用数据包可以和 QUIC 握手信息(连接信息 + TLS 信息)一起发送,达到 0-RTT 的效果。
  • 连接迁移,QUIC 协议没有用四元组的方式来“绑定”连接,而是通过「连接 ID 」来标记通信的两个端点,客户端和服务器可以各自选择一组 ID 来标记自己,因此即使移动设备的网络变化后,导致 IP 地址变化了,只要仍保有上下文信息(比如连接 ID、TLS 密钥等),就可以“无缝”地复用原连接,消除重连的成本;

另外 HTTP/3 的 QPACK 通过两个特殊的单向流来同步双方的动态表,解决了 HTTP/2 的 HPACK 队头阻塞问题。

HTTP与RPC

现在电脑上装的各种联网软件,比如xx管家,xx卫士,它们都作为客户端(client)需要跟服务端(server)建立连接收发消息,此时都会用到应用层协议,在这种 client/server (c/s) 架构下,它们可以使用自家造的RPC协议,因为它只管连自己公司的服务器就ok了。
但有个软件不同,浏览器(browser),不管是chrome还是IE,它们不仅要能访问自家公司的服务器(server),还需要访问其他公司的网站服务器,因此它们需要有个统一的标准,不然大家没法交流。于是,HTTP就是那个时代用于统一 browser/server (b/s) 的协议。
也就是说在多年以前,HTTP主要用于b/s架构,而RPC更多用于c/s架构。但现在其实已经没分那么清了,b/s和c/s在慢慢融合。很多软件同时支持多端,比如某度云盘,既要支持网页版,还要支持手机端和pc端,如果通信协议都用HTTP的话,那服务器只用同一套就够了。而RPC就开始退居幕后,一般用于公司内部集群里,各个微服务之间的通讯。

传输层

TCP 头部格式

源端口号、目的端口号、序列号、应答号、首部长度、控制位、窗口大小、校验和、紧急指针

image.png

什么是TCP

TCP 是面向连接的、可靠的、基于字节流的传输层通信协议。

  • 面向连接:一定是「一对一」才能连接,不能像 UDP 协议可以一个主机同时向多个主机发送消息,也就是一对多是无法做到的;
  • 可靠的:无论的网络链路中出现了怎样的链路变化,TCP 都可以保证一个报文一定能够到达接收端
  • 字节流:用户消息通过 TCP 协议传输时,消息可能会被操作系统「分组」成多个的 TCP 报文,如果接收方的程序如果不知道「消息的边界」,是无法读出一个有效的用户消息的。并且 TCP 报文是「有序的」,当「前一个」TCP 报文没有收到的时候,即使它先收到了后面的 TCP 报文,那么也不能扔给应用层去处理,同时对「重复」的 TCP 报文会自动丢弃。

服务端最大的TCP连接数

服务端最大并发 TCP 连接数远不能达到理论上限,会受以下因素影响:

  • 文件描述符限制,每个 TCP 连接都是一个文件,如果文件描述符被占满了,会发生 too many open files。Linux 对可打开的文件描述符的数量分别作了三个方面的限制:
    • 系统级:当前系统可打开的最大数量,通过 cat /proc/sys/fs/file-max 查看;
    • 用户级:指定用户可打开的最大数量,通过 cat /etc/security/limits.conf 查看;
    • 进程级:单个进程可打开的最大数量,通过 cat /proc/sys/fs/nr_open 查看;
  • 内存限制,每个 TCP 连接都要占用一定内存,操作系统的内存是有限的,如果内存资源被占满后,会发生 OOM。

TCP和UDP的区别

TCP是面向连接的基于字节流的可靠传输层协议,UDP是无连接的基于数据报的传输层协议;

  1. 服务对象
    1. TCP 是点对点服务,每个连接只有两个端点
    2. UDP支持一对一、一对多、多对一、多对多的交互通信
  2. 连接
    1. TCP面向连接,传输之前必须三次握手建立好连接
    2. UDP无连接
  3. 可靠性
    1. TCP可靠交付:差错控制、按序到达
    2. UDP尽力而为,不保证可靠交付
  4. 拥塞控制、流量控制
    1. TCP提供差错控制与流量控制
    2. UDP没有拥塞可控制
  5. 报文长度
    1. TCP最大报文长度为MSS,一般为1460字节,TCP会根据滑动窗口的大小和当前网络的拥塞情况调整报文长度
    2. UDP基于数据报,不合并,不拆分,保留应用层传递下来的报文
  6. 头部开销
    1. TCP头部有20字节(源端口、目的端口、序列号、确认号、首部长度、标志位、窗口大小、校验和、紧急指针、其他选项)
    2. UDP首部长度为8字节(源端口、目的端口、数据长度、校验和)
  7. 适用场景
    1. 特点:TCP可靠传输慢,UDP不可靠传输快
    2. 通信数据完整性要求高于通信实时性时,则应选用TCP(如文件传输、重要状态的更新等);反之,采用UDP(如 视频传输、实时通信等)

TCP三次握手的过程

image.png

  1. 第一次握手:客户端向服务器端发送连接请求报文段,包含自身数据通讯初始序号,进入SYN-SENT状态。
  2. 第二次握手:服务器端收到连接请求报文段后,如果同意,发送应答,包含自身数据通讯初始序号,进入SYN-RECEIVED状态。
  3. 第三次握手:客户端收到应答,最后向服务器端发送确认报文,进入ESTABLISHED状态,此时成功建立长连接。

TCP 三次握手的原因

  1. 避免历史连接(主要原因)

    在两次握手的情况下,旧的SYN比新的SYN先到达,「被动发起方」没有中间状态给「主动发起方」来阻止历史连接,导致「被动发起方」可能建立一个历史连接,造成资源浪费。
    要解决这种现象,最好就是在「被动发起方」发送数据前,也就是建立连接之前,要阻止掉历史连接,这样就不会造成资源浪费,而要实现这个功能,就需要三次握手。

  2. 同步双方序列号

    TCP 协议的通信双方, 都必须维护一个「序列号」,序列号是可靠传输的一个关键因素,它的作用:

    • 接收方可以去除重复的数据
    • 接收方可以根据数据包的序列号按序接收
    • 可以标识发送出去的数据包中, 哪些是已经被对方收到的(通过 ACK 报文中的序列号知道)
  3. 避免资源浪费

既然 IP 层会分片,为什么 TCP 层还需要 MSS 呢?

如果在 TCP 的整个报文(头部 + 数据)交给 IP 层进行分片,当一个 IP 分片丢失,整个 IP 报文的所有分片都得重传。
因为 IP 层本身没有超时重传机制,它由传输层的 TCP 来负责超时和重传
当接收方发现 TCP 报文(头部 + 数据)的某一片丢失后,则不会响应 ACK 给对方,那么发送方的 TCP 在超时后,就会重发「整个 TCP 报文(头部 + 数据)」。
因此,可以得知由 IP 层进行分片传输,是非常没有效率的
所以,为了达到最佳的传输效能 TCP 协议在建立连接的时候通常要协商双方的 MSS 值,当 TCP 层发现数据超过 MSS 时,则就先会进行分片,当然由它形成的 IP 包的长度也就不会大于 MTU ,自然也就不用 IP 分片了。
经过 TCP 层分片后,如果一个 TCP 分片丢失后,进行重发时也是以 MSS 为单位,而不用重传所有的分片,大大增加了重传的效率

TCP四次挥手的过程

image.png

  1. 第一次挥手:客户端认为数据发送完毕,向服务器端发送连接释放请求FIN
  2. 第二次挥手:服务器收到连接释放请求,告诉应用层释放TCP连接。然后发送ACK包,进入CLOSE-WAIT状态,此时表明客户端到服务器端的连接已经释放,不再接受客户端的数据。因为TCP是全双工的,所以服务器仍可以发送数据。
  3. 第三次挥手:当服务器端数据发送完毕,向客户端发送连接释放请求FIN,进入LAST-ACK状态。
  4. 第四次挥手:客户端收到连接释放请求,向服务器端发送确认应答报文,此时客户端进入TIME-WAIT状态,持续2倍的MSL(最长报文段寿命),若期间没有收到服务器端的数据报文,进入CLOSED状态。服务器端收到确认应答后,也进入CLOSED状态。

TCP 为什么需要四次挥手

再来回顾下四次挥手双方发 FIN 包的过程,就能理解为什么需要四次了。

  • 关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。
  • 服务器收到客户端的 FIN 报文时,先回一个 ACK 应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN 报文给客户端来表示同意现在关闭连接。

从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的 ACK 和 FIN 一般都会分开发送,因此是需要四次挥手。

请你说说TIME_WAIT

四次挥手结束,主动方进入TIME_WAIT状态。

  1. TCP连接第四次挥手结束时,主动发起连接释放请求的一方进入TIME_WAIT状态,此时主动发起连接释放请求的一方会等待2MSL(最大报文生存期)才会回到初始状态CLOSED。
  2. 产生TIME_WAIT的原因:
    1. 防止历史连接中的数据,被后面相同四元组的连接错误的接收。序列号和初始化序列号并不是无限递增的,会发生回绕为初始值的情况,所以无法根据序列号来判断新老数据。如果服务端在关闭连接之前发送的报文,被网络延迟了。服务端以相同的四元组重新打开了新连接,前面被延迟的报文这时抵达了客户端,而且该数据报文的序列号刚好在客户端接收窗口内,因此客户端会正常接收这个数据报文,但是这个数据报文是上一个连接残留下来的,这样就产生数据错乱等严重的问题。为了防止历史连接中的数据,被后面相同四元组的连接错误的接收,因此 TCP 设计了 TIME_WAIT 状态,状态会持续 2MSL 时长,这个时间足以让两个方向上的数据包都被丢弃,使得原来连接的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接所产生的。
    2. 保证「被动关闭连接」的一方,能被正确的关闭。当主动发起连接释放请求的一方最后发送ACK确认数据包在网络中丢失时,由于TCP的重传机制,被动关闭的一方会重新发送FIN,在FIN到达主动关闭的一方之前,主动关闭的一方需要维持这条连接,也就是主动的一方TCP资源不可以释放,直到被动关闭一方的FIN到达之后,主动关闭方重新发送ACK确认数据包,经过2MSL时间周期没有再收到被动关闭一方的FIN之后,才会恢复到CLOSED状态,如果没有TIME_WAIT这个状态,当FIN到达时,主动方会用RST来响应,在被动关闭的一方看来似乎是一个错误,实际上是正常的连接释放过程。

TIME_WAIT 过多有什么危害?

危害:

  1. 占用系统资源,比如文件描述符、内存资源、CPU资源、线程资源等;
  2. 占用端口

客户端和服务端TIME_WAIT过多造成的影响是不同的。
客户端:
客户端(发起连接方)都是和「目的 IP+ 目的 PORT 」都一样的服务器建立连接的话,当客户端的 TIME_WAIT 状态连接过多的话,就会受端口资源限制,如果占满了所有端口资源,那么就无法再跟「目的 IP+ 目的 PORT」都一样的服务器建立连接了。
不过,即使是在这种场景下,只要连接的是不同的服务器,端口是可以重复使用的,所以客户端还是可以向其他服务器发起连接的,这是因为内核在定位一个连接的时候,是通过四元组(源IP、源端口、目的IP、目的端口)信息来定位的,并不会因为客户端的端口一样,而导致连接冲突。
服务端:
并不会导致端口资源受限,因为服务端只监听一个端口,而且由于一个四元组唯一确定一个 TCP 连接,因此理论上服务端可以建立很多连接,但是 TCP 连接过多,会占用系统资源,比如文件描述符、内存资源、CPU 资源、线程资源等。

TIME_WAIT 优化

  1. 打开 net.ipv4.tcp_tw_reuse 和 net.ipv4.tcp_timestamps 选项;可以复用处于TIME_WAIT的socket为新的连接所用;
  2. 修改TIME_WAIT最大上限,默认18000,一旦超过这个值,系统会将后面的TIME_WAIT直接重置;
  3. 程序中使用SO_LINGER,调用close后会立即发送RST,跳过TIME_WAIT状态直接关闭,但是这种行为非常危险,不值得提倡。

请你说说 CLOSE_WT

在TCP四次挥手阶段,当主动方提出连接释放请求时,被动方给予响应ACK确认应答,但是TCP连接是全双工的,也需要被动方发送连接释放请求,即FIN。但是被动方并没有立即发送FIN,还在读取数据或者传输数据, 进入CLOSE_WAIT状态。 自身,没有关闭连接。

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

TCP 有一个机制是保活机制。这个机制的原理是这样的:
定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间间隔,发送一个探测报文,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。

如果已经建立了连接,但是服务端的进程崩溃会发生什么

TCP 的连接信息是由内核维护的,所以当服务端的进程崩溃后,内核需要回收该进程的所有 TCP 连接资源,于是内核会发送第一次挥手 FIN 报文,后续的挥手过程也都是在内核完成,并不需要进程的参与,所以即使服务端的进程退出了,还是能与客户端完成 TCP四次挥手的过程

请你说说 TCP 如何实现可靠传输

三次握手、重传、校验和 、流量控制、拥塞控制

  1. 三次握手建立可靠的连接,对发送的数据进行编号,确保交付给应用层的数据是有序的
  2. 接收方收到后会根据校验和进行检查,看数据是否发生了改变,没改变就发送确认号,此时发送方接受到确认号才会将数据从缓冲区删除,如果没有接受到确认号,就会超时重传
  3. 同时TCP还有流量控制拥塞控制

TCP重传机制

常见的重传机制:

  1. 超时重传
    1. 数据包丢失
    2. 确认应答丢失
  2. 快速重传
    1. 不以时间为驱动,而是以数据驱动重传
    2. 连续收到三个相同的ACK
  3. SACK(选择性确认)
    1. 可以将已收到的数据的信息发送给「发送方」
  4. D-SACK
    1. 使用了 SACK 来告诉「发送方」有哪些数据被重复接收了
    2. 好处:
      1. 可以让「发送方」知道,是发出去的包丢了,还是接收方回应的 ACK 包丢了;
      2. 可以知道是不是「发送方」的数据包被网络延迟了;
      3. 可以知道网络中是不是把「发送方」的数据包给复制了;

TCP超时重传机制,时间是多少

TCP可靠性中最重要的一个机制是处理数据超时和重传。TCP协议要求在发送端每发送一个报文段,就启动一个定时器并等待确认信息。接收端成功接收新数据后返回确认信息。若在定时器超时前数据未能被确认,TCP就认为报文段中的数据已丢失或损坏,需要对报文段中的数据重新组织和重传
影响超时重传机制协议效率的一个关键参数是重传超时时间(RTO)。RTO的值被设置过大过小都会对协议造成不利影响。

  1. 如果RTO设置过大将会使发送端经过较长时间的等待才能发现报文段丢失,降低了连接数据传输的吞吐量;
  2. 若RTO过小,发送端尽管可以很快地检测出报文段的丢失,但也可能将一些延迟大的报文段误认为是丢失,造成不必要的重传,浪费了网络资源。

TCP协议使用自适应算法以适应互联网分组传输时延的变化。这种算法的基本要点是TCP监视每个连接的传输时延,由此每一个TCP连接推算出合适的RTO值,当连接时延性能变化时,TCP也能够相应地自动修改RTO的设定,以适应这种网络的变化。 TCP协议采用自适应算法记录数据包的往返时延,并根据往返时延设定RTO的取值。一般来说,RTO的取值会略大于RTT以保证数据包的正常传输。RTO的计算方式为: RTO = RTTs + 4xRTTd 其中RTTs为加权平均往返时间,RTTd是偏差的加权平均值。

说一说TCP的流量控制

得分点 流量控制就是让发送方的发送速率不要过快,让接收方来得及接收所有的数据。
如果发送方把数据发送得过快,接收方可能就来不及接受到所有的数据,中间可能会丢失数据报。流量控制就是让发送方的发送速率不要过快,让接收方来得及接收所有的数据。 利用滑动窗口这个机制可以很方便的实现在TCP连接上控制对方发送数据报的速率。例如:发送方和接受方端建立TCP连接的时候,接受方告诉发送方“我的接收窗口,rwnd= 400”,这时候发送方的发送窗口发送的数据报总大小不能超过接收端给出的接收窗口的数值,这个数值的单位是字节,而不是报文段。

说一说滑动窗口

得分点 流量控制中的窗口会持续的向前滑动,因此这个窗口被称为滑动窗口。
TCP每发送一个数据,就要进行一次确认应答,如果收到上一个数据包的应答才发送下一个数据的话,数据包的往返时间越长,通信效率就会越低。为了解决这个问题TCP引入了窗口的概念,无需等待确认应答,可以继续发送数据,实现累计确认。
窗口是操作系统开辟的一个缓冲区。

  1. 发送方在收到ACK之前会保留窗口中已经发送的数据,如果按期收到ACK,此时数据就可以从缓冲区中清除;
  2. 接受方窗口表示未接受到但可以接受的数据,当收到下一个期望到达的数据时,窗口向后滑动,

当接受方繁忙,应用层无法及时读取缓冲区数据时,接受方的滑动窗口会减少,最后可能会发生窗口关闭;当接受方资源非常紧张时,操作系统直接减少接收缓冲区的大小,应用层又无法及时读取数据,就会发送数据丢包的现象,为了防止这种情况发生,TCP 规定是不允许同时减少缓存又收缩窗口的,而是采用先收缩窗口,过段时间再减少缓存,这样就可以避免了丢包情况。

糊涂窗口综合证

如果接受方太忙,来不及取走接受窗口的数据,那么会导致发送方的窗口越来越小。到最后如果接受方腾出几个字节并告诉发送方现在有几个字节的窗口,那么发送方就会发送几个字节的数据,TCP的效率会变得很低。
糊涂窗口综合症状可以发生在发送方和接受方:

  • 接受方可以通告一个小窗口
  • 发送方可以发送小数据

解决方法:

  1. 不让接受方通告小窗口

    当「窗口大小」小于min( MSS,缓存空间/2 ),也就是小于 MSS 与 1/2 缓存大小中的最小值时,就会向发送方通告窗口为 0,也就阻止了发送方再发数据过来。
    等到接收方处理了一些数据后,窗口大小大于 **min( MSS,缓存空间/2 )**,就可以把窗口打开让发送方发送数据过来。

  2. 发送方避免发送小数据:

    使用 Nagle 算法,该算法的思路是延时处理,只有满足下面两个条件中的任意一个条件,才可以发送数据:

    1. 等到窗口大小 >= MSS **并且 **数据大小 >= MSS
    2. 收到之前发送数据的ACK包(要求TCP连接上最多只能有一个未被确认的小分组)
1
2
3
4
5
6
7
8
9
10
11
if 有数据要发送 {
if 可用窗口大小 >= MSS and 可发送的数据 >= MSS {
立刻发送MSS大小的数据
} else {
if 有未确认的数据 {
将数据放入缓存等待接收ACK
} else {
立刻发送数据 // 仍然会发送小数据
}
}
}

TCP 是如何解决窗口关闭时,潜在的死锁现象呢?

TCP 为每个连接设有一个持续定时器,只要 TCP 连接一方收到对方的零窗口通知,就启动持续计时器
如果持续计时器超时,就会发送窗口探测 ( Window probe ) 报文,而对方在确认这个探测报文时,给出自己现在的接收窗口大小。

  • 如果接收窗口仍然为 0,那么收到这个报文的一方就会重新启动持续计时器;
  • 如果接收窗口不是 0,那么死锁的局面就可以被打破了。
  • 如果 3 次过后接收窗口还是 0 的话,有的 TCP 实现就会发 RST 报文来中断连接。

说说TCP的拥塞控制机制

防止太多的数据进入到网络中,四个算法:慢启动、拥塞避免、快重传、快恢复
image.png
拥塞控制就是防止太多的数据进入到网络中,这样可以使网络中的路由器或者链路不会过载,首先要求当前的网络可以承受住现有的网络负荷,它是一个全局性的过程,拥塞控制的算法有以下四种:慢启动、拥塞避免、快重传、快恢复

  1. 慢启动(slow-start):当客户端发送数据的时候,如果一次性把大量的数据字节发送到网络中,就有可能引起网络拥塞,因为并不清楚网络的负荷状态。所以较好的方法是先探测一下,由小到大逐渐增大发送窗口,也就是慢慢地增大窗口数值。通常刚开始发送报文段时先把拥塞窗口cwnd设置为一个最大报文段MSS的值,每收到一对新的报文段确认后,把拥塞窗口的数值再加一个MSS(指数增加)
  2. 拥塞避免(congestion avoidance):让拥塞窗口cwnd缓缓地增大,即每经过一个往返时间RTT就把发送方的拥塞窗口cwnd加1,而不是加倍,让拥塞窗口按照线性规律慢慢增长,比慢开始算法的拥塞窗口增长速率慢很多(线性增加)
  3. 快重传(fast retransmit):要求接收方每收到一个失序的报文段之后就立即发出重复确认而不是等待自己发送数据时捎带确认,为的就是让发送方能尽早地知道有报文段没有到达接收方。
  4. 快恢复(fast recovery):两个要点,一是当发送方连续收到三个重复确认时,就执行”乘法减小“算法,把慢开始门限ssthresh减半,这是为了预防网络发生拥塞。二是发送方认为网络很可能没有发生阻塞,因此不会执行慢开始算法,而是把cwnd值设置成慢开始门限ssthresh减半之后的数值(减半+3),然后执行拥塞避免算法,使拥塞窗口呈线性增长。

请你说说 TCP 粘包

得分点TCP基于字节流,无法判断发送方报文段边界,一个完整的业务可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这个就是TCP的拆包和粘包问题。
粘包的原因:

  1. 应用程序写入数据的字节大小大于套接字发送缓冲区的大小;
  2. 进行MSS(最大段长度)的TCP分段;
  3. 以太网的payload大于MTU分片。

解决办法:

  1. 固定长度的消息;
  2. 特殊字符作为边界,如http中的回车换行;
  3. 自定义的消息结构。

UDP怎么实现可靠的传输

得分点 将运输层TCP的可靠传输机制在应用层实现
UDP不是面向连接的协议,因此资源消耗小,处理速度快的优点,所以通常音频、视频和普通数据在传送时使用UDP较多,因为它们即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。如果想要使用UDP还要保证数据的可靠传输,就要将TCP在传输层功能,如确认机制、重传功能、流量控制、拥塞控制等功能实现在了应用层。
现在已经有基于 UDP 协议实现的可靠传输协议的成熟方案了,那就是 QUIC 协议,已经应用在了 HTTP/3。

一、QUIC的可靠传输
要基于UDP完成可靠的传输,需要解决两个主要的问题:

  1. 超时重传
  2. 序列号

QUIC主要是通过Packet Header和 QUIC Frame Header实现可靠传输的:

  1. Packet Header,分为Long Packet Header 和 Short Packet Header:
    1. Long Packet Header用于首次建立连接,QUIC也需要三次握手建立连接,目的是为了协商连接ID,后续传输时双方只需要固定住连接 ID,从而实现连接迁移功能。
    2. Short Packet Header用于数据传输,Short Packet Header中有一个Packet Number,它是每个报文唯一的编号,是严格递增的,也就是说就算 Packet N丢失了,重传的数据包的Packet Number也不是N了。这样设计的目的是为了解决TCP重传歧义的问题,TCP发生重传时,发送端无法判断出是「原始报文的响应」还是「重传报文的响应」,这样在计算 RTT(往返时间)就不精准,会影响重传时间的计算;
  2. QUIC Frame Header,一个Packet报文中可以存放多个 QUIC Frame。每个Frame都有不同的类型,功能和格式都不相同。Stream类型的报文就可以看作HTTP报文,包含:
    1. Stream ID,作用:用于区别多个并发传输的 HTTP 消息,,类似于 HTTP2 的 Stream ID;
    2. Offset:类似于 TCP 协议中的 Seq 序号,保证数据的顺序性和可靠性
    3. Length:指明Frame数据的长度;
    4. 负载数据。

基于TCP 的HTTP2.0多个stream共同使用一个滑动窗口,会因为某个数据包重传而导致窗口队头阻塞,QUIC为每个stream都分配了一个独立的滑动窗口,通过单向递增的 Packet Number,配合 Stream ID 与 Offset 字段信息,可以支持乱序确认而不影响数据包的正确组装,摆脱了TCP 必须按顺序确认应答 ACK 的限制,解决了 TCP 因某个数据包重传而阻塞后续所有待发送数据包的问题。

二、QUIC的流量控制
QUIC 实现了两种级别的流量控制,分别为 Stream 和 Connection 两种级别:

  1. Stream 级别的流量控制:Stream 可以认为就是一条 HTTP 请求,每个 Stream 都有独立的滑动窗口,所以每个 Stream 都可以做流量控制,防止单个 Stream 消耗连接(Connection)的全部接收缓冲。
  2. Connection 流量控制:限制连接中所有 Stream 相加起来的总字节数,防止发送方超过连接的缓冲容量。

三、QUIC的拥塞控制
QUIC 协议当前默认使用了 TCP 的 拥塞控制算法,但QUIC 是处于应用层的,应用程序层面就能实现不同的拥塞控制算法,不需要操作系统内核支持。传统的 TCP 拥塞控制,必须要端到端的网络协议栈支持,才能实现控制效果。而内核和操作系统的部署成本非常高,升级周期很长,所以 TCP 拥塞控制算法迭代速度是很慢的。而 QUIC 可以随浏览器更新,QUIC 的拥塞控制算法就可以有较快的迭代速度。

四、QUIC的连接迁移
基于 TCP 传输协议的 HTTP 协议,由于是通过四元组(源 IP、源端口、目的 IP、目的端口)确定一条 TCP 连接。
那么当移动设备的网络从 4G 切换到 WIFI 时,意味着 IP 地址变化了,那么就必须要断开连接,然后重新建立 TCP 连接
而建立连接的过程包含 TCP 三次握手和 TLS 四次握手的时延,因此连接的迁移成本是很高的。
QUIC 协议没有用四元组的方式来“绑定”连接,而是通过连接 ID来标记通信的两个端点,客户端和服务器可以各自选择一组 ID 来标记自己,因此即使移动设备的网络变化后,导致 IP 地址变化了,只要仍保有上下文信息(比如连接 ID、TLS 密钥等),就可以“无缝”地复用原连接,消除重连的成本,达到了连接迁移的功能。

请你说说 TCP 和 UDP 的使用场景

回答角度:优缺点,应用场景
UDP的优点,没有TCP各种机制,少了很多首部信息和重复确认的过程,节省了大量的网络资源。缺点不可靠不稳定,只管数据的发送不管过程和结果,网络不好的时候很容易造成数据丢失。又因为网络不好的时候不会影响到主机数据报的发送速率,这对很多实时的应用程序很重要,因为像语音通话、视频会议等要求源主机要以恒定的速率发送数据报,允许网络不好的时候丢失一些数据,但不允许太大的延迟。DNS和ARP协议也是基于UDP实现的,要求快速获取IP、MAC地址,如果基于TCP那么对整个因特网的资源占用过大且速度慢。还有游戏应用程序也是通过UDP来传输报文段,允许出现丢帧导致的卡顿,但是对游戏的整体体验不会产生严重的影响。所以UDP在语音、视频、寻址、游戏、广播方面有很好的应用前景,实时性高,允许部分的数据丢失。
TCP的优点面向连接提供可靠交付,即对数据有保证、无差错的进行运输。当需要数据准确无误的运输给对方时,如浏览器中需要获取服务器资源使用的HTTP/HTTPS协议,需要保证文件准确、无差错,邮件服务器中使用的SMTP协议,保证邮件内容准确无误的传递给对方,或者是大型应用程序文件,这些都要保证文件的准确、无差错的运输给对方,所以一定要基于TCP来运输,而不是UDP。
综上,通信数据完整性要求高于通信实时性时,则应选用TCP(如文件传输、重要状态的更新等);反之,采用UDP(如 视频传输、实时通信等)。

在 TIME_WAIT 状态的 TCP 连接,收到 SYN 后会发生什么?

  1. 合法的SYN:如果客户端的 SYN 的「序列号」比服务端「期望下一个收到的序列号」要大,并且SYN 的「时间戳」比服务端「最后收到的报文的时间戳」要大。那么就会重用该四元组连接,跳过 2MSL 而转变为 SYN_RECV 状态,接着就能进行建立连接过程
  2. 非法的SYN:如果客户端的 SYN 的「序列号」比服务端「期望下一个收到的序列号」要小,或者SYN 的「时间戳」比服务端「最后收到的报文的时间戳」要小。那么就会重传第四次挥手的 ACK 报文,客户端收到后,发现并不是自己期望收到确认号,就回 RST 报文给服务端

TCP Keepalive 和 HTTP Keep-Alive 是一个东西吗?

这两个完全是两样不同东西,实现的层面也不同:

  • HTTP 的 Keep-Alive,是由应用层(用户态) 实现的,称为 HTTP 长连接
  • TCP 的 Keepalive,是由 TCP 层(内核态) 实现的,称为 TCP 保活机制

cookies方案为什么不直接取代半连接队列?

目前看下来syn cookies方案省下了半连接队列所需要的队列内存,还能解决 SYN Flood攻击,那为什么不直接取代半连接队列?
凡事皆有利弊,cookies方案虽然能防 SYN Flood攻击,但是也有一些问题。因为服务端并不会保存连接信息,所以如果传输过程中数据包丢了,也不会重发第二次握手的信息。
另外,编码解码cookies,都是比较耗CPU的,利用这一点,如果此时攻击者构造大量的第三次握手包(ACK包),同时带上各种瞎编的cookies信息,服务端收到ACK包后以为是正经cookies,憨憨地跑去解码(耗CPU),最后发现不是正经数据包后才丢弃。
这种通过构造大量ACK包去消耗服务端资源的攻击,叫ACK攻击,受到攻击的服务器可能会因为CPU资源耗尽导致没能响应正经请求。

网络层

ARP协议

在传输一个 IP 数据报的时候,确定了源 IP 地址和目标 IP 地址后,就会通过主机「路由表」确定 IP 数据包下一跳。然而,网络层的下一层是数据链路层,所以我们还要知道「下一跳」的 MAC 地址。由于主机的路由表中可以找到下一跳的 IP 地址,所以可以通过 ARP 协议,求得下一跳的 MAC 地址。
ARP提供了将IP地址转换为链路层地址的机制,而且只为在同一个子网上的主机和路由器接口解析IP地址。 ARP寻址的具体过程如下:
假设主机A和B在同一个网段,主机A要向主机B发送数据报文,具体的地址解析过程如下:

  1. 主机A首先查看自己的ARP缓存表,确定其中是否包含有主机B对应的ARP表项。如果找到了对应的MAC地址,则主机A直接利用ARP表中的MAC地址,对IP数据包进行帧封装,并将数据包发送给主机B;
  2. 如果主机A在ARP表中找不到对应的MAC地址,则将缓存该数据报文,然后以广播方式发送一个ARP请求报文。ARP请求报文中的发送端IP地址和发送端MAC地址为主机A的IP地址和MAC地址,目标IP地址和目标MAC地址为主机B的IP地址和全0的MAC地址。由于ARP请求报文以广播方式发送,该网段上的所有主机都可以接收到该请求,但只有被请求的主机(即主机B)会对该请求进行处理
  3. 主机B比较自己的IP地址和ARP请求报文中的目标IP地址,当两者相同时进行如下处理:将ARP请求报文中的发送端(即主机A)的IP地址和MAC地址存入自己的ARP表中。之后以单播方式发送ARP响应报文给主机A,其中包含了自己的MAC地址。
  4. 主机A收到ARP响应报文后,将主机B的MAC地址加入到自己的ARP表中以用于后续报文的转发,同时将IP数据包进行封装后发送出去。

当主机A和主机B不在同一网段时,主机A就会先向网关发出ARP请求,ARP请求报文中的目标IP地址为网关的IP地址。当主机A从收到的响应报文中获得网关的MAC地址后,将报文封装并发给网关。如果网关没有主机B的ARP表项,网关会广播ARP请求,目标IP地址为主机B的IP地址,当网关从收到的响应报文中获得主机B的MAC地址后,就可以将报文发给主机B;如果网关已经有主机B的ARP表项,网关直接把报文发给主机B。
ARP表项又分为动态ARP表项和静态ARP表项。

  1. 动态ARP表项由ARP协议通过ARP报文自动生成和维护,可以被老化,可以被新的ARP报文更新,可以被静态ARP表项覆盖。
  2. 静态ARP表项通过手工配置和维护,不会被老化,不会被动态ARP表项覆盖。直到重新启动计算机为止。

DHCP 协议

  1. 客户端首先发起 **DHCP 发现报文(DHCP DISCOVER) **的 IP 数据报,由于客户端没有 IP 地址,也不知道 DHCP 服务器的地址,所以使用的是 UDP 广播通信,其使用的广播目的地址是 255.255.255.255(端口 67) 并且使用 0.0.0.0(端口 68) 作为源 IP 地址。DHCP 客户端将该 IP 数据报传递给链路层,链路层然后将帧广播到所有的网络中设备。
  2. DHCP 服务器收到 DHCP 发现报文时,用 **DHCP 提供报文(DHCP OFFER) **向客户端做出响应。该报文仍然使用 IP 广播地址 255.255.255.255,该报文信息携带服务器提供可租约的 IP 地址、子网掩码、默认网关、DNS 服务器以及 IP 地址租用期。
  3. 客户端收到一个或多个服务器的 DHCP 提供报文后,从中选择一个服务器,并向选中的服务器发送 DHCP 请求报文(DHCP REQUEST)进行响应,回显配置的参数。
  4. 最后,服务端用DHCP ACK 报文对 DHCP 请求报文进行响应,应答所要求的参数。

NAT

NAT 就是同个公司、家庭、教室内的主机对外部通信时,把私有 IP 地址转换成公有 IP 地址。两个私有 IP 地址都转换 IP 地址为公有地址 120.229.175.121,但是以不同的端口号作为区分。
image.png

由于 NAT/NAPT 都依赖于自己的转换表,因此会有以下的问题:

  • 外部无法主动与 NAT 内部服务器建立连接,因为 NAPT 转换表没有转换记录。
  • 转换表的生成与转换操作都会产生性能开销
  • 通信过程中,如果 NAT 路由器重启了,所有的 TCP 连接都将被重置

解决的方法主要有两种方法。

  1. 第一种就是改用 IPv6

IPv6 可用范围非常大,以至于每台设备都可以配置一个公有 IP 地址,就不搞那么多花里胡哨的地址转换了,但是 IPv6 普及速度还需要一些时间。

  1. 第二种 NAT 穿透技术

NAT 穿透技术拥有这样的功能,它能够让网络应用程序主动发现自己位于 NAT 设备之后,并且会主动获得 NAT 设备的公有 IP,并为自己建立端口映射条目,注意这些都是 NAT设备后的应用程序自动完成的。
也就是说,在 NAT 穿透技术中,NAT设备后的应用程序处于主动地位,它已经明确地知道 NAT 设备要修改它外发的数据包,于是它主动配合 NAT 设备的操作,主动地建立好映射,这样就不像以前由 NAT 设备来建立映射了。
说人话,就是客户端主动从 NAT 设备获取公有 IP 地址,然后自己建立端口映射条目,然后用这个条目对外通信,就不需要 NAT 设备来进行转换了。

ICMP

image.png

IGMP

  • IGMP 报文向路由器申请加入和退出组播组,默认情况下路由器是不会转发组播包到连接中的主机,除非主机通过 IGMP 加入到组播组,主机申请加入到组播组时,路由器就会记录 IGMP 路由器表,路由器后续就会转发组播包到对应的主机了。
  • IGMP 报文采用 IP 封装,IP 头部的协议号为 2,而且 TTL 字段值通常为 1,因为 IGMP 是工作在主机与连接的路由器之间。

ping 的工作原理

image.png

traceroute的工作原理

traceroute 的第一个作用就是故意设置特殊的 TTL,来追踪去往目的地时沿途经过的路由器
它的原理就是利用 IP 包的生存期限 从 1 开始按照顺序递增的同时发送 UDP 包,强制接收 ICMP 超时消息的一种方法。
比如,将 TTL 设置 为 1,则遇到第一个路由器,就牺牲了,接着返回 ICMP 差错报文网络包,类型是时间超时。接下来将 TTL 设置为 2,第一个路由器过了,遇到第二个路由器也牺牲了,也同时返回了 ICMP 差错报文数据包,如此往复,直到到达目的主机。这样的过程,traceroute 就可以拿到了所有的路由器 IP。
traceroute 在发送 UDP 包时,会填入一个不可能的端口号值作为 UDP 目标端口号:33434。然后对于每个下一个探针,它都会增加一个,这些端口都是通常认为不会被使用,不过,没有人知道当某些应用程序监听此类端口时会发生什么。
所以,当差错报文类型是端口不可达时,说明发送方发出的 UDP 包到达了目的主机。

traceroute 还有一个作用是故意设置不分片,从而确定路径的 MTU
它的工作原理如下:
首先在发送端主机发送 IP 数据报时,将 IP 包首部的分片禁止标志位设置为 1。根据这个标志位,途中的路由器不会对大数据包进行分片,而是将包丢弃。
随后,通过一个 ICMP 的不可达消息将数据链路上 MTU 的值一起给发送主机,不可达消息的类型为「需要进行分片但设置了不分片位」。
发送主机端每次收到 ICMP 差错报文时就减少包的大小,以此来定位一个合适的 MTU 值,以便能到达目标主机。

计算机网络中的安全

安全攻击有哪些

ARP 攻击

对称加密和非对称的区别,非对称加密有哪些

得分点 密钥、公钥、私钥

  1. 对称加密:对称加密指的就是加密和解密使用同一个秘钥,所以叫做对称加密。对称加密只有一个秘钥,作为私钥。常见的对称加密算法有:DES、AES、3DES等。
  2. 非对称加密:非对称加密指的是:加密和解密使用不同的秘钥,一把作为公开的公钥,另一把作为私钥。公钥加密的信息,只有私钥才能解密。私钥加密的信息,只有公钥才能解密。常见的非对称加密算法:RSA,ECC等。
  3. 对称加密和非对称加密相比安全性低,因为加密和解密是同一个密钥,数据包被拦截之后不安全。而非对称加密中,公钥用来加密,私钥用来解密。公钥可以公开给任何用户进行加密,私钥永远在服务器或某个客户端手里,非常安全,数据被拦截也没用,因为私钥未公开就永远无法打开数据包。

DDoS 有哪些,如何防范

DDoS 为分布式拒绝服务攻击,是指处于不同位置的多个攻击者同时向一个或数个目标发动攻击,或者一个攻击者控制了不同位置上的多台机器并利用这些机器对受害者同时实施攻击。和单一的 DoS 攻击相比,DDoS 是借助数百台或者数千台已被入侵并添加了攻击进程的主机一起发起网络攻击。

DDoS 攻击主要有两种形式:流量攻击资源耗尽攻击。前者主要针对网络带宽,攻击者和已受害主机同时发起大量攻击导致网络带宽被阻塞,从而淹没合法的网络数据包;后者主要针对服务器进行攻击,大量的攻击包会使得服务器资源耗尽或者 CPU 被内核应用程序占满从而无法提供网络服务。

常见的 DDos 攻击主要有:TCP 洪水攻击(SYN Flood)、放射性攻击(DrDos)、CC 攻击(HTTP Flood)等。

针对 DDoS 中的流量攻击,最直接的方法是增加带宽,理论上只要带宽大于攻击流量就可以了,但是这种方法成本非常高。在有充足网络带宽的前提下,我们应尽量提升路由器、网卡、交换机等硬件设施的配置。

针对资源耗尽攻击,我们可以升级主机服务器硬件,在网络带宽得到保证的前提下,使得服务器能有效对抗海量的 SYN 攻击包。我们也可以安装专业的抗 DDoS 防火墙,从而对抗 SYN Flood等流量型攻击。此外,负载均衡,CDN 等技术都能够有效对抗 DDoS 攻击

防御 SYN 攻击的方法

SYN攻击:当服务器受到SYN攻击,可能会导致TCP半连接队列全满,这时后面来的SYN包都会被丢弃。
防御SYN攻击的方法有以下几种:

  1. 增大半连接队列;
  2. 开启tcp_cookies功能;
  3. 减少SYN+ACK的重传次数(及时断开半连接队列)

DNS劫持

在完成整个域名解析的过程之后,并没有收到本该收到的IP地址,而是接收到了一个错误的IP地址。比如输入的网址是百度,但是却进入了奇怪的网址,并且地址栏依旧是百度。在这个过程中,攻击者一般是修改了本地路由器的DNS地址,从而访问了一个伪造的DNS服务器,这个伪造的服务器解析域名的时候返回了一个攻击者精心设计的网站,这个网站可能和目标网站一模一样,当用户输入个人账户时,数据会发送给攻击者,从而造成个人财产的丢失。
预防DNS劫持可以通过以下几种方法:

  1. 准备多个域名,当某个域名被劫持时,暂时使用另一个
  2. 手动修改DNS,在地址栏输入http://192.168.1.1,进入路由器配置,填写主DNS服务器为114.114.114.114,填写备用DNS服务器为8.8.8.8
  3. 修改路由器密码
  4. 给运营商打投诉电话,讲明被劫持的情况

网络编程相关

socket 编程

accept发生在三次握手哪个阶段

img

其他

如何查看TCP的连接状态

TCP 的连接状态查看,在 Linux 可以通过 netstat -napt 命令查看。
image.png

常用默认端口号

DNS:43
HTTP:80
HTTPS: 443
TCP:80
SQL:3306
redis:6379
SSH:22
tomcat:8080

假设客户端有多个网卡,就会有多个 IP 地址,那 IP 头部的源地址应该选择哪个 IP 呢?

根据路由表规则,将目的ip与每个条目的子网掩码进行与运算,目的子网号匹配成功时将该网卡的ip地址作为源ip地址,如果都不匹配,则交给默认网关。

查看ARP缓存内容

在 Linux 系统中,我们可以使用 arp -a命令来查看 ARP 缓存的内容。


八股文之计算机网络
https://ww1820.github.io/posts/947f1ab2/
作者
AWei
发布于
2022年10月9日
更新于
2022年10月10日
许可协议