1. TCP/IP 四层模型

FILE TAGS

Network

应用层

协议:

HTTP

DNS

image.png

image.png

操作系统协议栈 (包含传输层和网络层)

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

image.png

image.png

image.png

START

Basic

Linux 内核是怎么使用中断来处理网络包的

Back:

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

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

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

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

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

内核中的 ksoftirqd 线程专门负责软中断的处理,当 ksoftirqd 内核线程收到软中断后,就会来轮询处理数据。

ksoftirqd 线程会从 Ring Buffer 中获取一个数据帧,用 sk_buff 表示,从而可以作为一个网络包交给网络协议栈进行逐层处理。

发送数据包的过程发生了几次拷贝?

第一次,调用发送数据的系统调用的时候,内核会申请一个内核态的 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。

END

传输层

START

Basic

TCP 包头格式

Back:

image.png

序号:这个是为了解决包乱序的问题。

确认号:解决丢包的问题

状态位:例如 SYN 是发起一个连接,ACK 是回复,RST 是重新连接,FIN 是结束连接等。TCP 是面向连接的,因而双方要维护连接的状态,这些带状态位的包的发送,会引起双方的状态变更。

窗口大小:流量控制和拥塞控制

END

START

Basic

TCP 三次握手四次挥手

Back:

image.png

END

START

Basic

TCP 数据分割

Back:

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

END

网络层(IP 协议)

image.png

当存在多个网卡时,在填写源地址 IP 时,就需要判断到底应该填写哪个地址。这个判断相当于在多块网卡中判断应该使用哪个一块网卡来发送包。

这个时候就需要根据路由表规则,来判断哪一个网卡作为源地址 IP。

image.png

第三条目比较特殊,它目标地址和子网掩码都是 0.0.0.0,这表示默认网关,如果其他所有条目都无法匹配,就会自动匹配这一行。并且后续就把包发给路由器,Gateway 即是路由器的 IP 地址。

START

Basic

网络层(IP 层)协议

Back:

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

END

网络接口层(MAC)

image.png

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

ARP 协议会在以太网中以广播的形式,对以太网所有的设备喊出:“这个 IP 地址是谁的?请把你的 MAC 地址告诉我”。

网络设备

网卡

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

image.png

START

Basic

交换机是如何工作的

Back:

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

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

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

交换机根据 MAC 地址表查找 MAC 地址,然后将信号发送到相应的端口。仅此而已

如果地址表中没有,就会向除了源端口外的其他端口广播

END

START

Basic

路由器是如何工作的

Back:

路由器是基于 IP 设计的,俗称三层网络设备,路由器的各个端口都具有 MAC 地址和 IP 地址;

路由器的端口具有 MAC 地址,因此它就能够成为以太网的发送方和接收方;同时还具有 IP 地址,从这个意义上来说,它和计算机的网卡是一样的。

完成包接收操作之后,路由器就会去掉包开头的 MAC 头部。(路由器只会接受目标 MAC 为自己的 MAC 的包)

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

接下来,路由器会根据 MAC 头部后方的 IP 头部中的内容进行包的转发操作。

转发操作分为几个阶段,首先是查询路由表判断转发目标。

首先,我们需要根据路由表的网关列判断对方的地址。

END

RPC

START

Basic

HTTP 和 RPC 有什么区别

Back:

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

END

START

Basic

TCP 是什么,有什么特征

Back:

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

建立一个 TCP 连接是需要客户端与服务端达成三个信息的共识:

END

START

Basic

TCP 和 UDP 的区别

Back:

1. 连接

2. 服务对象

3. 可靠性

4. 拥塞控制、流量控制

5. 首部开销

6. 传输方式

7. 分片不同

TCP 和 UDP 应用场景:

由于 TCP 是面向连接,能保证数据的可靠性交付,因此经常用于:

由于 UDP 面向无连接,它可以随时发送数据,再加上 UDP 本身的处理既简单又高效,因此经常用于:

END

TCP 三次握手四次挥手

START

Basic

TCP 三次握手

Back:

image.png

  1. 客户端会随机初始化序号(client_isn),将此序号置于 TCP 首部的「序号」字段中,同时把 SYN 标志位置为 1,表示 SYN 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT 状态。
  2. 服务端收到客户端的 SYN 报文后,首先服务端也随机初始化自己的序号(server_isn),将此序号填入 TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填入 client_isn + 1, 接着把 SYN 和 ACK 标志位置为 1。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD 状态。
  3. 客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部 ACK 标志位置为 1 ,其次「确认应答号」字段填入 server_isn + 1 ,最后把报文发送给服务端,这次报文可以携带客户到服务端的数据,之后客户端处于 ESTABLISHED 状态。

为什么需要三次握手,而不是两次、四次

为什么每次建立 TCP 连接时,初始化的序列号都要求不一样呢?

主要原因有两个方面:

TCP 三次握手优化

image.png

END

START

Basic

TCP 四次挥手

Back:

image.png

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

主动关闭连接的,才有 TIME_WAIT 状态。

为什么需要 TIME_WAIT 状态?

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

这个时间足以让两个方向上的数据包都被丢弃,使得原来连接的数据包在网络中都自然消失,再出现的数据包一定都是新建立连接所产生的。

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

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

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

TCP 四次挥手优化

image.png

END

START

Basic

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

Back:

当如果一个 IP 分片丢失,整个 IP 报文的所有分片都得重传。因为 IP 层无法获得完整的 TCP 头部,就不知道这是个 TCP 包,从而交给 TCP 负责高效的超时重传

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

