【檀越剑指大厂—网络协议】网络协议基础与进阶

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。

img

非常期待和您一起在这个小小的网络世界里共同探索、学习和成长。💝💝💝

✨✨ 欢迎订阅本专栏 ✨✨

博客目录

一.基础知识

1.什么是计算机网络?

计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。

网络编程的目的:

  • 无线电台

  • 传播交流信息

  • 数据交换

  • 通信

2.网络模型

为了使得多种设备能通过网络相互通信,和为了解决各种不同设备在网络互联中的兼容性问题,国际标准化组织制定了开放式系统互联通信参考模型(Open System Interconnection Reference Model),也就是 OSI 网络模型,该模型主要有 7 层,分别是应用层、表示层、会话层、传输层、网络层、数据链路层以及物理层。

每一层负责的职能都不同,如下:

  • 应用层,负责给应用程序提供统一的接口;
  • 表示层,负责把数据转换成兼容另一个系统能识别的格式;
  • 会话层,负责建立、管理和终止表示层实体之间的通信会话;
  • 传输层,负责端到端的数据传输;
  • 网络层,负责数据的路由、转发、分片;
  • 数据链路层,负责数据的封帧和差错检测,以及 MAC 寻址;
  • 物理层,负责在物理网络中传输数据帧;

TCP/IP 网络模型共有 4 层,分别是应用层、传输层、网络层和网络接口层,每一层负责的职能如下:

  • 应用层,负责向用户提供一组应用程序,比如 HTTP、DNS、FTP 等;
  • 传输层,负责端到端的通信,比如 TCP、UDP 等;
  • 网络层,负责网络包的封装、分片、路由、转发,比如 IP、ICMP 等;
  • 网络接口层,负责网络包在物理网络中的传输,比如网络包的封帧、 MAC 寻址、差错检测,以及通过网卡传输网络帧等;

image-20230411104214354

image-20230414215446705

3.应用层

网络分层中最上面一层,是应用层,也是我们能直接接触的一层,我们电脑或手机使用的应用软件都是在应用层实现。那么,当两个不同设备的应用需要通信的时,应用或者浏览器就把应用数据传给下一层,也就是传输层。

所以,应用层只需要专注于为用户提供应用功能.应用层是不用去关心数据是如何传输的.

而且应用层操作是在操作系统中的用户态,传输层及以下层则工作在内核态。

在计算机网络中,应用层是网络模型中的最高层,它负责为用户提供网络服务。应用层定义了网络应用程序使用的协议,以便它们可以互相通信和交换数据。

以下是一些常见的应用层协议和应用:

  1. HTTP(超文本传输协议):用于在 Web 浏览器和 Web 服务器之间传输 HTML 页面和其他数据。
  2. FTP(文件传输协议):用于在计算机之间传输文件。
  3. SMTP(简单邮件传输协议):用于电子邮件的传输。
  4. DNS(域名系统):用于将域名解析为 IP 地址。
  5. Telnet:远程登录协议,允许用户通过网络连接到远程计算机。
  6. SSH(安全外壳协议):用于通过网络连接到远程计算机,提供安全的加密通信。
  7. DHCP(动态主机配置协议):用于动态地为网络设备分配 IP 地址和其他网络配置信息。
  8. SNMP(简单网络管理协议):用于监控和管理网络设备。
  9. IRC(Internet Relay Chat):用于在线聊天和交流。
  10. SIP(会话初始化协议):用于 Internet 电话和视频通话。

这些应用层协议和应用程序使得我们可以在网络上进行各种活动,例如浏览网页、发送电子邮件、下载文件、进行在线交流等。

除了上述常见的应用层协议和应用程序之外,还有许多其他的应用层协议和应用程序,例如:

  1. MQTT(消息队列遥测传输):用于在物联网(IoT)设备之间传输数据。
  2. XMPP(可扩展通信和预约协议):用于实时通信,例如即时消息和在线语音通话。
  3. RTP(实时传输协议):用于在音频和视频应用程序之间传输实时流媒体。
  4. SNMP(简单网络管理协议):用于管理网络设备,例如路由器、交换机和服务器等。
  5. LDAP(轻量目录访问协议):用于在网络上访问和管理目录服务,例如用户和组等。
  6. NNTP(网络新闻传输协议):用于在网络上访问和传输新闻组文章和消息。
  7. NTP(网络时间协议):用于同步网络设备的时间。
  8. STUN(简单穿越 UDP 协议):用于在 NAT(网络地址转换)后的设备之间建立实时通信。
  9. DNS-SD(DNS 服务发现):用于在网络上发现可用的服务。
  10. CoAP(受限制的应用协议):用于在低功率设备之间传输数据,例如物联网(IoT)设备。

这些应用层协议和应用程序在网络中发挥着重要的作用,使得我们可以更加便捷、高效地进行数据交换和通信。

4.传输层

应用层的数据包会传给传输层,传输层是为应用层提供网络支持的。

网络模型中的传输层是指 OSI 七层模型中的第四层,它负责为数据传输提供端到端的可靠性、流量控制、错误检测和恢复等服务。在 TCP/IP 协议中,传输层主要有两个协议:传输控制协议(TCP)和用户数据报协议(UDP)。

TCP 是一个可靠的协议,它能够确保数据的正确传输,并且在传输过程中能够控制数据的流量,从而避免网络拥塞。它还提供了错误检测和恢复机制,确保数据的完整性。TCP 通常用于传输重要数据,如电子邮件、文件传输等。

UDP 是一种不可靠的协议,它只提供最基本的数据传输功能,不保证数据的正确性和可靠性。UDP 通常用于实时应用程序,如在线游戏、流媒体等,因为在这些应用中,速度和实时性比可靠性更加重要。

传输层也可以使用其他协议,如 SCTP、DCCP 等。这些协议在特定场景下可能比 TCP 和 UDP 更加适合。

除了提供可靠的数据传输和流量控制,传输层还有以下主要功能:

  1. 分段和重组:当数据量较大时,当传输层的数据包大小超过 MSS(TCP 最大报文段长度),传输层会将数据分成多个小的数据段进行传输,我们把每个分块称为一个 TCP 段TCP Segment),接收端再将这些数据段重组成原始数据。这样做有利于提高数据传输效率。
  2. 端口号:传输层使用端口号来标识不同的应用程序。发送端通过源端口号标识自己的应用程序,接收端通过目的端口号确定应该将数据传输给哪个应用程序。
  3. 会话控制:传输层还提供了会话控制功能,可以在数据传输前建立一个会话,并在传输过程中维护该会话。这样做可以确保数据的正确传输,同时还能够提高数据传输的效率。
  4. 拥塞控制: 当网络拥塞时,传输层还能够根据拥塞控制算法来控制数据传输速率,从而保证网络的稳定性和可靠性。
  5. 安全加密: 传输层还经常使用加密技术来保护数据的安全性,例如使用 TLS/SSL 协议来加密数据传输。

总之,传输层在网络模型中扮演着至关重要的角色,它为网络上的应用程序提供了可靠的数据传输服务,保证了数据的完整性、可靠性和高效性。

由于传输层的报文中会携带端口号,因此接收方可以识别出该报文是发送给哪个应用。

5.网络层

在网络分层中,网络层是其中一个重要的层次,它位于传输层和数据链路层之间。传输层可能大家刚接触的时候,会认为它负责将数据从一个设备传输到另一个设备,事实上它并不负责。数据传输由网络层负责.

网络层的主要功能是实现逻辑地址的寻址和路由选择,以便将数据包从源主机发送到目的主机。具体来说,网络层将上层数据(即传输层数据段)添加一个 IP 头部,形成一个 IP 数据包,然后通过路由选择算法将数据包发送到目标主机。

  1. 逻辑地址的寻址:通过子网掩码进行寻址
  2. 路由选择:然后通过路由选择算法将数据包发送到目标主机。
  3. 分段和重组:网络层可以将传输层的数据段分成更小的片段,以适应不同网络的最大传输单元(MTU)。然后,在接收方,网络层可以将这些片段重组成原始数据。
  4. 网络拓扑:网络层可以决定网络的拓扑结构,包括网络中节点之间的连接方式,以及路由器的位置和数量。
  5. 流量控制和拥塞控制:网络层可以实现一些流量控制和拥塞控制机制,以确保网络中的数据流量不会超过网络的处理能力。
  6. 安全性:网络层可以实现一些安全措施,如 IPSec 协议,来保护数据在传输过程中的安全性。
  7. 负载均衡:网络层可以将流量均衡地分配到不同的路径上,以确保网络中的流量不会集中在某个路径上,导致网络拥塞。
  8. IPv4 和 IPv6:网络层支持 IPv4 和 IPv6 协议,其中 IPv4 是互联网上最常用的协议,而 IPv6 则是未来网络的趋势,以支持更多的 IP 地址。
  9. VPN(虚拟专用网络):网络层可以支持 VPN 技术,使得在公共网络上的通信变得更加安全和私密。
  10. NAT(网络地址转换):网络层可以支持 NAT 技术,将一个公网 IP 地址转换成多个私有 IP 地址,以支持局域网中的多个计算机共享同一个公网 IP 地址。

网络层的协议有很多种,其中最常见的是 Internet Protocol(IP 协议)。IP 协议会将传输层的报文作为数据部分,再加上 IP 包头组装成 IP 报文,如果 IP 报文大小超过 MTU(以太网中一般为 1500 字节)就会再次进行分片,得到一个即将发送到网络的 IP 报文。IP 协议是互联网中最重要的协议之一,它负责在互联网中传输数据包,并通过路由选择算法找到最佳路径。其他常见的网络层协议包括 IPX、AppleTalk 等。

img

网络层也对于网络的性能和可靠性有着重要的影响,它可以通过以下方式来提高网络的性能和可靠性:

  1. 路由算法优化:网络层可以采用不同的路由算法,如距离向量算法、链路状态算法、路径向量算法等,以提高路由选择的效率和准确性。
  2. TTL(生存时间):网络层可以通过 TTL 字段来控制数据包在网络中的最大生存时间,以避免数据包在网络中无限循环,浪费网络资源。
  3. 网络拓扑优化:网络层可以通过重新设计网络拓扑,调整节点位置和路由器数量等方式来优化网络的性能和可靠性。
  4. 多路径路由:网络层可以通过多路径路由技术,将数据流量分配到不同的路径上,提高网络的吞吐量和可靠性。

6.每层封装格式

网络接口层的传输单位是帧(frame),IP 层的传输单位是包(packet),TCP 层的传输单位是段(segment),HTTP 的传输单位则是消息或报文(message)。但这些名词并没有什么本质的区分,可以统称为数据包。

image-20230411104131752

  • 网络接口层(链路层):包括操作系统中的设备驱动程序、计算机中对应的网络接口卡
  • 网络层(互连层、网际层):处理分组在网络中的活动,比如分组的选路。负责 IP 寻址,分割和组装数据包,路由选择等。
  • 运输层:主要为两台主机上的应用提供端到端的通信
  • 应用层:负责处理特定的应用程序细节。

7.接收包的流程

网卡是计算机里的一个硬件,专门负责接收和发送网络包,当网卡接收到一个网络包后,会通过 DMA 技术,将网络包写入到指定的内存地址,也就是写入到 Ring Buffer ,这个是一个环形缓冲区,接着就会告诉操作系统这个网络包已经到达。

那应该怎么告诉操作系统这个网络包已经到达了呢?

为了解决频繁中断带来的性能开销,Linux 内核在 2.6 版本中引入了 NAPI 机制,它是混合「中断和轮询」的方式来接收网络包,它的核心概念就是不采用中断的方式读取数据,而是首先采用中断唤醒数据接收的服务程序,然后 poll 的方法来轮询数据。

当有网络包到达时,会通过 DMA 技术,将网络包写入到指定的内存地址,接着网卡向 CPU 发起硬件中断,当 CPU 收到硬件中断请求后,根据中断表,调用已经注册的中断处理函数。

硬件中断处理函数会做如下的事情:

  • 需要先「暂时屏蔽中断」,表示已经知道内存中有数据了,告诉网卡下次再收到数据包直接写内存就可以了,不要再通知 CPU 了,这样可以提高效率,避免 CPU 不停的被中断。
  • 接着,发起「软中断」,然后恢复刚才屏蔽的中断。

至此,硬件中断处理函数的工作就已经完成。

硬件中断处理函数做的事情很少,主要耗时的工作都交给软中断处理函数了。

网络协议栈的流程:

首先,会先进入到网络接口层,在这一层会检查报文的合法性,如果不合法则丢弃,合法则会找出该网络包的上层协议的类型,比如是 IPv4,还是 IPv6,接着再去掉帧头和帧尾,然后交给网络层。

到了网络层,则取出 IP 包,判断网络包下一步的走向,比如是交给上层处理还是转发出去。当确认这个网络包要发送给本机后,就会从 IP 头里看看上一层协议的类型是 TCP 还是 UDP,接着去掉 IP 头,然后交给传输层。

传输层取出 TCP 头或 UDP 头,根据四元组「源 IP、源端口、目的 IP、目的端口」 作为标识,找出对应的 Socket,并把数据放到 Socket 的接收缓冲区。

最后,应用层程序调用 Socket 接口,将内核的 Socket 接收缓冲区的数据「拷贝」到应用层的缓冲区,然后唤醒用户进程。

至此,一个网络包的接收过程就已经结束了,你也可以从下图左边部分看到网络包接收的流程,右边部分刚好反过来,它是网络包发送的流程。

image-20230411104330953

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

第一次,调用发送数据的系统调用的时候,内核会申请一个内核态的 sk_buff 内存,将用户待发送的数据拷贝到 sk_buff 内存,并将其加入到发送缓冲区。

第二次,在使用 TCP 传输协议的情况下,从传输层进入网络层的时候,每一个 sk_buff 都会被克隆一个新的副本出来。副本 sk_buff 会被送往网络层,等它发送完的时候就会释放掉,然后原始的 sk_buff 还保留在传输层,目的是为了实现 TCP 的可靠传输,等收到这个数据包的 ACK 时,才会释放原始的 sk_buff 。

第三次,当 IP 层发现 sk_buff 大于 MTU 时才需要进行。会再申请额外的 sk_buff,并将原来的 sk_buff 拷贝为多个小的 sk_buff。

8.URL 详解

以百度链接为例,URL 是统一资源定位符:定位资源的,定位互联网上的某一个资源,长长的 URL 实际上是请求服务器里的文件资源

https://www.baidu.com/

url-api详解:

  • protocol 协议
  • host 域名
  • port 端口
  • path 路径
  • query 参数
@Slf4j
public class UrlDemo {
    public static void main(String[] args) throws Exception {
        final URL url = new URL("http://localhost:8080/helloworld/index.jsp?username=kuangshen&password=123");
        log.info(url.getAuthority());//localhost:8080
        log.info(url.getPath());///helloworld/index.jsp
        log.info(url.getProtocol());//http
        log.info(url.getHost());//localhost
        log.info(url.getFile());///helloworld/index.jsp?username=kuangshen&password=123
        log.info(url.getUserInfo());//null
        log.info(url.getQuery());//username=kuangshen&password=123
        log.info(url.getRef());//null
        log.info(String.valueOf(url.getDefaultPort()));//80
        log.info((String) url.getContent());
    }
}

9.浏览器 url 如何工作?

