面试题答案
一键面试1. 关键数据结构
- TCP 控制块(TCB, Transmission Control Block):
- 每个 TCP 连接在内核中都有一个对应的 TCB,它存储了该连接的所有状态信息,比如源 IP 地址、目的 IP 地址、源端口号、目的端口号、发送和接收的序列号、窗口大小等。在 Linux 内核中,
struct sock
结构是与套接字相关的通用数据结构,struct tcp_sock
是基于struct sock
专门为 TCP 协议扩展的结构,包含了 TCP 连接特有的信息,例如tcb->snd_nxt
表示下一个要发送的序列号,tcb->rcv_nxt
表示期望接收的下一个序列号。
- 每个 TCP 连接在内核中都有一个对应的 TCB,它存储了该连接的所有状态信息,比如源 IP 地址、目的 IP 地址、源端口号、目的端口号、发送和接收的序列号、窗口大小等。在 Linux 内核中,
- TCP 头部(TCP Header):
- 是 TCP 数据包的重要组成部分,长度通常为 20 字节(不包含选项字段)。包含源端口号(16 位)、目的端口号(16 位)、序列号(32 位)、确认号(32 位)、数据偏移(4 位,指示 TCP 头部长度,以 4 字节为单位)、保留位(6 位)、标志位(6 位,如 SYN、ACK、FIN 等)、窗口大小(16 位)、校验和(16 位)、紧急指针(16 位,仅当 URG 标志位被置位时有效)。在网络传输过程中,TCP 头部携带了连接建立、数据传输和连接终止等关键信息。
2. 三次握手过程在底层的实现
- 第一次握手(客户端发送 SYN 包):
- 当应用层调用
socket()
函数创建一个 TCP 套接字,并调用connect()
函数发起连接请求时,内核开始处理。 - 内核在
tcp_v4_connect()
函数中,构建一个 SYN 包。首先填充 TCP 头部,设置源端口号为客户端套接字绑定的端口号,目的端口号为服务器监听的端口号。生成一个初始序列号(ISN,Initial Sequence Number),将 SYN 标志位置 1。 - 然后通过 IP 层发送该数据包,IP 层会根据目的 IP 地址查找路由表,确定下一跳地址,并封装 IP 头部,最终通过网络接口发送出去。
- 当应用层调用
- 第二次握手(服务器接收 SYN 包并回复 SYN + ACK 包):
- 服务器内核在接收网络接口传来的数据包后,经过 IP 层解包,发现是 TCP 协议且目的端口号与监听端口号匹配,调用
tcp_v4_rcv()
函数处理。 - 函数检查 TCP 头部,确认 SYN 标志位为 1,生成自己的初始序列号,同时设置确认号为客户端的 ISN 加 1,表示已收到客户端的 SYN 包。然后设置 SYN 和 ACK 标志位,构建 SYN + ACK 包。
- 同样通过 IP 层封装并发送回客户端。
- 服务器内核在接收网络接口传来的数据包后,经过 IP 层解包,发现是 TCP 协议且目的端口号与监听端口号匹配,调用
- 第三次握手(客户端接收 SYN + ACK 包并回复 ACK 包):
- 客户端内核收到服务器的 SYN + ACK 包后,调用
tcp_v4_rcv()
函数处理。检查 TCP 头部,确认 SYN 和 ACK 标志位正确,并且确认号与自己发送的 ISN 加 1 相符。 - 然后客户端发送 ACK 包,设置确认号为服务器的 ISN 加 1,ACK 标志位置 1。通过 IP 层发送该 ACK 包给服务器。
- 服务器收到 ACK 包后,连接正式建立,双方可以开始进行数据传输。
- 客户端内核收到服务器的 SYN + ACK 包后,调用
3. 函数调用过程总结
- 客户端:
socket()
创建套接字 ->connect()
发起连接 ->tcp_v4_connect()
构建并发送 SYN 包 ->tcp_v4_rcv()
处理服务器的 SYN + ACK 包 -> 构建并发送 ACK 包。
- 服务器:
socket()
创建套接字 ->bind()
绑定地址和端口 ->listen()
监听连接 ->tcp_v4_rcv()
处理客户端的 SYN 包 -> 构建并发送 SYN + ACK 包 ->tcp_v4_rcv()
处理客户端的 ACK 包,连接建立。