END

START

Basic

TCP 的重传机制有哪些

Back:

超时重传

Linux 系统估计 RTO,通常需要采样以下两个:

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

快速重传

image.png

快速重传的工作方式是当收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文段。

SACK(选择性确认) 方法

这种方式需要在 TCP 头部「选项」字段里加一个 SACK 的东西,它可以将已收到的数据的信息发送给「发送方」,这样发送方就可以知道哪些数据收到了,哪些数据没收到,知道了这些信息,就可以只重传丢失的数据

image.png

D-SACk

image.png

D-SACK 有这么几个好处:

  1. 可以让「发送方」知道,是发出去的包丢了,还是接收方回应的 ACK 包丢了;
  2. 可以知道是不是「发送方」的数据包被网络延迟了;
  3. 可以知道网络中是不是把「发送方」的数据包给复制了;

END

START

Basic

什么是TCP 滑动窗口

Back:

滑动窗口的本质是维护异步队列,增加消息发送和接收的效率

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

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

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

image.png

只要发送方收到了 ACK 700 确认应答,就意味着 700 之前的所有数据「接收方」都收到了。这个模式就叫累计确认或者累计应答

发送方的滑动窗口

image.png

接收方的滑动窗口

image.png

END

START

Basic

TCP 是怎么实现流量控制的

Back:

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

所以“滑动窗口”的首要作用是异步,而不是控制流量,但是也同时可以做到流量控制

TCP 的流量控制是由操作系统缓冲区完成的

窗口关闭

TCP 通过让接收方指明希望从发送方接收的数据大小(窗口大小)来进行流量控制。

如果窗口大小为 0 时,就会阻止发送方给接收方传递数据,直到窗口变为非 0 为止,这就是窗口关闭。

如果窗口非 0 的协商包丢失了,那么发送和接收双方会进入死锁状态

TCP 解决这个问题的办法是在收到窗口关闭协商的时候开启一个计时器,如果计时器超时则向接收方发送一个探测报文,收到探测报文的一方要回复自己现在的接收窗口大小

糊涂窗口综合症

如果窗口大小只有几个字节,那么为了发送这个几个字节的 TCP 包而要附带上 40 个字节的 TCP+IP 头,显然是非常不经济的

要解决糊涂窗口综合症,就要同时解决上面两个问题就可以了:

END

START

Basic

TCP 拥塞控制是什么

Back:

流量控制的作用是防止【发送方】的数据填满【接收方】的缓存,而拥塞控制的作用是防止【发送方】的数据【填满整个网络】,因为网络是一个共享的环境

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

发送窗口的值是 swnd = min(cwnd, rwnd),也就是拥塞窗口和接收窗口中的最小值。

拥塞窗口 cwnd 变化的规则:

只要「发送方」没有在规定时间内接收到 ACK 应答报文,也就是发生了超时重传,就会认为网络出现了拥塞。

拥塞控制主要有以下四个机制

慢启动

当发送方每收到一个 ACK,拥塞窗口 cwnd 的大小就会加 1。

image.png

有一个叫慢启动门限  ssthresh (slow start threshold)状态变量。

拥塞避免算法

每当收到一个 ACK 时,cwnd 增加 1/cwnd。

image.png

拥塞发生

慢启动后进入拥塞避免之后,cwnd 会持续增长,当增长到一定值的时候,网络一定会出现丢包现象,从而触发重传,如果发生超时重传,则采用拥塞发生策略进行拥塞控制

image.png

如果发生快速重传,则 TCP 认为这个情况没有那么严重,因为包只丢了一部分,而不是丢了全部,于是会采用以下的策略

快速恢复

image.png

END

START

Basic

Socket 通信的流程是怎样的

Back:

image.png

END

Listen 时候参数 Backlog 的意义?

在早期 Linux 内核 backlog 是 SYN 队列大小,也就是未完成的队列大小。

在 Linux 内核 2.2 之后,backlog 变成 accept 队列,也就是已完成连接建立的队列长度,所以现在通常认为 backlog 是 accept 队列。

但是上限值是内核参数 somaxconn 的大小,也就说 accpet 队列长度 = min(backlog, somaxconn)。

START

Basic

没有 accept,能建立 TCP 连接吗?

Back:

答案:可以的

accpet 系统调用并不参与 TCP 三次握手过程,它只是负责从 TCP 全连接队列取出一个已经建立连接的 socket,用户层通过 accpet 系统调用拿到了已经建立连接的 socket,就可以对该 socket 进行读写操作了。

END

Q&A

START

Basic

如何确定最大传输速度以手动调整窗口大小(发送方缓冲区大小)

Back:

image.png

发送缓冲区与带宽时延积的关系:

所以,发送缓冲区的大小最好是往带宽时延积靠近。

END

START

Basic

有一个 IP 的服务端监听了一个端口,它的 TCP 的最大连接数是多少?

Back:

对 IPv 4,客户端的 IP 数最多为 2 的 32 次方,客户端的端口数最多为 2 的 16 次方,也就是服务端单机最大 TCP 连接数,约为 2 的 48 次方。

但是每个 TCP 连接是由一个文件描述符 fd 描述的,在 Linux 中能打开的 fd 是有限制的,并且每个连接是会占用一定量的内存的

END

START

Basic

TCP 和 UDP 可以使用同一个端口吗?

Back:

可以的

image.png

END

START

Basic

如何解决 TCP 的粘包问题

Back:

END