大致流程:

  1. 首先,浏览器进程接收到用户输入的 URL 请求,会解析 URL,从而生成发送给 Web 服务器的请求信息。

  2. 然后,对 URL 进行解析之后,浏览器确定了 Web 服务器和文件名,接下来就是根据这些信息来生成 HTTP 请求消息了。

  3. 根据 DNS 服务器查询真实的 ip 地址

  4. 通过 DNS 获取到 IP 后,就可以把 HTTP 的传输工作交给操作系统中的协议栈

  5. 在传输层使用 TCP 协议,在网络层使用 ip 协议

  6. 添加发送端和接收端的 MAC 头部,用于传输数据

  7. 通过网卡将二进制数字信号转换为电信号,进行数据传输

  8. 通过交换机中的 MAC 地址和端口的映射,知道包应该发往哪里,MAC 头部的作用就是将包送达路由器

  9. 需要 MAC 地址在以太网内进行两个设备之间的包传输。路由器包含了 MAC 地址.

  10. 接着网络进程接收到了响应头数据,便解析响应头数据,并将数据转发给浏览器进程。

  11. 浏览器进程接收到网络进程的响应头数据之后,发送“提交导航 (Commit Navigation)”消息到渲染进程;

  12. 渲染进程接收到“提交导航”的消息之后,便开始准备接收 HTML 数据,接收数据的方式是直接和网络进程建立数据管道;

  13. 最后渲染进程会向浏览器进程“确认提交”,这是告诉浏览器进程:“已经准备好接受和解析页面数据了”。

  14. 浏览器进程接收到渲染进程“提交文档”的消息之后,便开始移除之前旧的文档,然后更新浏览器进程中的页面状态。

HTTP 的消息格式

URL 请求过程:
浏览器进程会通过进程间通信(IPC)把 URL 请求发送至网络进程,网络进程接收到 URL 请求后,会在这里发起真正的 URL 请求流程。

(1)网络进程会查找本地缓存是否缓存了该资源。如果有缓存资源,那么直接返回资源给浏览器进程;如果在缓存中没有查找到资源,那么直接进入网络请求流程。

(2)这请求前的第一步是要进行 DNS 解析,以获取请求域名的服务器 IP 地址。如果请求协议是 HTTPS,那么还需要建立 TLS 连接。

(3)接下来就是利用 IP 地址和服务器建立 TCP 连接。连接建立之后,浏览器端会构建请求行、请求头等信息,并把和该域名相关的 Cookie 等数据附加到请求头中,然后向服务器发送构建的请求信息。

(4)服务器接收到请求信息后,会根据请求信息生成响应数据(包括响应行、响应头和响应体等信息),并发给网络进程。等网络进程接收了响应行和响应头之后,就开始解析响应头的内容了。

10.交换机和路由器的区别

交换机:拥有一条高带宽的背部总线和内部交换矩阵。交换机的所有的端口都挂接在这条总线上,控制电路收到数据包以后,处理端口会查找内存中的地址对照表以确定目的 MAC(网卡的硬件地址)的 NIC(网卡)挂接在哪个端口上,通过内部交换矩阵迅速将数据包传送到目的端口,目的 MAC 若不存在则广播到所有的端口,接收端口回应后交换机会“学习”新的地址,并把它添加入内部 MAC 地址表中。

使用交换机也可以把网络“分段”,通过对照 MAC 地址表,交换机只允许必要的网络流量通过交换机。通过交换机的过滤和转发,可以有效的隔离广播风暴,减少误包和错包的出现,避免共享冲突。

交换机在同一时刻可进行多个端口对之间的数据传输。每一端口都可视为独立的网段,连接在其上的网络设备独自享有全部的带宽,无须同其他设备竞争使用。当节点 A 向节点 D 发送数据时,节点 B 可同时向节点 C 发送数据,而且这两个传输都享有网络的全部带宽,都有着自己的虚拟连接。假使这里使用的是 10Mbps 的以太网交换机,那么该交换机这时的总流通量就等于 2×10Mbps = 20Mbps,而使用 10Mbps 的共享式 HUB 时,一个 HUB 的总流通量也不会超出 10Mbps。

总之,交换机是一种基于 MAC 地址识别,能完成封装转发数据包功能的网络设备。交换机可以“学习”MAC 地址,并把其存放在内部地址表中,通过在数据帧的始发者和目标接收者之间建立临时的交换路径,使数据帧直接由源地址到达目的地址。

路由器:从过滤网络流量的角度来看,路由器的作用与交换机和网桥非常相似。但是与工作在网络物理层,从物理上划分网段的交换机不同,路由器使用专门的软件协议从逻辑上对整个网络进行划分。例如,一台支持 IP 协议的路由器可以把网络划分成多个子网段,只有指向特殊 IP 地址的网络流量才可以通过路由器。对于每一个接收到的数据包,路由器都会重新计算其校验值,并写入新的物理地址。因此,使用路由器转发和过滤数据的速度往往要比只查看数据包物理地址的交换机慢。但是,对于那些结构复杂的网络,使用路由器可以提高网络的整体效率。路由器的另外一个明显优势就是可以自动过滤网络广播。

二.HTTP

1.http 是什么?

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

  • 超文本
  • 传输
  • 协议

提纲

2.HTTP 状态码有哪些?

图片

  • 1xx 类状态码属于提示信息,是协议处理中的一种中间状态,实际用到的比较少。
  • 2xx 类状态码表示服务器成功处理了客户端的请求,也是我们最愿意看到的状态。
    • 200 OK」是最常见的成功状态码,表示一切正常。如果是非 HEAD 请求,服务器返回的响应头都会有 body 数据。
    • 204 No Content」也是常见的成功状态码,与 200 OK 基本相同,但响应头没有 body 数据。
    • 206 Partial Content」是应用于 HTTP 分块下载或断点续传,表示响应返回的 body 数据并不是资源的全部,而是其中的一部分,也是服务器处理成功的状态。
  • 3xx 类状态码表示客户端请求的资源发生了变动,需要客户端用新的 URL 重新发送请求获取资源,也就是重定向
    • 301 Moved Permanently」表示永久重定向,说明请求的资源已经不存在了,需改用新的 URL 再次访问。
    • 302 Found」表示临时重定向,说明请求的资源还在,但暂时需要用另一个 URL 来访问。
    • 301 和 302 都会在响应头里使用字段 Location,指明后续要跳转的 URL,浏览器会自动重定向新的 URL。
    • 304 Not Modified」不具有跳转的含义,表示资源未修改,重定向已存在的缓冲文件,也称缓存重定向,也就是告诉客户端可以继续使用缓存资源,用于缓存控制。
  • 4xx 类状态码表示客户端发送的报文有误,服务器无法处理,也就是错误码的含义。
    • 400 Bad Request」表示客户端请求的报文有错误,但只是个笼统的错误。
    • 403 Forbidden」表示服务器禁止访问资源,并不是客户端的请求出错。
    • 404 Not Found」表示请求的资源在服务器上不存在或未找到,所以无法提供给客户端。
  • 5xx 类状态码表示客户端请求报文正确,但是服务器处理时内部发生了错误,属于服务器端的错误码。
    • 500 Internal Server Error」与 400 类型,是个笼统通用的错误码,服务器发生了什么错误,我们并不知道。
    • 501 Not Implemented」表示客户端请求的功能还不支持,类似“即将开业,敬请期待”的意思。
    • 502 Bad Gateway」通常是服务器作为网关或代理时返回的错误码,表示服务器自身工作正常,访问后端服务器发生了错误。
    • 503 Service Unavailable」表示服务器当前很忙,暂时无法响应客户端,类似“网络服务正忙,请稍后重试”的意思。

3.http 常见字段?

  • host:客户端发送请求时,用来指定服务器的域名;
  • Content-Length: 表明本次回应的数据长度。
  • Connection: 字段最常用于客户端要求服务器使用「HTTP 长连接」机制,以便其他请求复用。
  • Content-Type 字段用于服务器回应时,告诉客户端,本次数据是什么格式。
  • Accept 字段声明自己可以接受哪些数据格式。
  • Content-Encoding 字段说明数据的压缩方法。表示服务器返回的数据使用了什么压缩格式
  • Accept-Encoding 字段说明自己可以接受哪些压缩方法。
