查缺补漏(二)

136 阅读12分钟

我又来啦,不知不觉查缺补漏部分已经更新到第二个了,加油加油,查缺补漏!

TCP

四层模型:

  1. 应用层
  2. 传输层
  3. 网络层
  4. 网络接口层

参考文章:小林coding:TCP 和 参考文章:序列号和确认号
这是一个大章节,我会逐渐补充

序列号和确认号

  • 公式一:序列号 = 上一次发送的序列号 + len(数据长度)。
    特殊情况,如果上一次发送的报文是 SYN 报文或者 FIN 报文,则改为上一次发送的序列号 + 1。
  • 公式二:确认号 = 上一次收到的报文中的序列号 + len(数据长度)。
    特殊情况,如果收到的是 SYN 报文或者 FIN 报文,则改为上一次收到的报文中的序列号 + 1。

重点三个字段的作用:

  • 序列号:在建立连接时由内核生成的随机数作为其初始值,通过 SYN 报文传给接收端主机,每发送一次数据,就「累加」一次该「数据字节数」的大小。用来解决网络包乱序问题。
  • 确认号:指下一次「期望」收到的数据的序列号,发送端收到接收方发来的 ACK 确认报文以后,就可以认为在这个序号以前的数据都已经被正常接收。用来解决丢包的问题。
  • 控制位:用来标识 TCP 报文是什么类型的报文,比如是 SYN 报文、数据报文、ACK 报文,FIN 报文等。

TCP 重传

类型:

  1. 超时重传
  2. 快速重传
  3. SACK 方法
  4. Duplicate SACK

RTT(Round-Trip Time 往返时延)
TCP 实现可靠传输的方式之一,是通过序列号与确认应答。
在 TCP 中,当发送端的数据到达接收主机时,接收端主机会返回一个确认应答消息,表示已收到消息。
超时重传
超时重传时间是以 RTO (Retransmission Timeout 超时重传时间)表示。

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

超时重传时间 RTO 的值应该略大于报文往返 RTT 的值

实际上「报文往返 RTT 的值」是经常变化的,因为我们的网络也是时常变化的。也就因为「报文往返 RTT 的值」 是经常波动变化的,所以「超时重传时间 RTO 的值」应该是一个动态变化的值。

我们来看看 Linux 是如何计算 RTO 的呢?

估计往返时间,通常需要采样以下两个:

  • 需要 TCP 通过采样 RTT 的时间,然后进行加权平均,算出一个平滑 RTT 的值,而且这个值还是要不断变化的,因为网络状况不断地变化。
  • 除了采样 RTT,还要采样 RTT 的波动范围,这样就避免如果 RTT 有一个大的波动的话,很难被发现的情况。

如果超时重发的数据,再次超时的时候,又需要重传的时候,TCP 的策略是超时间隔加倍

也就是每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍。两次超时,就说明网络环境差,不宜频繁反复发送。

超时触发重传存在的问题是,超时周期可能相对较长。那是不是可以有更快的方式呢?

于是就可以用**「快速重传」**机制来解决超时重发的时间等待。

快速重传

TCP 还有另外一种快速重传(Fast Retransmit)机制,它不以时间为驱动,而是以数据驱动重传。

  • 三次同样的ACK,会触发快速重传
  • 但是面临的问题:比如发了六个包,第一个和后三个到了,2和3丢了,客户端确实收到了三个ACK2,但是如何确定是只重传2还是2及以后都重传呢?
  • 于是,出现了SACK 方法来解决这个问题

SACK 方法
还有一种实现重传机制的方式叫:SACK( Selective Acknowledgment), 选择性确认