Host: www.A.com
Content-Length: 1000
Connection: Keep-Alive
Content-Type: text/html; Charset=utf-8
Accept: */*
Content-Encoding: gzip
Accept-Encoding: gzip, deflate

4.HTTP 缓存有哪些现方式?

对于一些具有重复性的 HTTP 请求,比如每次请求得到的数据都一样的,我们可以把这对「请求-响应」的数据都缓存在本地,那么下次就直接读取本地的数据,不必在通过网络获取服务器的响应了,这样的话 HTTP/1.1 的性能肯定肉眼可见的提升。

所以,避免发送 HTTP 请求的方法就是通过缓存技术,HTTP 设计者早在之前就考虑到了这点,因此 HTTP 协议的头部有不少是针对缓存的字段。

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

5.什么是强制缓存?

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

from disk cache,就是使用了强制缓存。

img

强缓存是利用下面这两个 HTTP 响应头部(Response Header)字段实现的,它们都用来表示资源在客户端缓存的有效期,如果 HTTP 响应头部同时有 Cache-Control 和 Expires 字段的话,Cache-Control 的优先级高于 Expires

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

Cache-control 选项更多一些,设置更加精细,所以建议使用 Cache-Control 来实现强缓存。

具体的实现流程如下:

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

6.什么是协商缓存?

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

协商缓存就是与服务端协商之后,通过协商结果来判断是否使用本地缓存

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

第一种:请求头部中的 If-Modified-Since 字段与响应头部中的 Last-Modified 字段实现,这两个字段的意思是:

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

第二种:请求头部中的 If-None-Match 字段与响应头部中的 ETag 字段,这两个字段的意思是:

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

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

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

下图是强制缓存和协商缓存的工作流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QH0Oqfhc-1681712375905)(http://qinyingjie.top/blogImg/http%E7%BC%93%E5%AD%98.png)]

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

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

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

    • 如果没有过期,则直接使用本地缓存;
    • 如果缓存过期了,会在 Request 头部加上 If-None-Match 字段,该字段的值就是 ETag 唯一标识;
  • 服务器再次收到请求后,

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

    • 如果值相等,则返回 304 Not Modified,不会返回资源
    • 如果不相等,则返回 200 状态码和返回资源,并在 Response 头部加上新的 ETag 唯一标识;
  • 如果浏览器收到 304 的请求响应状态码,则会从本地缓存中加载资源,否则更新资源。

7.http 协议版本历史

image-20230318194205020

8.http0.9 版本

  • 单线协议
  • 没有 http 标头
  • 没有状态码
  • 没有错误码
  • 一行请求信息,仅能显示 html 文件
  • 单行版本

image-20230318194720972

9.http1.0 版本

  • http 协议标头
  • 状态码
  • 传输内容类型,即 MIME 类型
  • 单线协议
  • 短连接

image-20230318194903233

10.http1.1 版本

  • 持久连接,keep-alive 的标头
  • 保持的这个链接上发送多个请求
  • 节省了握手和挥手的时间
  • 可以一次放松多个请求,逐个响应
  • 遗留了 2 个问题
    • 顺序响应问题,会出现队头阻塞问题,解决办法是保持 6 个左右的连接
    • 请求头冗余问题

实际上 HTTP/1.1 管道化技术不是默认开启,而且浏览器基本都没有支持,知道有这个功能,但是没有被使用就行了。

image-20230318195125980

image-20230318195304325

11.http2.0 版本

  • 标头压缩(头部压缩),车轱轮话减少了,相当于加了一个索引表

  • 将标头和明文拆分成二进制数据帧进行传输,使用 stream id 做标识

    • 双向数据流,数据帧使用 stream id 做标识
    • 按照 stream id 将多个数据帧识别为一个消息,保证数据的完整性
  • 一个客户端和一个服务器保持一个连接,在该连接上并发传输,就是多路复用

    • 缓冲区处理 http 事务线头阻塞,解决了数据帧层的线头阻塞问题
    • 传输层的 TCP 线头阻塞问题并没有得到解决,在传输层会出现阻塞
  • 服务端主动推送资源

    • 在应用层改变了传输方式

image-20230411104951225

image-20230318195818687

image-20230318200041467

12.http3.0 版本

  • QUIC 编解码方式
  • UDP

image-20230318204120384

规避 TCP 线头阻塞问题

  • offset
  • stream id
  • 脱掉了 http 外衣

image-20230318204431844

13.Http2.0 和 http3.0

image-20230318204202789

https和http3对比:

图片

14.HPACK 原理

HTTP/2 会压缩头(Header)如果你同时发出多个请求,他们的头是一样的或是相似的,那么,协议会帮你消除重复的部分。头部压缩使用了 HPACK 原理,简单说一下 HPACK 原理.

在 http 1.x 的版本中,头部字段以字符串进行传输,在高并发的场景下,每个请求都携带了 header 字段,浪费了带宽,增加了网络延迟。针对这个问题,http 2.x 的版本对 hearder 信息进行压缩编码,从而提高带宽利用率,这种压缩编码算法就是 hpack。

hpack 编码和解码的过程需要依赖两张表格——静态表和动态表。静态表和动态表共同构成了索引值,索引值最小为 1,静态表是写死的,其索引值共有 61 对,表的内容如下:

      +-------+-----------------------------+---------------+
      | Index | Header Name                 | Header Value  |
      +-------+-----------------------------+---------------+
      | 1     | :authority                  |               |
      | 2     | :method                     | GET           |
      | 3     | :method                     | POST          |
      | 4     | :path                       | /             |
      | 5     | :path                       | /index.html   |
      | 6     | :scheme                     | http          |
      | 7     | :scheme                     | https         |
      | 8     | :status                     | 200           |
      | 9     | :status                     | 204           |
      | 10    | :status                     | 206           |
      | 11    | :status                     | 304           |
      | 12    | :status                     | 400           |
      | 13    | :status                     | 404           |
      | 14    | :status                     | 500           |
      | 15    | accept-charset              |               |
      | 16    | accept-encoding             | gzip, deflate |
      | 17    | accept-language             |               |
      | 18    | accept-ranges               |               |
      | 19    | accept                      |               |
      | 20    | access-control-allow-origin |               |
      | 21    | age                         |               |
      | 22    | allow                       |               |
      | 23    | authorization               |               |
      | 24    | cache-control               |               |
      | 25    | content-disposition         |               |
      | 26    | content-encoding            |               |
      | 27    | content-language            |               |
      | 28    | content-length              |               |
      | 29    | content-location            |               |
      | 30    | content-range               |               |
      | 31    | content-type                |               |
      | 32    | cookie                      |               |
      | 33    | date                        |               |
      | 34    | etag                        |               |
      | 35    | expect                      |               |
      | 36    | expires                     |               |
      | 37    | from                        |               |
      | 38    | host                        |               |
      | 39    | if-match                    |               |
      | 40    | if-modified-since           |               |
      | 41    | if-none-match               |               |
      | 42    | if-range                    |               |
      | 43    | if-unmodified-since         |               |
      | 44    | last-modified               |               |
      | 45    | link                        |               |
      | 46    | location                    |               |
      | 47    | max-forwards                |               |
      | 48    | proxy-authenticate          |               |
      | 49    | proxy-authorization         |               |
      | 50    | range                       |               |
      | 51    | referer                     |               |
      | 52    | refresh                     |               |
      | 53    | retry-after                 |               |
      | 54    | server                      |               |
      | 55    | set-cookie                  |               |
      | 56    | strict-transport-security   |               |
      | 57    | transfer-encoding           |               |
      | 58    | user-agent                  |               |
      | 59    | vary                        |               |
      | 60    | via                         |               |
      | 61    | www-authenticate            |               |
      +-------+-----------------------------+---------------+

标头压缩,车轱轮话减少了,使用了 HPACK,相当于加了一个索引表,使用了哈夫曼算法.

image-20230318201229047

15.二进制格式

HTTP/2 不再像 HTTP/1.1 里的纯文本形式的报文,而是全面采用了二进制格式,头信息和数据体都是二进制,并且统称为帧(frame):头信息帧(Headers Frame)和数据帧(Data Frame) 。这样虽然对人不友好,但是对计算机非常友好,因为计算机只懂二进制,那么收到报文后,无需再将明文的报文转成二进制,而是直接解析二进制报文,这增加了数据传输的效率

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f9jpJd1L-1681712379752)(null)]

Header: :status: 200 OK 的编码内容为:1000 1000,那么表达的含义是什么呢?

image-20230411105350080

  1. 最前面的 1 标识该 Header 是静态表中已经存在的 KV。
  2. 在静态表里,“:status: 200 ok” 静态表编码是 8,二进制即是 1000。

因此,整体加起来就是 1000 1000。

16.并发传输

我们都知道 HTTP/1.1 的实现是基于请求-响应模型的。同一个连接中,HTTP 完成一个事务(请求与响应),才能处理下一个事务,也就是说在发出请求等待响应的过程中,是没办法做其他事情的,如果响应迟迟不来,那么后续的请求是无法发送的,也造成了队头阻塞的问题。

而 HTTP/2 就很牛逼了,引出了 Stream 概念,多个 Stream 复用在一条 TCP 连接。

图片

从上图可以看到,1 个 TCP 连接包含多个 Stream,Stream 里可以包含 1 个或多个 Message,Message 对应 HTTP/1 中的请求或响应,由 HTTP 头部和包体构成。Message 里包含一条或者多个 Frame,Frame 是 HTTP/2 最小单位,以二进制压缩格式存放 HTTP/1 中的内容(头部和包体)。

针对不同的 HTTP 请求用独一无二的 Stream ID 来区分,接收端可以通过 Stream ID 有序组装成 HTTP 消息,不同 Stream 的帧是可以乱序发送的,因此可以并发不同的 Stream ,也就是 HTTP/2 可以并行交错地发送请求和响应

比如下图,服务端并行交错地发送了两个响应:Stream 1 和 Stream 3,这两个 Stream 都是跑在一个 TCP 连接上,客户端收到后,会根据相同的 Stream ID 有序组装成 HTTP 消息。

  • 客户端和服务器双方都可以建立 Stream
  • Stream ID 也是有区别的,客户端建立的 Stream 必须是奇数号,而服务器建立的 Stream 必须是偶数号。

图片

17.http2.0 有什么缺陷?

HTTP/2 通过 Stream 的并发能力,解决了 HTTP/1 队头阻塞的问题,看似很完美了,但是 HTTP/2 还是存在“队头阻塞”的问题,只不过问题不是在 HTTP 这一层面,而是在 TCP 这一层。

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

图片

举个例子

图中发送方发送了很多个 packet,每个 packet 都有自己的序号,你可以认为是 TCP 的序列号,其中 packet 3 在网络中丢失了,即使 packet 4-6 被接收方收到后,由于内核中的 TCP 数据不是连续的,于是接收方的应用层就无法从内核中读取到,只有等到 packet 3 重传后,接收方的应用层才可以从内核中读取到数据,这就是 HTTP/2 的队头阻塞问题,是在 TCP 层面发生的。

所以,一旦发生了丢包现象,就会触发 TCP 的重传机制,这样在一个 TCP 连接中的所有的 HTTP 请求都必须等待这个丢了的包被重传回来

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pVLYFf7W-1681712379826)(null)]

18.队头阻塞总结

我们知道了 HTTP/1.1 和 HTTP/2 都有队头阻塞的问题:

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

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

图片

UDP 发送是不管顺序,也不管丢包的,所以不会出现像 HTTP/2 队头阻塞的问题。大家都知道 UDP 是不可靠传输的,但基于 UDP 的 QUIC 协议 可以实现类似 TCP 的可靠性传输。QUIC 有自己的一套机制可以保证传输的可靠性的。当某个流发生丢包时,只会阻塞这个流,其他流不会受到影响,因此不存在队头阻塞问题。这与 HTTP/2 不同,HTTP/2 只要某个流中的数据包丢失了,其他流也会因此受影响。

19.QUIC 协议

QUIC 有以下 3 个特点。

  • 无队头阻塞
  • 更快的连接建立
  • 连接迁移,不是通过四元组进行连接,而是通过连接 id

无队头阻塞:

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dAwIFR7X-1681712379669)(null)]

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

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

更快的连接建立:

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

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

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

连接迁移:

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

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

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

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

20.传输层协议

image-20230318194305309

  • TCP
  • UDP

image-20230318194525364

21.说说 RPC?

一个完整的 RPC 架构里面包含了四个核心的组件,分别是 Client 、Server、Client Stub、Server Stub,这个 Stub 大家可以理解为存根。分别说说这几个组件

客户端(Client),服务的调用方。
服务端(Server),真正的服务提供者。
客户端存根(Client Stub),存放服务端的地址消息,再将客户端的请求参数打包成网络消息,然后通过网络远程发送给服务方。
服务端存根(Server Stub),接收客户端发送过来的消息,将消息解包,并调用本地的方法。

image-20230413171206756

RPC 主要是用在大型企业里面,因为大型企业里面系统繁多,业务线复杂,而且效率优势非常重要的一块,这个时候 RPC 的优势就比较明显了。实际的开发当中是这么做的,项目一般使用 maven 来管理。比如我们有一个处理订单的系统服务,先声明它的所有的接口(这里就是具体指 Java 中的 interface),然后将整个项目打包为一个 jar 包,服务端这边引入这个二方库,然后实现相应的功能,客户端这边也只需要引入这个二方库即可调用了。为什么这么做?主要是为了减少客户端这边的 jar 包大小,因为每一次打包发布的时候,jar 包太多总是会影响效率。另外也是将客户端和服务端解耦,提高代码的可移植性。

流行的RPC框架:
  目前流行的开源 RPC 框架还是比较多的。下面重点介绍三种:

(1)gRPC 是 Google 公布的开源软件,基于最新的 HTTP2.0 协议,并支持常见的众多编程语言。 我们知道 HTTP2.0 是基于二进制的 HTTP 协议升级版本,目前各大浏览器都在快马加鞭的加以支持。 这个 RPC 框架是基于 HTTP 协议实现的,底层使用到了 Netty 框架的支持。

(2)Thrift 是 Facebook 的一个开源项目,主要是一个跨语言的服务开发框架。它有一个代码生成器来对它所定义的 IDL 定义文件自动生成服务代码框架。用户只要在其之前进行二次开发就行,对于底层的 RPC 通讯等都是透明的。不过这个对于用户来说的话需要学习特定领域语言这个特性,还是有一定成本的。

(3)Dubbo 是阿里集团开源的一个极为出名的 RPC 框架,在很多互联网公司和企业应用中广泛使用。协议和序列化框架都可以插拔是及其鲜明的特色。同样的远程接口是基于 Java Interface,并且依托于 spring 框架方便开发。可以方便的打包成单一文件,独立进程运行,和现在的微服务概念一致。

RPC调用过程:

image-20230413171330682

(1) 客户端(client)以本地调用方式(即以接口的方式)调用服务;

(2) 客户端存根(client stub)接收到调用后,负责将方法、参数等组装成能够进行网络传输的消息体(将消息体对象序列化为二进制);

(3) 客户端通过 sockets 将消息发送到服务端;

(4) 服务端存根( server stub)收到消息后进行解码(将消息对象反序列化);

(5) 服务端存根( server stub)根据解码结果调用本地的服务;

(6) 本地服务执行并将结果返回给服务端存根( server stub);

(7) 服务端存根( server stub)将返回结果打包成消息(将结果消息对象序列化);

(8) 服务端(server)通过 sockets 将消息发送到客户端;

(9) 客户端存根(client stub)接收到结果消息,并进行解码(将结果消息发序列化);

(10) 客户端(client)得到最终结果。

RPC 的目标是要把 2、3、4、7、8、9 这些步骤都封装起来。

注意:无论是何种类型的数据,最终都需要转换成二进制流在网络上进行传输,数据的发送方需要将对象转换为二进制流,而数据的接收方则需要把二进制流再恢复为对象。

22.自定义 RPC

image-20230413171700693

与 RPC 调用过程的架构图相比较,rpc-sample-client 相当于 client-functions,通过接口方法发起请求;而 rpc-client 相当于 client-stub,负责把方法和参数序列化并发送给查找到的服务器。rpc-server 相当于 server stub,负责把客户端发送过来的消息反序列化,并通过反射调用本地服务,再把本地服务返回的结果序列化发送给客户端;rpc-sample-server 则相当于 server-functions,提供本地服务并返回得出的结果给 rpc-server。rpc-client 等待接收 rpc-server 返回的结果,并将结果反序列化,再把反序列化最终得到的结果返回给 rpc-sample-client。

rpc-common 提供基于 netty 的序列化与反序列化方法,并封装 rpc 请求和 rpc 响应,序列化和反序列的对象就是封装好的 RpcRequest 和 RpcResponse。rpc-registry 则是基于 zookeeper 设计的分布式服务器动态上下线通知的应用程序,通过 rpc-registry 可以查找服务器或者注册服务器,用于均衡负载。

rpc-client 与 rpc-server 之间的通信是基于 netty 的,netty 底层也是 sockets 设计,但是效率比 sockets 高。

23.http 和 RPC?

RPC(即 Remote Procedure Call,远程过程调用),主要是基于 TCP/IP 协议;而 HTTP 服务主要是基于 HTTP 协议的。我们都知道 HTTP 协议是在传输层协议 TCP 之上的,所以效率来看的话,RPC 当然是要更胜一筹啦

TCP 是传输层的协议,而基于 TCP 造出来的 HTTP 和各类 RPC 协议,它们都只是定义了不同消息格式的应用层协议而已。

HTTP 协议(Hyper Text Transfer Protocol),又叫做超文本传输协议。我们用的比较多,平时上网在浏览器上敲个网址就能访问网页,这里用到的就是 HTTP 协议。

而 RPC(Remote Procedure Call),又叫做远程过程调用。它本身并不是一个具体的协议,而是一种调用方式。

值得注意的是,虽然大部分 RPC 协议底层使用 TCP,但实际上它们不一定非得使用 TCP,改用 UDP 或者 HTTP,其实也可以做到类似的功能。

总结:

  • 纯裸 TCP 是能收发数据,但它是个无边界的数据流,上层需要定义消息格式用于定义消息边界。于是就有了各种协议,HTTP 和各类 RPC 协议就是在 TCP 之上定义的应用层协议。
  • RPC 本质上不算是协议,而是一种调用方式,而像 gRPC 和 Thrift 这样的具体实现,才是协议,它们是实现了 RPC 调用的协议。目的是希望程序员能像调用本地方法那样去调用远端的服务方法。同时 RPC 有很多种实现方式,不一定非得基于 TCP 协议。
  • 从发展历史来说,HTTP 主要用于 B/S 架构,而 RPC 更多用于 C/S 架构。但现在其实已经没分那么清了,B/S 和 C/S 在慢慢融合。很多软件同时支持多端,所以对外一般用 HTTP 协议,而内部集群的微服务之间则采用 RPC 协议进行通讯。
  • RPC 其实比 HTTP 出现的要早,且比目前主流的 HTTP/1.1 性能要更好,所以大部分公司内部都还在使用 RPC。
  • HTTP/2.0 在 HTTP/1.1 的基础上做了优化,性能可能比很多 RPC 协议都要好,但由于是这几年才出来的,所以也不太可能取代掉 RPC。
  • RPC 的核心并不在于使用什么协议。RPC 的目的是让你在本地调用远程的方法,而对你来说这个调用是透明的,你并不知道这个调用的方法是部署哪里

image-20230413170306937

24.GET 和 POST 区别?

根据 RFC 规范,GET 的语义是从服务器获取指定的资源,这个资源可以是静态的文本、页面、图片视频等。GET 请求的参数位置一般是写在 URL 中,URL 规定只能支持 ASCII,所以 GET 请求的参数只允许 ASCII 字符 ,而且浏览器会对 URL 的长度有限制(HTTP 协议本身对 URL 长度并没有做任何规定)。

根据 RFC 规范,POST 的语义是根据请求负荷(报文 body)对指定的资源做出处理,具体的处理方式视资源类型而不同。POST 请求携带数据的位置一般是写在报文 body 中,body 中的数据可以是任意格式的数据,只要客户端与服务端协商好即可,而且浏览器不会对 body 大小做限制。

GET和POST还有一个重大区别,简单的说:

GET 产生一个 TCP 数据包;POST 产生两个 TCP 数据包。

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

25.什么是安全和幂等?

先说明下安全和幂等的概念:

  • 在 HTTP 协议里,所谓的「安全」是指请求方法不会「破坏」服务器上的资源。
  • 所谓的「幂等」,意思是多次执行相同的操作,结果都是「相同」的。

如果从 RFC 规范定义的语义来看:

  • GET 方法就是安全且幂等的,因为它是「只读」操作,无论操作多少次,服务器上的数据都是安全的,且每次的结果都是相同的。所以,可以对 GET 请求的数据做缓存,这个缓存可以做到浏览器本身上(彻底避免浏览器发请求),也可以做到代理上(如 nginx),而且在浏览器中 GET 请求可以保存为书签
  • POST 因为是「新增或提交数据」的操作,会修改服务器上的资源,所以是不安全的,且多次提交数据就会创建多个资源,所以不是幂等的。所以,浏览器一般不会缓存 POST 请求,也不能把 POST 请求保存为书签

三.HTTPS

1.HTTP 与 HTTPS 区别?

HTTP 由于是明文传输,所以安全上存在以下三个风险:

  • 窃听风险,比如通信链路上可以获取通信内容,内容信息被窃取。
  • 篡改风险,比如强制植入垃圾广告,视觉污染,内容变更伪造。
  • 冒充风险,比如冒充淘宝网站,用户钱容易没,钓鱼链接。

image-20230403092550374

HTTPS 在 HTTP 与 TCP 层之间加入了 SSL/TLS 协议,可以很好的解决了上述的风险:

  • 信息加密:交互信息无法被窃取。
  • 校验机制:无法篡改通信内容,篡改了就不能正常显示。
  • 身份证书:证明淘宝是真的淘宝网。

2.HTTPS 如何保证安全?

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

3.混合加密

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

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

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

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

4.摘要算法和数字签名

为了保证传输的内容不被篡改,我们需要对内容计算出一个「指纹」,然后同内容一起传输给对方。

对方收到后,先是对内容也计算出一个「指纹」,然后跟发送方发送的「指纹」做一个比较,如果「指纹」相同,说明内容没有被篡改,否则就可以判断出内容被篡改了。

那么,在计算机里会用摘要算法(哈希函数)来计算出内容的哈希值,也就是内容的「指纹」,这个哈希值是唯一的,且无法通过哈希值推导出内容

通过哈希算法可以确保内容不会被篡改,但是并不能保证「内容 + 哈希值」不会被中间人替换,因为这里缺少对客户端收到的消息是否来源于服务端的证明

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JyBh7NZr-1681712375910)(http://qinyingjie.top/blogImg/%E6%91%98%E8%A6%81%E7%AE%97%E6%B3%95.png)]

那为了避免这种情况,计算机里会用非对称加密算法来解决,共有两个密钥:

  • 一个是公钥,这个是可以公开给所有人的;
  • 一个是私钥,这个必须由本人管理,不可泄露。

这两个密钥可以双向加解密的,比如可以用公钥加密内容,然后用私钥解密,也可以用私钥加密内容,公钥解密内容。

流程的不同,意味着目的也不相同:

  • 公钥加密,私钥解密。这个目的是为了保证内容传输的安全,因为被公钥加密的内容,其他人是无法解密的,只有持有私钥的人,才能解密出实际的内容;
  • 私钥加密,公钥解密。这个目的是为了保证消息不会被冒充,因为私钥是不可泄露的,如果公钥能正常解密出私钥加密的内容,就能证明这个消息是来源于持有私钥身份的人发送的。

一般我们不会用非对称加密来加密实际的传输内容,因为非对称加密的计算比较耗费性能的。

所以非对称加密的用途主要在于通过「私钥加密,公钥解密」的方式,来确认消息的身份,我们常说的数字签名算法,就是用的是这种方式,不过私钥加密内容不是内容本身,而是对内容的哈希值加密

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I4GgI8hy-1681712375910)(http://qinyingjie.top/blogImg/%E6%95%B0%E5%AD%97%E7%AD%BE%E5%90%8D.png)]

5.数字证书?

  • 可以通过哈希算法来保证消息的完整性;
  • 可以通过数字签名来保证消息的来源可靠性(能确认消息是由持有私钥的一方发送的);

但是还缺少身份认证,万一公钥是被伪造的呢?伪造出一对公私钥

「个人信息 + 公钥 + 数字签名」打包成一个数字证书 ,也就是说这个数字证书包含服务器的公钥。

在计算机里,这个权威的机构就是 CA (数字证书认证机构),将服务器公钥放在数字证书(由数字证书认证机构颁发)中,只要证书是可信的,公钥就是可信的。

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

image-20230403092643534

6.https 如何建立连接?

SSL/TLS 协议的基本流程:

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

前两步也就是 SSL/TLS 的建立过程,也就是 TLS 握手阶段。最后一步是数据加入阶段.

TLS 的「握手阶段」涉及四次通信,使用不同的密钥交换算法,TLS 握手流程也会不一样的,现在常用的密钥交换算法有两种:RSA 算法 (opens new window)和 ECDHE 算法 (opens new window)。

7.基于 RSA 的加密流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6RIfxgnt-1681712375911)(http://qinyingjie.top/blogImg/23-HTTPS%E5%B7%A5%E4%BD%9C%E6%B5%81%E7%A8%8B.jpeg)]

TLS 协议建立的详细流程:

  1. ClientHello

首先,由客户端向服务器发起加密通信请求,也就是 ClientHello 请求。

在这一步,客户端主要向服务器发送以下信息:

(1)客户端支持的 TLS 协议版本,如 TLS 1.2 版本。

(2)客户端生产的随机数(Client Random),后面用于生成「会话秘钥」条件之一。

(3)客户端支持的密码套件列表,如 RSA 加密算法。

  1. SeverHello

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

(1)确认 TLS 协议版本,如果浏览器不支持,则关闭加密通信。

(2)服务器生产的随机数(Server Random),也是后面用于生产「会话秘钥」条件之一。

(3)确认的密码套件列表,如 RSA 加密算法。

(4)服务器的数字证书。

3.客户端回应

客户端收到服务器的回应之后,首先通过浏览器或者操作系统中的 CA 公钥,确认服务器的数字证书的真实性。

如果证书没有问题,客户端会从数字证书中取出服务器的公钥,然后使用它加密报文,向服务器发送如下信息:

(1)一个随机数(pre-master key)。该随机数会被服务器公钥加密。

(2)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。

(3)客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供服务端校验。

上面第一项的随机数是整个握手阶段的第三个随机数,会发给服务端,所以这个随机数客户端和服务端都是一样的。

服务器和客户端有了这三个随机数(Client Random、Server Random、pre-master key),接着就用双方协商的加密算法,各自生成本次通信的「会话秘钥」

  1. 服务器的最后回应

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

然后,向客户端发送最后的信息:

(1)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。

(2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供客户端校验。

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

8.客户端校验数字证书流程?

数字证书签发和验证流程:

image-20230411102833214

CA 签发证书的过程,如上图左边部分:

  • 首先 CA 会把持有者的公钥、用途、颁发者、有效时间等信息打成一个包,然后对这些信息进行 Hash 计算,得到一个 Hash 值;
  • 然后 CA 会使用自己的私钥将该 Hash 值加密,生成 Certificate Signature,也就是 CA 对证书做了签名;
  • 最后将 Certificate Signature 添加在文件证书上,形成数字证书;

客户端校验服务端的数字证书的过程,如上图右边部分:

  • 首先客户端会使用同样的 Hash 算法获取该证书的 Hash 值 H1;
  • 通常浏览器和操作系统中集成了 CA 的公钥信息,浏览器收到证书后可以使用 CA 的公钥解密 Certificate Signature 内容,得到一个 Hash 值 H2 ;
  • 最后比较 H1 和 H2,如果值相同,则为可信赖的证书,否则则认为证书不可信。

9.证书信任链

事实上,证书的验证过程中还存在一个证书信任链的问题,因为我们向 CA 申请的证书一般不是根证书签发的,而是由中间证书签发的,比如百度的证书,从下图你可以看到,证书的层级有三级:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4KpT3Ki6-1681712380235)(null)]

对于这种三级层级关系的证书的验证过程如下:

  • 客户端收到 baidu.com 的证书后,发现这个证书的签发者不是根证书,就无法根据本地已有的根证书中的公钥去验证 baidu.com 证书是否可信。于是,客户端根据 baidu.com 证书中的签发者,找到该证书的颁发机构是 “GlobalSign Organization Validation CA - SHA256 - G2”,然后向 CA 请求该中间证书。
  • 请求到证书后发现 “GlobalSign Organization Validation CA - SHA256 - G2” 证书是由 “GlobalSign Root CA” 签发的,由于 “GlobalSign Root CA” 没有再上级签发机构,说明它是根证书,也就是自签证书。应用软件会检查此证书有否已预载于根证书清单上,如果有,则可以利用根证书中的公钥去验证 “GlobalSign Organization Validation CA - SHA256 - G2” 证书,如果发现验证通过,就认为该中间证书是可信的。
  • “GlobalSign Organization Validation CA - SHA256 - G2” 证书被信任后,可以使用 “GlobalSign Organization Validation CA - SHA256 - G2” 证书中的公钥去验证 baidu.com 证书的可信性,如果验证通过,就可以信任 baidu.com 证书。

在这四个步骤中,最开始客户端只信任根证书 GlobalSign Root CA 证书的,然后 “GlobalSign Root CA” 证书信任 “GlobalSign Organization Validation CA - SHA256 - G2” 证书,而 “GlobalSign Organization Validation CA - SHA256 - G2” 证书又信任 baidu.com 证书,于是客户端也信任 baidu.com 证书。

总括来说,由于用户信任 GlobalSign,所以由 GlobalSign 所担保的 baidu.com 可以被信任,另外由于用户信任操作系统或浏览器的软件商,所以由软件商预载了根证书的 GlobalSign 都可被信任。

证书链麻烦是为了确保根证书的绝对安全性,将根证书隔离地越严格越好,不然根证书如果失守了,那么整个信任链都会有问题。

10.https 如何保证数据完整性

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

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NdmoHGkX-1681712380634)(null)]

具体过程如下:

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

记录协议完成后,最终的报文数据将传递到传输控制协议 (TCP) 层进行传输。

11.https 一定可靠吗?

不可靠的情况下大致有 2 种,这 2 种都是用户自身接受的安全隐患,并非 https 的不安全,不可靠

  • 用户点击接受了中间人服务器的证书。
  • 电脑中毒了,根证书目录植入了中间人的根证书

所以,HTTPS 协议本身到目前为止还是没有任何漏洞的,即使你成功进行中间人攻击,本质上是利用了客户端的漏洞(用户点击继续访问或者被恶意导入伪造的根证书),并不是 HTTPS 不够安全。

12.中间人服务器?

客户端通过浏览器向服务端发起 HTTPS 请求时,被「假基站」转发到了一个「中间人服务器」,于是客户端是和「中间人服务器」完成了 TLS 握手,然后这个「中间人服务器」再与真正的服务端完成 TLS 握手。

image-20230411103259656

具体过程如下:

  • 客户端向服务端发起 HTTPS 建立连接请求时,然后被「假基站」转发到了一个「中间人服务器」,接着中间人向服务端发起 HTTPS 建立连接请求,此时客户端与中间人进行 TLS 握手,中间人与服务端进行 TLS 握手;
  • 在客户端与中间人进行 TLS 握手过程中,中间人会发送自己的公钥证书给客户端,客户端验证证书的真伪,然后从证书拿到公钥,并生成一个随机数,用公钥加密随机数发送给中间人,中间人使用私钥解密,得到随机数,此时双方都有随机数,然后通过算法生成对称加密密钥(A),后续客户端与中间人通信就用这个对称加密密钥来加密数据了。
  • 在中间人与服务端进行 TLS 握手过程中,服务端会发送从 CA 机构签发的公钥证书给中间人,从证书拿到公钥,并生成一个随机数,用公钥加密随机数发送给服务端,服务端使用私钥解密,得到随机数,此时双方都有随机数,然后通过算法生成对称加密密钥(B),后续中间人与服务端通信就用这个对称加密密钥来加密数据了。
  • 后续的通信过程中,中间人用对称加密密钥(A)解密客户端的 HTTPS 请求的数据,然后用对称加密密钥(B)加密 HTTPS 请求后,转发给服务端,接着服务端发送 HTTPS 响应数据给中间人,中间人用对称加密密钥(B)解密 HTTPS 响应数据,然后再用对称加密密钥(A)加密后,转发给客户端。

从客户端的角度看,其实并不知道网络中存在中间人服务器这个角色。那么中间人就可以解开浏览器发起的 HTTPS 请求里的数据,也可以解开服务端响应给浏览器的 HTTPS 响应数据。相当于,中间人能够 “偷看” 浏览器与服务端之间的 HTTPS 请求和响应的数据。

13.抓包工具的原理

很多抓包工具 之所以可以明文看到 HTTPS 数据,工作原理与中间人一致的。

对于 HTTPS 连接来说,中间人要满足以下两点,才能实现真正的明文代理:

  1. 中间人,作为客户端与真实服务端建立连接这一步不会有问题,因为服务端不会校验客户端的身份;
  2. 中间人,作为服务端与真实客户端建立连接,这里会有客户端信任服务端的问题,也就是服务端必须有对应域名的私钥;

中间人要拿到私钥只能通过如下方式:

  1. 去网站服务端拿到私钥;
  2. 去 CA 处拿域名签发私钥;
  3. 自己签发证书,切要被浏览器信任;

不用解释,抓包工具只能使用第三种方式取得中间人的身份。

使用抓包工具进行 HTTPS 抓包的时候,需要在客户端安装 Fiddler 的根证书,这里实际上起认证中心(CA)的作用。

抓包工具能够抓包的关键是客户端会往系统受信任的根证书列表中导入抓包工具生成的证书,而这个证书会被浏览器信任,也就是抓包工具给自己创建了一个认证中心 CA,客户端拿着中间人签发的证书去中间人自己的 CA 去认证,当然认为这个证书是有效的。

14.如何避免中间人攻击?

  • 保证电脑安全,不让病毒乘虚而入;
  • 不点击危险链接;
  • https 双向认证,服务端也检验客户端;

15.SSL 和 TLS

SSL 是洋文 “Secure Sockets Layer” 的缩写,中文叫做「安全套接层」。它是在上世纪 90 年代中期,由网景公司设计的。

到了 1999 年,SSL 因为应用广泛,已经成为互联网上的事实标准。IETF 就在那年把 SSL 标准化。标准化之后的名称改为 TLS(是 “Transport Layer Security” 的缩写),中文叫做 「传输层安全协议」。

很多相关的文章都把这两者并列称呼(SSL/TLS),因为这两者可以视作同一个东西的不同阶段。

四.TCP 篇

1.什么是 TCP?

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

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

TCP 连接:简单来说就是,用于保证可靠性和流量控制维护的某些状态信息,这些信息的组合,包括 Socket、序列号和窗口大小称为连接。

2.为什么需要 TCP?

IP 层是「不可靠」的,它不保证网络包的交付、不保证网络包的按序交付、也不保证网络包中的数据的完整性。

如果需要保障网络数据包的可靠性,那么就需要由上层(传输层)的 TCP 协议来负责。

因为 TCP 是一个工作在传输层可靠数据传输的服务,它能确保接收端接收的网络包是无损坏、无间隔、非冗余和按序的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZntzGQWY-1681712379916)(null)]

3.TCP/IP 模型

TCP/IP 是基于 TCP 和 IP 这两个最初的协议之上的不同的通信协议的大的集合。

在这里插入图片描述

  • 物理层:网卡,网线,集线器,中继器,调制解调器
  • 数据链路层:网桥,交换机
  • 网络层:路由器

在这里插入图片描述

4.TCP 头格式有哪些?

image-20230414001525350

image-20230417133601187

HTTP 是基于 TCP 协议传输的,所以在这我们先了解下 TCP 协议。

TCP 报文头部的格式:

  • 源端口号和目标端口号:首先,源端口号和目标端口号是不可少的,如果没有这两个端口号,数据就不知道应该发给哪个应用。
  • 包的序号:这个是为了解决包乱序的问题。
  • 确认号:目的是确认发出去对方是否有收到。如果没有收到就应该重新发送,直到送达,这个是为了解决丢包的问题。
  • 状态位:例如 SYN 是发起一个连接,ACK 是回复,RST 是重新连接,FIN 是结束连接等。TCP 是面向连接的,因而双方要维护连接的状态,这些带状态位的包的发送,会引起双方的状态变更。
  • 窗口大小:TCP 要做流量控制,通信双方各声明一个窗口(缓存大小),标识自己当前能够的处理能力,别发送的太快,也别发的太慢。长度为 16 位,共 2 个字节。此字段用来进行流量控制。流量控制的单位为字节数,这个值是本端期望一次接收的字节数。
  • 拥塞控制:控制发送的速度。
  • 校验和:长度为 16 位,共 2 个字节。对整个 TCP 报文段,即 TCP 头部和 TCP 数据进行校验和计算,接收端用于对收到的数据包进行验证。
  • 紧急指针:长度为 16 位,2 个字节。它是一个偏移量,和 SN 序号值相加表示紧急数据最后一个字节的序号。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u9CsEaew-1681712380469)(null)]

5.控制标志

控制标志(Control Bits)共 6 个 bit 位,具体的标志位为:URG、ACK、PSH、RST、SYN、FIN。6 个标志位的说明,如下表所示。

表:TCP 报文控制标志(Control Bits)说明

标志位说明
URG占 1 位,表示紧急指针字段有效。URG 位指示报文段里的上层实体(数据)标记为“紧急”数据。当 URG=1 时,其后的紧急指针指示紧急数据在当前数据段中的位置(相对于当前序列号的字节偏移量),TCP 接收方必须通知上层实体。
ACK占 1 位,置位 ACK=1 表示确认号字段有效;TCP 协议规定,接建立后所有发送的报文的 ACK 必须为 1;当 ACK=0 时,表示该数据段不包含确认信息。当 ACK=1 时,表示该报文段包括一个对已被成功接收报文段的确认序号 Acknowledgment Number,该序号同时也是下一个报文的预期序号。
PSH占 1 位,表示当前报文需要请求推(push)操作;当 PSH=1 时,接收方在收到数据后立即将数据交给上层,而不是直到整个缓冲区满。
RST占 1 位,置位 RST=1 表示复位 TCP 连接;用于重置一个已经混乱的连接,也可用于拒绝一个无效的数据段或者拒绝一个连接请求。如果数据段被设置了 RST 位,说明报文发送方有问题发生。
SYN占 1 位,在连接建立时用来同步序号。当 SYN=1 而 ACK=0 时,表明这是一个连接请求报文。对方若同意建立连接,则应在响应报文中使 SYN=1 和 ACK=1。 综合一下,SYN 置 1 就表示这是一个连接请求或连接接受报文。
FIN占 1 位,用于在释放 TCP 连接时,标识发送方比特流结束,用来释放一个连接。当 FIN = 1 时,表明此报文的发送方的数据已经发送完毕,并要求释放连接。

在连接建立的三次握手过程中,若只是单个 SYN 置位,表示的只是建立连接请求。如果 SYN 和 ACK 同时置位为 1,表示的建立连接之后的响应。

说明:

  • ACK:该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建立连接时的 SYN 包之外该位必须设置为 1
  • RST:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。
  • SYN:该位为 1 时,表示希望建立连接,并在其「序列号」的字段进行序列号初始值的设定。
  • FIN:该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双方的主机之间就可以相互交换 FIN 位为 1 的 TCP 段。

6.唯一确定一个 TCP 连接?

TCP 四元组可以唯一的确定一个连接,四元组包括如下:

  • 源地址
  • 源端口
  • 目的地址
  • 目的端口

源地址和目的地址的字段(32 位)是在 IP 头部中,作用是通过 IP 协议发送报文给对方主机。

源端口和目的端口的字段(16 位)是在 TCP 头部中,作用是告诉 TCP 协议应该把报文发给哪个进程。

7.TCP 的最大连接数?

服务端通常固定在某个本地端口上监听,等待客户端的连接请求。

因此,客户端 IP 和端口是可变的,其理论值计算公式如下:

image-20230414002031742

对 IPv4,客户端的 IP 数最多为 232 次方,客户端的端口数最多为 216 次方,也就是服务端单机最大 TCP 连接数,约为 248 次方。

当然,服务端最大并发 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。

8.TCP 三次握手?

TCP 传输数据之前,要先三次握手建立连接.

  • 一开始,客户端和服务端都处于 CLOSED 状态。先是服务端主动监听某个端口,处于 LISTEN 状态。
  • 然后客户端主动发起连接 SYN,之后处于 SYN-SENT 状态。
  • 服务端收到发起的连接,返回 SYN,并且 ACK 客户端的 SYN,之后处于 SYN-RCVD 状态。
  • 客户端收到服务端发送的 SYNACK 之后,发送对 SYN 确认的 ACK,之后处于 ESTABLISHED 状态,因为它一发一收成功了。
  • 服务端收到 ACKACK 之后,处于 ESTABLISHED 状态,因为它也一发一收了。

所以三次握手目的是保证双方都有发送和接收的能力

image-20230414142510183

一开始,客户端和服务端都处于 CLOSE 状态。先是服务端主动监听某个端口,处于 LISTEN 状态

image-20230414142706077

客户端会随机初始化序号(client_isn),将此序号置于 TCP 首部的「序号」字段中,同时把 SYN 标志位置为 1,表示 SYN 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT 状态。

image-20230414142757039

服务端收到客户端的 SYN 报文后,首先服务端也随机初始化自己的序号(server_isn),将此序号填入 TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填入 client_isn + 1, 接着把 SYNACK 标志位置为 1。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD 状态。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mjjmr19y-1681712380154)(null)]

客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部 ACK 标志位置为 1 ,其次「确认应答号」字段填入 server_isn + 1 ,最后把报文发送给服务端,这次报文可以携带客户到服务端的数据,之后客户端处于 ESTABLISHED 状态。

服务端收到客户端的应答报文后,也进入 ESTABLISHED 状态。

从上面的过程可以发现第三次握手是可以携带数据的,前两次握手是不可以携带数据的,这也是面试常问的题。

一旦完成三次握手,双方都处于 ESTABLISHED 状态,此时连接就已建立完成,客户端和服务端就可以相互发送数据了。

9.查看 TCP 状态?

netstat -napt

image-20230414143251800

image-20230414143301228

10.为什么是三次握手?

因为三次握手才能保证双方具有接收和发送的能力

以三个方面分析三次握手的原因:

  • 三次握手才可以阻止重复历史连接的初始化(主要原因)
  • 三次握手才可以同步双方的初始序列号
  • 三次握手才可以避免资源浪费

原因一:避免历史连接

简单来说,三次握手的首要原因是为了防止旧的重复连接初始化造成混乱。

我们考虑一个场景,客户端先发送了 SYN(seq = 90)报文,然后客户端宕机了,而且这个 SYN 报文还被网络阻塞了,服务端并没有收到,接着客户端重启后,又重新向服务端建立连接,发送了 SYN(seq = 100)报文(注意!不是重传 SYN,重传的 SYN 的序列号是一样的)。

image-20230414151138865

客户端连续发送多次 SYN(都是同一个四元组)建立连接的报文,在网络拥堵情况下:

  • 一个「旧 SYN 报文」比「最新的 SYN」 报文早到达了服务端,那么此时服务端就会回一个 SYN + ACK 报文给客户端,此报文中的确认号是 91(90+1)。
  • 客户端收到后,发现自己期望收到的确认号应该是 100 + 1,而不是 90 + 1,于是就会回 RST 报文。
  • 服务端收到 RST 报文后,就会释放连接。
  • 后续最新的 SYN 抵达了服务端后,客户端与服务端就可以正常的完成三次握手了。

如果服务端先收到新 SYN 报文,就会回 Challenge Ack (opens new window)报文给客户端,这个 ack 报文并不是确认收到「新 SYN 报文」的,而是上一次的 ack 确认号,也就是 91(90+1)。所以客户端收到此 ACK 报文时,发现自己期望收到的确认号应该是 101,而不是 91,于是就会回 RST 报文。

为什么 2 次握手不行呢?

如果是两次握手连接,就无法阻止历史连接,主要是因为在两次握手的情况下,服务端没有中间状态给客户端来阻止历史连接,导致服务端可能建立一个历史连接,造成资源浪费。

image-20230414151359363

可以看到,如果采用两次握手建立 TCP 连接的场景下,服务端在向客户端发送数据前,并没有阻止掉历史连接,导致服务端建立了一个历史连接,又白白发送了数据,妥妥地浪费了服务端的资源。

因此,要解决这种现象,最好就是在服务端发送数据前,也就是建立连接之前,要阻止掉历史连接,这样就不会造成资源浪费,而要实现这个功能,就需要三次握手。

所以,TCP 使用三次握手建立连接的最主要原因是防止「历史连接」初始化了连接。

原因二:同步双方初始序列号

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

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

可见,序列号在 TCP 连接中占据着非常重要的作用,所以当客户端发送携带「初始序列号」的 SYN 报文的时候,需要服务端回一个 ACK 应答报文,表示客户端的 SYN 报文已被服务端成功接收,那当服务端发送「初始序列号」给客户端的时候,依然也要得到客户端的应答回应,这样一来一回,才能确保双方的初始序列号能被可靠的同步。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D2qpOYjo-1681712380556)(null)]

四次握手其实也能够可靠的同步双方的初始化序号,但由于第二步和第三步可以优化成一步,所以就成了「三次握手」。

而两次握手只保证了一方的初始序列号能被对方成功接收,没办法保证双方的初始序列号都能被确认接收。

原因三:避免资源浪费

如果只有「两次握手」,当客户端发生的 SYN 报文在网络中阻塞,客户端没有接收到 ACK 报文,就会重新发送 SYN由于没有第三次握手,服务端不清楚客户端是否收到了自己回复的 ACK 报文,所以服务端每收到一个 SYN 就只能先主动建立一个连接,这会造成什么情况呢?

如果客户端发送的 SYN 报文在网络中阻塞了,重复发送多次 SYN 报文,那么服务端在收到请求后就会建立多个冗余的无效链接,造成不必要的资源浪费。

image-20230414151550726

即两次握手会造成消息滞留情况下,服务端重复接受无用的连接请求 SYN 报文,而造成重复分配资源。

小结:

TCP 建立连接时,通过三次握手能防止历史连接的建立,能减少双方不必要的资源开销,能帮助双方同步初始化序列号。序列号能够保证数据包不重复、不丢弃和按序传输。

不使用「两次握手」和「四次握手」的原因:

  • 「两次握手」:无法防止历史连接的建立,会造成双方资源的浪费,也无法可靠的同步双方序列号;
  • 「四次握手」:三次握手就已经理论上最少可靠连接建立,所以不需要使用更多的通信次数。

11.序列号 ISN 产生逻辑?

起始 ISN 是基于时钟的,每 4 微秒 + 1,转一圈要 4.55 个小时。

RFC793 提到初始化序列号 ISN 随机生成算法:ISN = M + F(localhost, localport, remotehost, remoteport)。

  • M 是一个计时器,这个计时器每隔 4 微秒加 1。
  • F 是一个 Hash 算法,根据源 IP、目的 IP、源端口、目的端口生成一个随机数值。要保证 Hash 算法不能被外部轻易推算得出,用 MD5 算法是一个比较好的选择。

可以看到,随机数是会基于时钟计时器递增的,基本不可能会随机成一样的初始化序列号。

12.MTU 和 MSS?

  • MTU:一个网络包的最大长度,以太网中一般为 1500 字节;
  • MSS:除去 IP 和 TCP 头部之后,一个网络包所能容纳的 TCP 数据的最大长度;

image-20230414152122207

如果在 TCP 的整个报文(头部 + 数据)交给 IP 层进行分片,会有什么异常呢?

当 IP 层有一个超过 MTU 大小的数据(TCP 头部 + TCP 数据)要发送,那么 IP 层就要进行分片,把数据分片成若干片,保证每一个分片都小于 MTU。把一份 IP 数据报进行分片以后,由目标主机的 IP 层来进行重新组装后,再交给上一层 TCP 传输层。

这看起来井然有序,但这存在隐患的,那么当如果一个 IP 分片丢失,整个 IP 报文的所有分片都得重传

因为 IP 层本身没有超时重传机制,它由传输层的 TCP 来负责超时和重传。

当某一个 IP 分片丢失后,接收方的 IP 层就无法组装成一个完整的 TCP 报文(头部 + 数据),也就无法将数据报文送到 TCP 层,所以接收方不会响应 ACK 给发送方,因为发送方迟迟收不到 ACK 确认报文,所以会触发超时重传,就会重发「整个 TCP 报文(头部 + 数据)」。

因此,可以得知由 IP 层进行分片传输,是非常没有效率的。

所以,为了达到最佳的传输效能 TCP 协议在建立连接的时候通常要协商双方的 MSS 值,当 TCP 层发现数据超过 MSS 时,则就先会进行分片,当然由它形成的 IP 包的长度也就不会大于 MTU ,自然也就不用 IP 分片了。

经过 TCP 层分片后,如果一个 TCP 分片丢失后,进行重发时也是以 MSS 为单位,而不用重传所有的分片,大大增加了重传的效率。

TCP 数据分割:

如果 HTTP 请求消息比较长,超过了 MSS 的长度,这时 TCP 就需要把 HTTP 的数据拆解成一块块的数据发送,而不是一次性发送所有数据。

MTU 与 MSS

  • MTU:一个网络包的最大长度,以太网中一般为 1500 字节。
  • MSS:除去 IP 和 TCP 头部之后,一个网络包所能容纳的 TCP 数据的最大长度。

数据会被以 MSS 的长度为单位进行拆分,拆分出来的每一块数据都会被放进单独的网络包中。也就是在每个被拆分的数据加上 TCP 头信息,然后交给 IP 模块来发送数据。

网络包的报文如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m9Seo2zm-1681712380079)(null)]

13.第一次握手丢失?

当客户端想和服务端建立 TCP 连接的时候,第一个发的就是 SYN 报文,然后客户端进入到 SYN_SENT 状态。

在这之后,如果客户端迟迟收不到服务端的 SYN-ACK 报文(第二次握手),就会触发「超时重传」机制,重传 SYN 报文,而且重传的 SYN 报文的序列号都是一样的

在 Linux 里,客户端的 SYN 报文最大重传次数由 tcp_syn_retries内核参数控制,这个参数是可以自定义的,默认值一般是 5。

#客户端重传次数
cat /proc/sys/net/ipv4/tcp_syn_retries
5

#服务端重传次数
cat /proc/sys/net/ipv4/tcp_synack_retries
5

通常,第一次超时重传是在 1 秒后,第二次超时重传是在 2 秒,第三次超时重传是在 4 秒后,第四次超时重传是在 8 秒后,第五次是在超时重传 16 秒后。没错,每次超时的时间是上一次的 2 倍

当第五次超时重传后,会继续等待 32 秒,如果服务端仍然没有回应 ACK,客户端就不再发送 SYN 包,然后断开 TCP 连接。

所以,总耗时是 1+2+4+8+16+32=63 秒,大约 1 分钟左右。

第二次和第三次握手丢失和第一次类似,都会触发超时重传机制,ack 报文是不会重传的,只能是主动方重传.

14.什么是 SYN 攻击?

我们都知道 TCP 连接建立是需要三次握手,假设攻击者短时间伪造不同 IP 地址的 SYN 报文,服务端每接收到一个 SYN 报文,就进入SYN_RCVD 状态,但服务端发送出去的 ACK + SYN 报文,无法得到未知 IP 主机的 ACK 应答,久而久之就会占满服务端的半连接队列,使得服务端不能为正常用户服务。

在 TCP 三次握手的时候,Linux 内核会维护两个队列,分别是:

  • 半连接队列,也称 SYN 队列;
  • 全连接队列,也称 accept 队列;

服务端收到客户端发起的 SYN 请求后,内核会把该连接存储到半连接队列,并向客户端响应 SYN+ACK,接着客户端会返回 ACK,服务端收到第三次握手的 ACK 后,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到 accept 队列,等待进程调用 accept 函数时把连接取出来。

image-20230414153151730

正常流程:

  • 当服务端接收到客户端的 SYN 报文时,会创建一个半连接的对象,然后将其加入到内核的「 SYN 队列」;
  • 接着发送 SYN + ACK 给客户端,等待客户端回应 ACK 报文;
  • 服务端接收到 ACK 报文后,从「 SYN 队列」取出一个半连接对象,然后创建一个新的连接对象放入到「 Accept 队列」;
  • 应用通过调用 accpet() socket 接口,从「 Accept 队列」取出连接对象。

不管是半连接队列还是全连接队列,都有最大长度限制,超过限制时,默认情况都会丢弃报文。

SYN 攻击方式最直接的表现就会把 TCP 半连接队列打满,这样当 TCP 半连接队列满了,后续再在收到 SYN 报文就会丢弃,导致客户端无法和服务端建立连接。

避免 SYN 攻击方式,可以有以下四种方法:

  • 调大 netdev_max_backlog;
  • 增大 TCP 半连接队列;
  • 开启 tcp_syncookies;
  • 减少 SYN+ACK 重传次数

15.全连接队列

在服务端可以使用 ss 命令,来查看 TCP 全连接队列的情况:

但需要注意的是 ss 命令获取的 Recv-Q/Send-Q 在「LISTEN 状态」和「非 LISTEN 状态」所表达的含义是不同的。

在「LISTEN 状态」时,Recv-Q/Send-Q 表示的含义如下:

image-20230417112317050

  • Recv-Q:当前全连接队列的大小,也就是当前已完成三次握手并等待服务端 accept() 的 TCP 连接个数;
  • Send-Q:当前全连接最大队列长度,上面的输出结果说明监听 8088 端口的 TCP 服务进程,最大全连接长度为 128;

在「非 LISTEN 状态」时,Recv-Q/Send-Q 表示的含义如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3CJCQuHk-1681712380730)(null)]

  • Recv-Q:已收到但未被应用进程读取的字节数;
  • Send-Q:已发送但未收到确认的字节数;

当服务端并发处理大量请求时,如果 TCP 全连接队列过小,就容易溢出。发生 TCP 全连接队溢出的时候,后续的请求就会被丢弃,这样就会出现服务端请求数量上不去的现象。

16.半连接队列

#查看当前TCP半连接队列长度
netstat -natp | grep SYN_RECV | wc -l

模拟 TCP 半连接溢出场景不难,实际上就是对服务端一直发送 TCP SYN 包,但是不回第三次握手 ACK,这样就会使得服务端有大量的处于 SYN_RECV 状态的 TCP 连接。

这其实也就是所谓的 SYN 洪泛、SYN 攻击、DDos 攻击。

链接丢弃场景:

  1. 如果半连接队列满了,并且没有开启 tcp_syncookies,则会丢弃;
  2. 若全连接队列满了,且没有重传 SYN+ACK 包的连接请求多于 1 个,则会丢弃;
  3. 如果没有开启 tcp_syncookies,并且 max_syn_backlog 减去 当前半连接队列长度小于 (max_syn_backlog >> 2),则会丢弃;

队列满的判断:

  • 连接队列的最大值是 sk_max_ack_backlog 变量,sk_max_ack_backlog 实际上是在 listen() 源码里指定的,也就是 min(somaxconn, backlog)
  • 连接队列的最大值是 max_qlen_log 变量,max_qlen_log 是在哪指定的呢?现在暂时还不知道,我们继续跟进;

17.TCP 四次挥手?

双方都可以主动断开连接,断开连接后主机中的「资源」将被释放,四次挥手的过程如下图:

image-20230414153520761

  • 客户端打算关闭连接,此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文,也即 FIN 报文,之后客户端进入 FIN_WAIT_1 状态。
  • 服务端收到该报文后,就向客户端发送 ACK 应答报文,接着服务端进入 CLOSE_WAIT 状态。
  • 客户端收到服务端的 ACK 应答报文后,之后进入 FIN_WAIT_2 状态。
  • 等待服务端处理完数据后,也向客户端发送 FIN 报文,之后服务端进入 LAST_ACK 状态。
  • 客户端收到服务端的 FIN 报文后,回一个 ACK 应答报文,之后进入 TIME_WAIT 状态
  • 服务端收到了 ACK 应答报文后,就进入了 CLOSE 状态,至此服务端已经完成连接的关闭。
  • 客户端在经过 2MSL 一段时间后,自动进入 CLOSE 状态,至此客户端也完成连接的关闭。

可以看到,每个方向都需要一个 FIN 和一个 ACK,因此通常被称为四次挥手

这里一点需要注意是:主动关闭连接的,才有 TIME_WAIT 状态。

18.TIME_WAIT 是 2MSL?

MSL 是 Maximum Segment Lifetime,报文最大生存时间,它是任何报文在网络上存在的最长时间,超过这个时间报文将被丢弃。因为 TCP 报文基于是 IP 协议的,而 IP 头中有一个 TTL 字段,是 IP 数据报可以经过的最大路由数,每经过一个处理他的路由器此值就减 1,当此值为 0 则数据报将被丢弃,同时发送 ICMP 报文通知源主机。

MSL 与 TTL 的区别: MSL 的单位是时间,而 TTL 是经过路由跳数。所以 MSL 应该要大于等于 TTL 消耗为 0 的时间,以确保报文已被自然消亡。

TTL 的值一般是 64,Linux 将 MSL 设置为 30 秒,意味着 Linux 认为数据报文经过 64 个路由器的时间不会超过 30 秒,如果超过了,就认为报文已经消失在网络中了

TIME_WAIT 等待 2 倍的 MSL,比较合理的解释是: 网络中可能存在来自发送方的数据包,当这些发送方的数据包被接收方处理后又会向对方发送响应,所以一来一回需要等待 2 倍的时间

主动断开方在发送完最后一个 ACK 报文后,再经过 2MSL,才能最终关闭和释放端口,这就意味着,相同端口的新 TCP 新连接,需要在 2MSL 的时间之后,才能够正常的建立。2MSL 这段时间内,旧连接所产生的所有数据报文,都已经从网络中消失了,从而,确保了下一个新的连接中不会出现这种旧连接请求报文。

  • 主动断开方等待 2MSL 的时间,是为了确保两端都能最终关闭。
  • 防止“旧连接的已失效的数据报文”出现在新连接中。

2MSL 的时间是从客户端接收到 FIN 后发送 ACK 开始计时的。如果在 TIME-WAIT 时间内,因为客户端的 ACK 没有传输到服务端,客户端又接收到了服务端重发的 FIN 报文,那么 2MSL 时间将重新计时

在 Linux 系统里 2MSL 默认是 60 秒,那么一个 MSL 也就是 30 秒。Linux 系统停留在 TIME_WAIT 的时间为固定的 60 秒

19.TIME_WAIT 状态?

主动发起关闭连接的一方,才会有 TIME-WAIT 状态。

需要 TIME-WAIT 状态,主要是两个原因:

  • 防止历史连接中的数据,被后面相同四元组的连接错误的接收;
  • 保证「被动关闭连接」的一方,能被正确的关闭;

原因一:防止历史连接中的数据,被后面相同四元组的连接错误的接收

为了能更好的理解这个原因,我们先来了解序列号(SEQ)和初始序列号(ISN)。

  • 序列号,是 TCP 一个头部字段,标识了 TCP 发送端到 TCP 接收端的数据流的一个字节,因为 TCP 是面向字节流的可靠协议,为了保证消息的顺序性和可靠性,TCP 为每个传输方向上的每个字节都赋予了一个编号,以便于传输成功后确认、丢失后重传以及在接收端保证不会乱序。序列号是一个 32 位的无符号数,因此在到达 4G 之后再循环回到 0。
  • 初始序列号,在 TCP 建立连接的时候,客户端和服务端都会各自生成一个初始序列号,它是基于时钟生成的一个随机数,来保证每个连接都拥有不同的初始序列号。初始化序列号可被视为一个 32 位的计数器,该计数器的数值每 4 微秒加 1,循环一次需要 4.55 小时。

通过前面我们知道,序列号和初始化序列号并不是无限递增的,会发生回绕为初始值的情况,这意味着无法根据序列号来判断新老数据。

为了防止历史连接中的数据,被后面相同四元组的连接错误的接收,因此 TCP 设计了 TIME_WAIT 状态,状态会持续 2MSL 时长,这个时间足以让两个方向上的数据包都被丢弃,使得原来连接的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接所产生的。

原因二:保证「被动关闭连接」的一方,能被正确的关闭

也就是说,TIME-WAIT 作用是等待足够的时间以确保最后的 ACK 能让被动关闭方接收,从而帮助其正常关闭。

如果客户端(主动关闭方)最后一次 ACK 报文(第四次挥手)在网络中丢失了,那么按照 TCP 可靠性原则,服务端(被动关闭方)会重发 FIN 报文。

假设客户端没有 TIME_WAIT 状态,而是在发完最后一次回 ACK 报文就直接进入 CLOSE 状态,如果该 ACK 报文丢失了,服务端则重传的 FIN 报文,而这时客户端已经进入到关闭状态了,在收到服务端重传的 FIN 报文后,就会回 RST 报文。

20.服务端大量 TIME_WAIT

服务端大量TIME_WAIT的原因:

如果服务端出现大量的 TIME_WAIT 状态的 TCP 连接,就是说明服务端主动断开了很多 TCP 连接

  • HTTP 没有使用长连接
  • HTTP 长连接超时
  • HTTP 长连接的请求数量达到上限

TIME_WAIT 状态的目的是:保证 ACK 重发丢弃延迟数据

如果服务端大量出现 TIME_WAIT 状态,会导致新建立 TCP 连接会出错,address already in use : connect 异常.

还会占用系统资源,比如文件描述符、内存资源、CPU 资源等;

Nginx 作为反向代理时,大量的短链接,可能导致 Nginx 上的 TCP 连接处于 time_wait 状态

每一个 time_wait 状态,都会占用一个「本地端口」,上限为 65535(16 bit,2 Byte);TCP 本地端口数量,上限为 65535(6.5w),这是因为 TCP 头部使用 16 bit,存储「端口号 」,因此约束上限为 65535

解决方法:

  • 客户端
    • HTTP 请求的头部,connection 设置为 keep-alive,保持存活一段时间
  • 服务端
    • 允许 time_wait 状态的 socket 被重用
    • 缩减 time_wait 时间,设置为 1 MSL

21.Socket 编程?

  • 服务端和客户端初始化 socket,得到文件描述符;
  • 服务端调用 bind,将 socket 绑定在指定的 IP 地址和端口;
  • 服务端调用 listen,进行监听;
  • 服务端调用 accept,等待客户端连接;
  • 客户端调用 connect,向服务端的地址和端口发起连接请求;
  • 服务端 accept 返回用于传输的 socket 的文件描述符;
  • 客户端调用 write 写入数据;服务端调用 read 读取数据;
  • 客户端断开连接时,会调用 close,那么服务端 read 读取数据的时候,就会读取到了 EOF,待处理完数据后,服务端调用 close,表示连接关闭。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NK47NcSL-1681712380394)(null)]

22.重传机制?

TCP 实现可靠传输的方式之一,是通过序列号与确认应答。

在 TCP 中,当发送端的数据到达接收主机时,接收端主机会返回一个确认应答消息,表示已收到消息。

常见的重传机制:

  • 超时重传: 超时的情况
  • 快速重传: 丢包的情况
  • SACK: 选择性确认
  • D-SACK: 使用了 SACK 来告诉「发送方」有哪些数据被重复接收了

什么是 RTT?

image-20230414160639645

RTT(Round-Trip Time 往返时延),RTT 指的是数据发送时刻到接收到确认的时刻的差值,也就是包的往返时间。

超时重传时间是以 RTO (Retransmission Timeout 超时重传时间)表示。

有两种超时时间不同的情况:

  • 当超时时间 RTO 较大时,重发就慢,丢了老半天才重发,没有效率,性能差;
  • 当超时时间 RTO 较小时,会导致可能并没有丢就重发,于是重发的就快,会增加网络拥塞,导致更多的超时,更多的超时导致更多的重发。

超时重传时间 RTO 的值应该略大于报文往返 RTT 的值。采样计算 RTT 的值,不能只用某一次的值,不准确.

23.滑动窗口?

一个发送一个回答,这样的传输方式有一个缺点:数据包的往返时间越长,通信的效率就越低

为解决这个问题,TCP 引入了窗口这个概念。即使在往返时间较长的情况下,它也不会降低网络通信的效率。

那么有了窗口,就可以指定窗口大小,窗口大小就是指无需等待确认应答,而可以继续发送数据的最大值

窗口的实现实际上是操作系统开辟的一个缓存空间,发送方主机在等到确认应答返回之前,必须在缓冲区中保留已发送的数据。如果按期收到确认应答,此时数据就可以从缓存区清除。

假设窗口大小为 3 个 TCP 段,那么发送方就可以「连续发送」 3 个 TCP 段,并且中途若有 ACK 丢失,可以通过「下一个确认应答进行确认」。如下图:

image-20230414161625794

图中的 ACK 600 确认应答报文丢失,也没关系,因为可以通过下一个确认应答进行确认,只要发送方收到了 ACK 700 确认应答,就意味着 700 之前的所有数据「接收方」都收到了。这个模式就叫累计确认或者累计应答

TCP 头里有一个字段叫 Window,也就是窗口大小。

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

24.TCP 协议可靠性

总体来说,TCP 协议的可靠性,主要通过以下几点来保障:

  1. 应用数据分割成 TCP 认为最适合发送的数据块。这部分是通过 MSS(最大数据包长度)选项来控制的,通常这种机制也被称为一种协商机制,MSS 规定了 TCP 传往另一端的最大数据块的长度。值得注意的是,MSS 只能出现在 SYN 报文段中,若一方不接收来自另一方的 MSS 值,则 MSS 就定为 536 字节。一般来讲,MSS 值还是越大越好,这样可以提高网络的利用率。
  2. 重传机制。设置定时器,等待确认包,如果定时器超时还没有收到确认包,则报文重传。
  3. 对首部和数据进行校验。
  4. 接收端对收到的数据进行排序,然后交给应用层。
  5. 接收端丢弃重复的数据。
  6. TCP 还提供流量控制,主要是通过滑动窗口来实现流量控制。

25.拥塞控制

对于拥塞控制来说,TCP 每条连接都需要维护两个核心状态:

  • 拥塞窗口(Congestion Window,cwnd)
  • 慢启动阈值(Slow Start Threshold,ssthresh)

涉及到的算法有这几个:

  • 慢启动
  • 拥塞避免
  • 快速重传和快速恢复

26.长连接区别?

HTTP 长连接和 TCP 长连接有区别?

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

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

TCP 的 Keepalive:

TCP 的 Keepalive 这东西其实就是 TCP 的保活机制,如果两端的 TCP 连接一直没有数据交互,达到了触发 TCP 保活机制的条件,那么内核里的 TCP 协议栈就会发送探测报文。

  • 如果对端程序是正常工作的。当 TCP 保活的探测报文发送给对端, 对端会正常响应,这样 TCP 保活时间会被重置,等待下一个 TCP 保活时间的到来。
  • 如果对端主机崩溃,或对端由于其他原因导致报文不可达。当 TCP 保活的探测报文发送给对端后,石沉大海,没有响应,连续几次,达到保活探测次数后,TCP 会报告该 TCP 连接已经死亡

所以,TCP 保活机制可以在双方没有数据交互的情况,通过探测报文,来确定对方的 TCP 连接是否存活,这个工作是在内核完成的。

TCP 还设有一个保活计时器,Client 端如果出现故障,Server 端不能一直等下去,这样会浪费系统资源。每收到一次 Client 客户端的数据帧后,Server 端都的保活计时器会复位。计时器的超时时间通常是设置为 2 小时,若 2 小时还没有收到 Client 端的任何数据帧,Server 端就会发送一个探测报文段,以后每隔 75 秒钟发送一次。若一连发送 10 个探测报文仍然没反应,Server 端就认为 Client 端出了故障,接着就关闭连接。如果觉得保活计时器的两个多小时的间隔太长,可以自行调整 TCP 连接的保活参数。

总结:

HTTP 的 Keep-Alive 也叫 HTTP 长连接,该功能是由「应用程序」实现的,可以使得用同一个 TCP 连接来发送和接收多个 HTTP 请求/应答,减少了 HTTP 短连接带来的多次 TCP 连接建立和释放的开销。

TCP 的 Keepalive 也叫 TCP 保活机制,该功能是由「内核」实现的,当客户端和服务端长达一定时间没有进行数据交互时,内核为了确保该连接是否还有效,就会发送探测报文,来检测对方是否还在线,然后来决定是否要关闭该连接。

27.说说 UDP?

UDP的特点:

  • 无连接:socket 创建好以后,就可以立即尝试读写数据了
  • 不可靠
  • 面向数据报:读写数据都是以 DatagramPacket 为单位进行的
  • 全双工:一个 socket 既能读,也能写。

UDP 不提供复杂的控制机制,利用 IP 提供面向「无连接」的通信服务。

UDP 协议真的非常简单,头部只有 8 个字节(64 位),UDP 的头部格式如下:

image-20230414002248190

  • 目标和源端口:主要是告诉 UDP 协议应该把报文发给哪个进程。
  • 包长度:该字段保存了 UDP 首部的长度跟数据的长度之和。
  • 校验和:校验和是为了提供可靠的 UDP 首部和数据而设计,防止收到在网络传输中受损的 UDP 包。

28.UDP 和 TCP 有什么区别?

1. 连接

  • TCP 是面向连接的传输层协议,传输数据前先要建立连接。
  • UDP 是不需要连接,即刻传输数据。

2. 服务对象

  • TCP 是一对一的两点服务,即一条连接只有两个端点。
  • UDP 支持一对一、一对多、多对多的交互通信

3. 可靠性

  • TCP 是可靠交付数据的,数据可以无差错、不丢失、不重复、按序到达。
  • UDP 是尽最大努力交付,不保证可靠交付数据。但是我们可以基于 UDP 传输协议实现一个可靠的传输协议,比如 QUIC 协议,具体可以参见这篇文章:如何基于 UDP 协议实现可靠传输?(opens new window)

4. 拥塞控制、流量控制

  • TCP 有拥塞控制和流量控制机制,保证数据传输的安全性。
  • UDP 则没有,即使网络非常拥堵了,也不会影响 UDP 的发送速率。

5. 首部开销

  • TCP 首部长度较长,会有一定的开销,首部在没有使用「选项」字段时是 20 个字节,如果使用了「选项」字段则会变长的。
  • UDP 首部只有 8 个字节,并且是固定不变的,开销较小。

6. 传输方式

  • TCP 是流式传输,没有边界,但保证顺序和可靠。
  • UDP 是一个包一个包的发送,是有边界的,但可能会丢包和乱序。

7. 分片不同

  • TCP 的数据大小如果大于 MSS 大小,则会在传输层进行分片,目标主机收到后,也同样在传输层组装 TCP 数据包,如果中途丢失了一个分片,只需要传输丢失的这个分片。
  • UDP 的数据大小如果大于 MTU 大小,则会在 IP 层进行分片,目标主机收到后,在 IP 层组装完数据,接着再传给传输层。

总结:

TCP:打电话

  • 连接,稳定
  • 三次握手 四次挥手
  • 客户端、服务端
  • 传输完成,释放连接,效率低

UDP:发短信

  • 不连接,不稳定
  • 客户端、服务端:没有明确的界限
  • 不管有没有准备好,都可以发给你.
  • 导弹
  • DDOS:洪水攻击!(饱和攻击)

29.UDP 包长度

为什么 UDP 头部有「包长度」字段,而 TCP 头部则没有「包长度」字段呢?

先说说 TCP 是如何计算负载数据长度:

image-20230414002943170

其中 IP 总长度 和 IP 首部长度,在 IP 首部格式是已知的。TCP 首部长度,则是在 TCP 首部格式已知的,所以就可以求得 TCP 数据的长度。确实感觉 UDP 的「包长度」是冗余的。

我查阅了很多资料,我觉得有两个比较靠谱的说法:

  • 第一种说法:因为为了网络设备硬件设计和处理方便,首部长度需要是 4 字节的整数倍。如果去掉 UDP 的「包长度」字段,那 UDP 首部长度就不是 4 字节的整数倍了,所以我觉得这可能是为了补全 UDP 首部长度是 4 字节的整数倍,才补充了「包长度」字段。
  • 第二种说法:如今的 UDP 协议是基于 IP 协议发展的,而当年可能并非如此,依赖的可能是别的不提供自身报文长度或首部长度的网络层协议,因此 UDP 报文首部需要有长度字段以供计算

30.TCP 和 UDP 一个端口号?

在数据链路层中,通过 MAC 地址来寻找局域网中的主机。在网际层中,通过 IP 地址来寻找网络中互连的主机或路由器。在传输层中,需要通过端口进行寻址,来识别同一计算机中同时通信的不同应用程序。

所以,传输层的「端口号」的作用,是为了区分同一个主机上不同应用程序的数据包。

传输层有两个传输协议分别是 TCP 和 UDP,在内核中是两个完全独立的软件模块。

当主机收到数据包后,可以在 IP 包头的「协议号」字段知道该数据包是 TCP/UDP,所以可以根据这个信息确定送给哪个模块(TCP/UDP)处理,送给 TCP/UDP 模块的报文根据「端口号」确定送给哪个应用程序处理。

image-20230414142344255

因此,TCP/UDP 各自的端口号也相互独立,如 TCP 有一个 80 号端口,UDP 也可以有一个 80 号端口,二者并不冲突。

五.IP 篇

1.什么是 Ip?

IP 在 TCP/IP 参考模型中处于第三层,也就是网络层

网络层的主要作用是:实现主机与主机之间的通信,也叫点对点(end to end)通信。

如何准确的定位网络上的一台主机 192.168.16.124

通过端口,定位到这个计算机上的某个运行的资源

2.InetAddress

Java 提供 InetAddress 类来封装 IP 地址或域名。InetAddress 类有两个子类:Inet4Address 和 Inet6Address,分别用于封装 4 个字节的 IP 地址和 6 个字节的 IP 地址。InetAddress 内部对地址数字进行隐藏,用户不需要了解实现地址的细节,只需了解如何调用相应的方法即可。
InetAddress 类无构造方法,因此不能直接创建其对象,而是通过该类的静态方法创建一个 InetAddress 对象或 InetAddress 数组。

ip 地址:InetAddress

  • 唯一定位一台网络上计算机
  • 127.0.0.1:本机 localhost
  • ip 地址的分类
    • ipv4 / ipv6
    • IPV4 127.0.0.1 ,4 个字节组成。0-255,42 亿~;30 亿都在北美,亚洲 41 乙。2011 年就用尽;
    • IPV6 , 128 位。8 个无符号整数!

3.端口

端口表示计算机上的一个程序的进程;

  • 不同的进程有不同的端口号!用来区分软件!

  • 被规定 0~65535

  • TCPUDP: 65535 *2 tcp: 80, udp: 801g、単 ↑ 物以下,端口号不能突

  • 端口分类

    • 公有端口 0-1023

      • HTTP: 80
      • HTTPS: 443
      • FTP: 21
      • Telent・23
    • 程序注册端口:1024~49151,分配用户或者程序

      • Tomcat: 8080

      • MYSOL: 3306

      • Oracle: 1521

    • 动态、私有:49152~65535

#查看所有端口
netstat -ano


#查看指定的端口
netstat -ano |findstr "5900"

4.路由表算法?

使用一下命令查看路由表

route -n

image-20230320015110998

什么是距离向量算法、链路状态算法、路径向量算法

距离向量算法、链路状态算法和路径向量算法是网络层中用来计算路由表的常见算法。它们分别采用不同的计算方式来选择最佳路径,并且在路由器之间交换信息以更新路由表。

  1. 距离向量算法:距离向量算法(Distance Vector,又称为 Bellman-Ford 算法)是一种分布式的路由选择算法,每个节点在其路由表中存储到每个目标节点的距离向量(距离、下一跳节点),并且定期将其向邻居节点发送。当节点收到邻居节点的路由信息后,更新自己的路由表,并且将自己的路由表向邻居节点发送。通过多次更新和交换路由信息,每个节点最终可以计算出到目标节点的最短路径。距离向量算法具有简单、容错、实现方便等特点,但是其收敛速度较慢,容易出现路由环路问题。
  2. 链路状态算法:链路状态算法(Link State,又称为 Dijkstra 算法)是一种集中式的路由选择算法,每个节点需要向网络中的所有节点广播链路状态信息(连接情况、带宽等),并且收集所有节点的链路状态信息。然后,节点使用 Dijkstra 算法计算出到目标节点的最短路径,并更新自己的路由表。链路状态算法具有快速收敛、路由选择准确等优点,但是需要大量的网络带宽和计算资源。
  3. 路径向量算法:路径向量算法(Path Vector)是一种改进的距离向量算法,它引入了路径向量的概念,即在路由表中存储到每个目标节点的路径向量(路径、下一跳节点)。路径向量算法中,每个节点需要向邻居节点发送其到所有目标节点的路径向量,并且使用 BGP(边界网关协议)来交换信息。通过多次更新和交换路径向量信息,每个节点可以计算出到目标节点的最佳路径。路径向量算法具有快速收敛、容错性好、可扩展性强等优点,但是需要更多的存储空间和计算资源。

5.如何寻址?

我们一般用 IP 地址给设备进行编号,对于 IPv4 协议, IP 地址共 32 位,分成了四段(比如,192.168.100.1),每段是 8 位。ip 地址太多,不可能一个个去遍历寻址.

需要将 IP 地址分成两种意义:

  • 一个是网络号,负责标识该 IP 地址是属于哪个「子网」的;
  • 一个是主机号,负责标识同一「子网」下的不同主机;

需要配合子网掩码才能算出 IP 地址 的网络号和主机号。

比如 10.100.122.0/24,后面的/24表示就是 255.255.255.0 子网掩码,255.255.255.0 二进制是「11111111-11111111-11111111-00000000」,一共是 24 个 1,为了简化子网掩码的表示,用/24 代替 255.255.255.0。

将 10.100.122.2 和 255.255.255.0 进行按位与运算,就可以得到网络号,如下图:

img

将 255.255.255.0 取反后与 IP 地址进行按位与运算,就可以得到主机号是 2。

00000000 00000000 00000000 11111111
00001010 1100100  1111010  00000010

00000000 00000000 00000000 00000010

那么在寻址的过程中,先匹配到相同的网络号(表示要找到同一个子网),才会去找对应的主机。

所以,IP 协议的寻址作用是告诉我们去往下一个目的地该朝哪个方向走,路由则是根据「下一个目的地」选择路径。寻址更像在导航,路由更像在操作方向盘

6.网络层与数据链路层关系?

IP(网络层) 和 MAC (数据链路层)之间的区别和关系。

其实很容易区分,在上面我们知道 IP 的作用是主机之间通信用的,而 MAC 的作用则是实现「直连」的两个设备之间通信,而 IP 则负责在「没有直连」的两个网络之间进行通信传输。

以旅行举例,飞机票和地铁票都是去往特定的地点的,每张票只能够在某一限定区间内移动,此处的「区间内」就如同通信网络中数据链路。

在区间内移动相当于数据链路层,充当区间内两个节点传输的功能,区间内的出发点好比源 MAC 地址,目标地点好比目的 MAC 地址。

整个旅游行程表就相当于网络层,充当远程定位的功能,行程的开始好比源 IP,行程的终点好比目的 IP 地址。

image-20230414163650530

其实,在网络中数据包传输中也是如此,源 IP 地址和目标 IP 地址在传输过程中是不会变化的(前提:没有使用 NAT 网络),只有源 MAC 地址和目标 MAC 一直在变化。

7.IP 地址分类

IP 地址分类成了 5 种类型,分别是 A 类、B 类、C 类、D 类、E 类。

IP 地址是以网络号和主机号来标示网络上的主机,我们把网络号相同的主机称之为本地网络,网络号不相同的主机称之为远程网络主机,本地网络中的主机可以直接进行通信;远程网络中的主机要相互通信必须通过**本地网关(Gateway)**来传递转发数据。

标准分类的 ip 地址的网络号是

  • A 类是前 8 位
  • B 类是前 16 位
  • C 类是前 24 位

图中黄色部分为分类号,用以区分 IP 地址类别。

image-20230414163906398

A、B、C 分类对应的地址范围、最大主机个数。

image-20230414164019758

而 D 类和 E 类地址是没有主机号的,所以不可用于主机 IP,D 类常被用于多播,E 类是预留的分类,暂时未使用。注:主机最大连接数减去 2,是除主机号为全 0 和全 1 的特殊 IP 地址。

image-20230414164252903

8.Ip 举例

比如 10.100.122.2/24,这种地址表示形式就是 CIDR,/24 表示前 24 位是网络号,剩余的 8 位是主机号。

image-20230414164518927

表示局域网的三类IP(剩下的都属于外网IP):

  • A 类:10.0.0.0 ~ 10.255.255.255
  • B 类:172.16.0.0 ~ 172.31.255.255
  • C 类:192.168.0.0 ~ 192.168.255.255

9. ICMP 协议?

ICMP 全称是 Internet Control Message Protocol,也就是互联网控制报文协议

ICMP 主要的功能包括:确认 IP 包是否成功送达目标地址、报告发送过程中 IP 包被废弃的原因和改善网络设置等。

ping 是基于 ICMP 协议工作的,ICMP 报文是封装在 IP 包里面,它工作在网络层,是 IP 协议的助手。

IP 协议并不是一个可靠的协议,它不保证数据被送达,那么,自然的,保证数据送达的工作应该由其他的模块来完成。其中一个重要的模块就是 ICMP(网络控制报文)协议。ICMP 不是高层协议,而是 IP 层的协议。

当传送 IP 数据包发生错误。比如主机不可达,路由不可达等等,ICMP 协议将会把错误信息封包,然后传送回给主机。给主机一个处理错误的机会,这 也就是为什么说建立在 IP 层以上的协议是可能做到安全的原因。

image-20230414164834073

10.IP 模块

TCP 模块在执行连接、收发、断开等各阶段操作时,都需要委托 IP 模块将数据封装成网络包发送给通信对象。

IP 报文头部的格式:

IP 包头格式

在 IP 协议里面需要有源地址 IP目标地址 IP

  • 源地址 IP,即是客户端输出的 IP 地址;多个网卡时通过路由表规则进行选择源地址 IP
  • 目标地址,即通过 DNS 域名解析得到的 Web 服务器 IP。

因为 HTTP 是经过 TCP 传输的,所以在 IP 包头的协议号,要填写为 06(十六进制),表示协议为 TCP。

网络包的报文:

IP 层报文

11.MAC 地址

生成了 IP 头部之后,接下来网络包还需要在 IP 头部的前面加上 MAC 头部

MAC 头部是以太网使用的头部,它包含了接收方和发送方的 MAC 地址等信息。

在 MAC 包头里需要发送方 MAC 地址接收方目标 MAC 地址,用于两点之间的传输

一般在 TCP/IP 通信里,MAC 包头的协议类型只使用:

  • 0800 : IP 协议
  • 0806 : ARP 协议

需要 ARP 协议帮我们找到路由器的 MAC 地址。在后续操作系统会把本次查询结果放到一块叫做 ARP 缓存的内存空间留着以后用,不过缓存的时间就几分钟。

  • 先查询 ARP 缓存,如果其中已经保存了对方的 MAC 地址,就不需要发送 ARP 查询,直接使用 ARP 缓存中的地址。
  • 而当 ARP 缓存中不存在对方 MAC 地址时,则发送 ARP 广播查询。

ARP 广播

ARP 缓存

arp -a

image-20230320015757244

网络包的报文:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vojqu30f-1681712379997)(null)]

12.网卡的作用?

网络包只是存放在内存中的一串二进制数字信息,没有办法直接发送给对方。因此,我们需要将数字信息转换为电信号,才能在网线上传输,也就是说,这才是真正的数据发送过程。

负责执行这一操作的是网卡,要控制网卡还需要靠网卡驱动程序

网卡驱动获取网络包之后,会将其复制到网卡内的缓存区中,接着会在其开头加上报头和起始帧分界符,在末尾加上用于检测错误的帧校验序列

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9GllRrqG-1681712375917)(http://qinyingjie.top/blogImg/%E6%95%B0%E6%8D%AE%E5%8C%85.drawio.png)]

  • 起始帧分界符是一个用来表示包起始位置的标记
  • 末尾的 FCS(帧校验序列)用来检查包传输过程是否有损坏

最后网卡会将包转为电信号,通过网线发送出去。

13.交换机的作用?

交换机的设计是将网络包原样转发到目的地。交换机工作在 MAC 层,也称为二层网络设备

交换机接收包操作:

首先,电信号到达网线接口,交换机里的模块进行接收,接下来交换机里的模块将电信号转换为数字信号。

然后通过包末尾的 FCS 校验错误,如果没问题则放到缓冲区。这部分操作基本和计算机的网卡相同,但交换机的工作方式和网卡不同。

计算机的网卡本身具有 MAC 地址,并通过核对收到的包的接收方 MAC 地址判断是不是发给自己的,如果不是发给自己的则丢弃;相对地,交换机的端口不核对接收方 MAC 地址,而是直接接收所有的包并存放到缓冲区中。因此,和网卡不同,交换机的端口不具有 MAC 地址

将包存入缓冲区后,接下来需要查询一下这个包的接收方 MAC 地址是否已经在 MAC 地址表中有记录了。

交换机的 MAC 地址表主要包含两个信息:

  • 一个是设备的 MAC 地址,
  • 另一个是该设备连接在交换机的哪个端口上。

所以,交换机根据 MAC 地址表查找 MAC 地址,然后将信号发送到相应的端口

交换机的 MAC 地址表

14.路由器作用?

路由器和交换机的区别:

网络包经过交换机之后,现在到达了路由器,并在此被转发到下一个路由器或目标设备。

这一步转发的工作原理和交换机类似,也是通过查表判断包转发的目标。

不过在具体的操作过程上,路由器和交换机是有区别的。

  • 因为路由器是基于 IP 设计的,俗称三层网络设备,路由器的各个端口都具有 MAC 地址和 IP 地址;
  • 交换机是基于以太网设计的,俗称二层网络设备,交换机的端口不具有 MAC 地址。

路由器的包接收操作:

首先,电信号到达网线接口部分,路由器中的模块会将电信号转成数字信号,然后通过包末尾的 FCS 进行错误校验。

如果没问题则检查 MAC 头部中的接收方 MAC 地址,看看是不是发给自己的包,如果是就放到接收缓冲区中,否则就丢弃这个包。

总的来说,路由器的端口都具有 MAC 地址,只接收与自身地址匹配的包,遇到不匹配的包则直接丢弃。

完成包接收操作之后,路由器就会去掉包开头的 MAC 头部。

MAC 头部的作用就是将包送达路由器,其中的接收方 MAC 地址就是路由器端口的 MAC 地址。因此,当包到达路由器之后,MAC 头部的任务就完成了,于是 MAC 头部就会被丢弃

发送出去的网络包会通过交换机到达下一个路由器。由于接收方 MAC 地址就是下一个路由器的地址,所以交换机会根据这一地址将包传输到下一个路由器。

在网络包传输的过程中,源 IP 和目标 IP 始终是不会变的,一直变化的是 MAC 地址,因为需要 MAC 地址在以太网内进行两个设备之间的包传输。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GbkeAYoS-1681712380311)(null)]

15.DNS 服务器

DNS 是计算机域名系统 (Domain Name System 或 Domain Name Service) 的缩写,它是由解析器以及域名服务器组成的。域名服务器是指保存有该网络中所有主机的域名和对应 IP 地址,并具有将域名转换为 IP 地址功能的服务器。

有一种服务器就专门保存了 Web 服务器域名与 IP 的对应关系,它就是 DNS 服务器。

DNS 中的域名都是用句点来分隔的,比如 www.server.com,这里的句点代表了不同层次之间的界限

在域名中,越靠右的位置表示其层级越高。实际上域名最后还有一个点,比如 www.server.com.,这个最后的一个点代表根域名。也就是,. 根域是在最顶层,它的下一层就是 .com 顶级域,再下面是 server.com

所以域名的层级关系类似一个树状结构:

  • 根 DNS 服务器(.)
  • 顶级域 DNS 服务器(.com)
  • 权威 DNS 服务器(server.com)
  • 如果缓存中有,直接用缓存中的返回

DNS 树状结构

16.什么是协议栈?

通过 DNS 获取到 IP 后,就可以把 HTTP 的传输工作交给操作系统中的协议栈

image-20230320013526581

应用程序(浏览器)通过调用 Socket 库,来委托协议栈工作。协议栈的上半部分有两块,分别是负责收发数据的 TCP 和 UDP 协议,这两个传输协议会接受应用层的委托执行收发数据的操作。

协议栈的下面一半是用 IP 协议控制网络包收发操作,在互联网上传数据时,数据会被切分成一块块的网络包,而将网络包发送给对方的操作就是由 IP 负责的。

此外 IP 中还包括 ICMP 协议和 ARP 协议。

  • ICMP 用于告知网络包传送过程中产生的错误以及各种控制信息。
  • ARP 用于根据 IP 地址查询相应的以太网 MAC 地址。

17.ARP 协议找到 MAC 地址

ARP 是根据 IP 地址获取 MAC 地址的一种协议。

IP 协议只能帮我们进行一个远程的定位,这远远还不够,我们数据的传输其实是在链路上面进行传输的,这也是数据链路层的工作,我们需要通过 MAC 地址去进行传输,因为 IP 是互联网世界的唯一逻辑地址,而 MAC 则是计算机唯一的物理地址,此时我们需要去浏览器本地缓存中寻找对应目标 IP 地址的下一跳 MAC 地址,如果没有则会交由 ARP 去帮我们寻找 MAC 地址。

在同个网关下,ARP 会广播发出 ARP 请求,ARP 请求携带了源 IP 地址,源 MAC 地址,目标 IP 地址,此时在这个网关下的全部主机都将收到这个 ARP 请求,它们会将 ARP 请求中的目标 IP 地址拿出来跟自己的进行对比,如果跟自己的一样,则证明我就是它的下一跳,此时这个主机会先将源 IP 地址和源 MAC 地址写进自己的缓存中,再将自己的 IP 地址和 MAC 地址封装进 ARP 响应报文中单播返回给请求者。 在不同网关下,ARP 请求会被路由器阻断,路由器会返回自己的 MAC 地址,然后请求者将数据发送给路由器,交由路由器去帮我们寻找发送数据给目标主机。

18.网络端口的分类

按端口号可分为 3 大类:

  • 公认端口(Well-KnownPorts):范围从 0 到 1023
  • 动态端口(Dynamic Ports):范围从 1024 到 65535
    • 注册端口(RegisteredPorts):从 1024 到 49151
    • 动态和/或私有端口(Dynamicand/orPrivatePorts):从 49152 到 65535。

19.0.0.0.0 是什么?

IPV4 中,0.0.0.0 地址被用于表示一个无效的,未知的或者不可用的目标。

在服务器中,0.0.0.0 指的是本机上的所有 IPV4 地址,如果一个主机有两个 IP 地址,192.168.1.1 和 10.1.2.1,并且该主机上的一个服务监听的地址是 0.0.0.0,那么通过两个 ip 地址都能够访问该服务。

在路由中,0.0.0.0 表示的是默认路由,即当路由表中没有找到完全匹配的路由的时候所对应的路由。

用途总结:

  • 当一台主机还没有被分配一个 IP 地址的时候,用于表示主机本身。(DHCP 分配 IP 地址的时候)
  • 用作默认路由,表示”任意 IPV4 主机”。
  • 用来表示目标机器不可用。
  • 用作服务端,表示本机上的任意 IPV4 地址。

20.127.0.0.1 是什么?

127.0.0.1 属于{127,}集合中的一个,而所有网络号为 127 的地址都被称之为回环地址,所以回环地址 != 127.0.0.1,它们是包含关系,即回环地址包含 127.0.0.1。
回环地址:所有发往该类地址的数据包都应该被 loop back。

用途:
回环测试,通过使用 ping 127.0.0.1 测试某台机器上的网络设备,操作系统或者 TCP/IP 实现是否工作正常。
DDos 攻击防御:网站收到 DDos 攻击之后,将域名 A 记录到 127.0.0.1,即让攻击者自己攻击自己。
大部分 Web 容器测试的时候绑定的本机地址。

127.0.0.1 是一个环回地址。并不表示“本机”。0.0.0.0才是真正表示“本网络中的本机”。

21.localhost 是什么?

相比 127.0.0.1,localhost 具有更多的意义。localhost 是个域名,而不是一个 ip 地址。之所以我们经常把 localhost 与 127.0.0.1 认为是同一个是因为我们使用的大多数电脑上都讲 localhost 指向了 127.0.0.1 这个地址。

127.0.0.1   localhost

几乎每台电脑上都会有的默认配置,但是 localhost 的意义并不局限于 127.0.0.1。
localhost 是一个域名,用于指代 this computer 或者 this host,可以用它来获取运行在本机上的网络服务。

六.websocket 篇

1.为什么需要 websocket?

在 HTTP 协议中,所有的请求都是由客户端发起的,由服务端进行响应,服务端无法向客户端推送消息,但是在一些需要即时通信的应用中,又不可避免地需要服务端向客户端推送消息,传统的解决方案主要有如下几种。

轮询:轮询是最简单的一种解决方案,所谓轮询,就是客户端在固定的时间间隔下不停地向服务端发送请求,查看服务端是否有最新的数据,若服务端有最新的数据,则返回给客户端,若服务端没有,则返回一个空的 JSON 或者 XML 文档。轮询对开发人员而言实现方便,但是弊端也很明显:客户端每次都要新建 HTTP 请求,服务端要处理大量的无效请求,在高并发场景下会严重拖慢服务端的运行效率,同时服务端的资源被极大的浪费了,因此这种方式并不可取。

长轮询:长轮询是传统轮询的升级版,当聪明的工程师看到轮询所存在的问题后,就开始解决问题,于是有了长轮询。不同于传统轮询,在长轮询中,服务端不是每次都会立即响应客户端的请求,只有在服务端有最新数据的时候才会立即响应客户端的请求,否则服务端会持有这个请求而不返回,到有新数据时才返回 这种方式可以在一定程度上节省网络资源和服务器资源,但是也存在问题,例如:

  • 如果浏览器在服务器响应之前有新数据要发送,就只能创建一个新的并发请求,或者先尝试断掉当前请求,再创建新的请求。
  • TCP 和 HTTP 规范中都有连接超时一说,所以所谓的长轮询并不能一直持续,服务端和客户端的连接需要定期的连接和关闭再连接,这又增大了程序员的工作量,当然也有一些技术能够延长每次连接的时间,但毕竟是非主流解决方案。

其实,传统的解决方案不止这种,但是无论哪种解决方案都有自身的缺陷,于是有了 WebSocket。

2.什么是 websocket?

websocket 出现之前使用如下 2 种方式实现服务端推数据,实质是客户端拉数据

  • HTTP 不断轮询
  • 长轮询

我们知道 TCP 连接的两端,同一时间里双方都可以主动向对方发送数据。这就是所谓的全双工

而现在使用最广泛的HTTP/1.1,也是基于 TCP 协议的,同一时间里,客户端和服务器只能有一方主动发数据,这就是所谓的半双工

image-20230413175920438

3.WebSocket 特点?

  • WebSocket 使用时需要先创建连接,这使得 WebSocket 成为一种有状态的协议,在之后的通信过程中可以省略部分状态信息(例如身份认证等)。
  • WebSocket 连接在端口 80 ( WS )或者 443 ( wss )上创建,与 HTTP 使用的端口相同,这样,基本上所有的防火墙都不会阻 WebSocket 连接。
  • WebSocket 使用 HTTP 协议进行握手,因此它可以自然而然地集成到网络浏览器和 HTTP 服务器中,而不需要额外的成本。
  • 心跳消息( ping 和 pong )将被反复的发送,进而保持 WebSocket 连接一直处于活跃状态。
  • 使用该协议,当消息启动或者到达的时候,服务端和客户端都可以知道。
  • WebSocket 连接关闭时将发送一个特殊的关闭消息
  • WebSocket 支持跨域,可以避免 Ajax 的限制
  • HTTP 规范要求浏览器将并发连接数限制为每个主机名两个连接,但是当我们使用 WebSocket 的时候,当握手完成之后,该限制就不存在了,因为此时的连接已经不再是 HTTP 连接了。
  • WebSocket 协议支持扩展,用户可以扩展协议,实现部分自定义的子协议。
  • 更好的二进制支持以及更好的压缩效果。

4.如何建立连接?

为了兼容这些使用场景。浏览器在 TCP 三次握手建立连接之后,都统一使用 HTTP 协议先进行一次通信。

  • 如果此时是普通的 HTTP 请求,那后续双方就还是老样子继续用普通 HTTP 协议进行交互,这点没啥疑问。
  • 如果这时候是想建立 WebSocket 连接,就会在 HTTP 请求里带上一些特殊的 header 头,如下:
Connection: Upgrade
Upgrade: WebSocket
Sec-WebSocket-Key: T2a6wZlAwhgQNqruZ2YUyg==\r\n

这些 header 头的意思是,浏览器想升级协议(Connection: Upgrade),并且想升级成 WebSocket 协议(Upgrade: WebSocket)。同时带上一段随机生成的 base64 码(Sec-WebSocket-Key),发给服务器。

如果服务器正好支持升级成 WebSocket 协议。就会走 WebSocket 握手流程,同时根据客户端生成的 base64 码,用某个公开的算法变成另一段字符串,放在 HTTP 响应的 Sec-WebSocket-Accept 头里,同时带上101状态码,发回给浏览器。HTTP 的响应如下:

HTTP/1.1 101 Switching Protocols\r\n
Sec-WebSocket-Accept: iBJKv/ALIW2DobfoA4dmr3JHBCY=\r\n
Upgrade: WebSocket\r\n
Connection: Upgrade\r\n

HTTP 状态码=200(正常响应)的情况,大家见得多了。101 确实不常见,它其实是指协议切换。之后,浏览器也用同样的公开算法base64码转成另一段字符串,如果这段字符串跟服务器传回来的字符串一致,那验证通过。

就这样经历了一来一回两次 HTTP 握手,WebSocket 就建立完成了,后续双方就可以使用 webscoket 的数据格式进行通信了。

image-20230413180125579

5.WebSocket 的消息格式

数据包在 WebSocket 中被叫做

image-20230413180236414

opcode 字段:这个是用来标志这是个什么类型的数据帧。比如。

  • 等于 1 ,是指 text 类型(string)的数据包
  • 等于 2 ,是二进制数据类型([]byte)的数据包
  • 等于 8 ,是关闭连接的信号

payload 字段:存放的是我们真正想要传输的数据的长度,单位是字节。比如你要发送的数据是字符串"111",那它的长度就是3

存放 payload 长度的字段有好几个,我们既可以用最前面的7bit, 也可以用后面的7+16bit 或 7+64bit。

payload data 字段:这里存放的就是真正要传输的数据,在知道了上面的 payload 长度后,就可以根据这个值去截取对应的数据。

WebSocket 的数据格式也是数据头(内含 payload 长度) + payload data 的形式。这是因为 TCP 协议本身就是全双工,但直接使用纯裸 TCP去传输数据,会有粘包的"问题"。为了解决这个问题,上层协议一般会用消息头+消息体的格式去重新包装要发的数据。

6. WebSocket 的使用场景

场景:

  • 即时聊天
  • 多人在线游戏
  • 应用集群通信
  • 系统性能实时监控
  • 在线股票网站

WebSocket 完美继承了 TCP 协议的全双工能力,并且还贴心的提供了解决粘包的方案。

它适用于需要服务器和客户端(浏览器)频繁交互的大部分场景,比如网页/小程序游戏,网页聊天室,以及一些类似飞书这样的网页协同办公软件。

总结:

  • TCP 协议本身是全双工的,但我们最常用的 HTTP/1.1,虽然是基于 TCP 的协议,但它是半双工的,对于大部分需要服务器主动推送数据到客户端的场景,都不太友好,因此我们需要使用支持全双工的 WebSocket 协议。
  • 在 HTTP/1.1 里,只要客户端不问,服务端就不答。基于这样的特点,对于登录页面这样的简单场景,可以使用定时轮询或者长轮询的方式实现服务器推送(comet)的效果。
  • 对于客户端和服务端之间需要频繁交互的复杂场景,比如网页游戏,都可以考虑使用 WebSocket 协议。
  • WebSocket 和 socket 几乎没有任何关系,只是叫法相似。
  • 正因为各个浏览器都支持 HTTP 协 议,所以 WebSocket 会先利用 HTTP 协议加上一些特殊的 header 头进行握手升级操作,升级成功后就跟 HTTP 没有任何关系了,之后就用 WebSocket 的数据格式进行收发数据。

❤️❤️❤️本人水平有限,如有纰漏,欢迎各位大佬评论批评指正!😄😄😄

💘💘💘如果觉得这篇文对你有帮助的话,也请给个点赞、收藏下吧,非常感谢!👍 👍 👍

🔥🔥🔥Stay Hungry Stay Foolish 道阻且长,行则将至,让我们一起加油吧!🌙🌙🌙

img

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Kwan的解忧杂货铺@新空间代码工作室

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值