这种方式需要在 TCP 头部「选项」字段里加一个 SACK 的东西,它可以将已收到的数据的信息发送给「发送方」,这样发送方就可以知道哪些数据收到了,哪些数据没收到,知道了这些信息,就可以只重传丢失的数据。
如果要支持 SACK,必须双方都要支持。在 Linux 下,可以通过 net.ipv4.tcp_sack 参数打开这个功能(Linux 2.4 后默认打开

Duplicate SACK
Duplicate SACK 又称 D-SACK,其主要使用了 SACK 来告诉「发送方」有哪些数据被重复接收了。

D-SACK 有这么几个好处

  1. 可以让「发送方」知道,是发出去的包丢了,还是接收方回应的 ACK 包丢了;
    可以知道是不是「发送方」的数据包被网络延迟了;
  2. 可以知道网络中是不是把「发送方」的数据包给复制了;
  3. 在 Linux 下可以通过 net.ipv4.tcp_dsack 参数开启/关闭这个功能(Linux 2.4 后默认打开)。

滑动窗口

窗口大小就是指无需等待确认应答,而可以继续发送数据的最大值
TCP 头里有一个字段叫 Window,也就是窗口大小。

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

接收窗口和发送窗口的大小是相等的吗?

并不是完全相等,接收窗口的大小是约等于发送窗口的大小的。

因为滑动窗口并不是一成不变的。比如,当接收方的应用进程读取数据的速度非常快的话,这样的话接收窗口可以很快的就空缺出来。那么新的接收窗口大小,是通过 TCP 报文中的 Windows 字段来告诉发送方。那么这个传输过程是存在时延的,所以接收窗口和发送窗口是约等于的关系。

流量控制

TCP 提供一种机制可以让「发送方」根据「接收方」的实际接收能力控制发送的数据量,这就是所谓的流量控制。

操作系统缓冲区和窗口大小的影响?
当服务端系统资源非常紧张的时候,操作系统可能会直接减少了接收缓冲区大小,这时应用程序又无法及时读取缓存数据,那么这时候就有严重的事情发生了,会出现数据包丢失的现象。

TCP 为每个连接设有一个持续定时器,只要 TCP 连接一方收到对方的零窗口通知,就启动持续计时器。

如果持续计时器超时,就会发送窗口探测 ( Window probe )  报文,而对方在确认这个探测报文时,给出自己现在的接收窗口大小。

怎么让接收方不通告小窗口呢?
接收方通常的策略如下:

当「窗口大小」小于 min( MSS,缓存空间/2 ) ,也就是小于 MSS 与 1/2 缓存大小中的最小值时,就会向发送方通告窗口为 0,也就阻止了发送方再发数据过来。

等到接收方处理了一些数据后,窗口大小 >= MSS,或者接收方缓存空间有一半可以使用,就可以把窗口打开让发送方发送数据过来。

怎么让发送方避免发送小数据呢?
发送方通常的策略如下:

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

条件一:要等到窗口大小 >= MSS 并且 数据大小 >= MSS;
条件二:收到之前发送数据的 ack 回包;
只要上面两个条件都不满足,发送方一直在囤积数据,直到满足上面的发送条件。

拥塞控制

拥塞控制,控制的目的就是避免「发送方」的数据填满整个网络

拥塞窗口 cwnd发送方维护的一个的状态变量,它会根据网络的拥塞程度动态变化的。

我们在前面提到过发送窗口 swnd 和接收窗口 rwnd 是约等于的关系,那么由于加入了拥塞窗口的概念后,此时发送窗口的值是swnd = min(cwnd, rwnd),也就是拥塞窗口和接收窗口中的最小值。

拥塞窗口 cwnd 变化的规则

  • 只要网络中没有出现拥塞,cwnd 就会增大;
  • 但网络中出现了拥塞,cwnd 就减少;

四个相关算法:

  • 慢启动: 当发送方每收到一个 ACK,拥塞窗口 cwnd 的大小就会加 1。
    有一个叫慢启动门限 ssthresh (slow start threshold)状态变量。
    当 cwnd < ssthresh 时,使用慢启动算法。
    当 cwnd >= ssthresh 时,就会使用「拥塞避免算法」。
  • 拥塞避免算法: 每当收到一个 ACK 时,cwnd 增加 1/cwnd。
  • 拥塞发生算法: 当发生了「超时重传」,则就会使用拥塞发生算法。
    这个时候,ssthresh 和 cwnd 的值会发生变化:
    ssthresh 设为 cwnd/2,
    cwnd 重置为 1 (是恢复为 cwnd 初始化值,我这里假定 cwnd 初始化值 1)
  • 快速恢复算法: 快速重传和快速恢复算法一般同时使用,快速恢复算法是认为,你还能收到 3 个重复 ACK 说明网络也不那么糟糕,所以没有必要像 RTO 超时那么强烈。

IP

参考资料
MAC(数据链路层)  的作用则是实现「直连」的两个设备之间通信,而 IP(网络层)  则负责在「没有直连」的两个网络之间进行通信传输。
IP地址由P协议负责传递(不在TCP报文首部里)

Linux 的内核态和用户态的区别

参考文章

有这两者的原因?对不同的操作给予不同的“ 权限 ”,防止用户进程误操作或者是恶意破坏系统

内核态:

  • 也叫内核空间,是内核进程/线程所在的区域。主要负责运行系统、硬件交互。
  • 运行的代码不受任何限制,CPU可以执行任何指令。

用户态:

  • 也叫用户空间,是用户进程/线程所在的区域。主要用于执行用户程序
  • 运行的代码需要受到CPU的很多检查,不能直接访问内核数据和程序,也就是说不可以像内核态线程一样访问任何有效地址。

用户态切换到内核态的方式

  1. 系统调用(主动)
    由于用户态无法完成某些任务,用户态会请求切换到内核态,内核态通过为用户专门开放的中断完成切换。
  2. 异常(被动)
    在执行用户程序时出现某些不可知的异常,会从用户程序切换到内核中处理该异常的程序,也就是切换到了内核态。
  3. 外围设备中断(被动)
    外围设备发出中断信号,当中断发生后,当前运行的进程暂停运行,并由操作系统内核对中断进程处理,如果中断之前CPU执行的是用户态程序,就相当于从用户态向内核态的切换。

git 一些命令的查缺补漏

git commit --amend :有时你提交过代码之后,发现一个地方改错了,你下次提交时不想保留上一次的记录;或者你上一次的commit message的描述有误,这时候你可以使用这个命令(想复用上一次commit的message也可以用这个)

wget和yum的区别

参考资料

  1. wget
  • 类似于迅雷,是一种下载工具
  • 通过HTTP、HTTPS、FTP三个最常见的TCP/IP协议下载,并可以使用HTTP代理
  • 名字是World Wide Web”与“get”的结合。
  1. yum:
  • 是redhat, centos 系统下的软件安装方式,基于Linux,
  • 全称为 Yellow dog Updater, Modified,
  • 是一个在Fedora和RedHat以及CentOS中的Shell前端软件包管理器
  • 基于RPM包管理,能够从指定的服务器自动下载RPM包并且安装,可以自动处理依赖性关系,并且一次安装所有依赖的软件包。

Vite 分包

参考文章:Vite性能优化之分包策略 和 Vue Router懒加载

分包策略就是把一些不会经常更新的文件,进行单独打包处理。
vite中实现分包策略,实际是靠配置rollup的打包配置完成的

// vite.config.js
export default defineConfig({
  build: {
    rollupOptions: {
      // https://rollupjs.org/guide/en/#outputmanualchunks
      output: {
        manualChunks: {
          'group-user': [
            './src/UserDetails',
            './src/UserDashboard',
            './src/UserProfileEdit',
          ],
        },
      },
    },
  },
})

BFC

BFC的触发条件(父元素设置以下属性中的一个,可以清除子元素浮动导致的高度塌陷)

  • float的值不为none
  • overflow的值不为visible
  • display的值为inline-block、table-cell、table-caption
  • position的值为absolute或fixed

TS 拓展window

declare global {
  interface Window {
     name: string;
  }
}

否则会Property ‘name’ does not exist on type 'Window & typeof globalThis'.ts(2339)

Why Not iframe

Why Not iframe-语雀

  1. url 不同步。浏览器刷新 iframe url 状态丢失、后退前进按钮无法使用。
  2. UI 不同步,DOM 结构不共享。想象一下屏幕右下角 1/4 的 iframe 里来一个带遮罩层的弹框,同时我们要求这个弹框要浏览器居中显示,还要浏览器 resize 时自动居中..
  3. 全局上下文完全隔离,内存变量不共享。iframe 内外系统的通信、数据同步等需求,主应用的 cookie 要透传到根域名都不同的子应用中实现免登效果。
  4. 慢。每次子应用进入都是一次浏览器上下文重建、资源重新加载的过程。