Linux C/C++ 学习日记(16):TCP网络编程API的介绍(二):tcb的建立过程、元数据的介绍

注:该文用于个人学习记录和知识交流,如有不足,欢迎指点。

tcb的创建流程

tcb的控制块对应的代码结构为struct tcp_sock ,当该结构体确认好各个参数时,我们就说tcb控制块建立了!

一、调用socket()时候

1.所有相关结构体(socketsockinet_socktcp_sock)的 内存都已分配(因为是嵌套结构,  一次性分配完整);

2.与 “套接字类型、协议族、通用操作” 相关的 核心参数已确定(如 socket->typesock->sk_familyinet_sock->inet_protocol);

3.但与 “具体地址 / 端口”(inet_sock 的地址端口)、“TCP 协议特有状态”(tcp_sock 的序列号、拥塞控制参数等)相关的参数,需要后续 bind()connect()listen() 等操作才能逐步确定。

4.简单说:“壳子(内存)在 socket() 时就有了,核心配置(通用参数)也定了,但细节(地址、TCP 特有状态)还得后续步骤填。”

二、struct socket、struct sock、struct inet_sock、struct tcp_sock四个结构体

1. 概念:

结构体作用范围核心功能
struct socket所有套接字的顶层抽象

-关联用户态文件描述符(file

-存储套接字类型(SOCK_STREAM等)

-状态(连接 / 未连接),通过sk指针绑定到struct sock,是用户态操作(如bind/connect)的入口。

struct sock全协议族通用核心

存储所有套接字的基础运行时状态:

-收发缓冲区队列、等待队列(用于epoll等)

-协议操作表(sk_prot),是内核网络栈操作的核心载体。

struct inet_sockIPv4 协议族专用

扩展 IPv4 特有属性:

-本地 / 对端 IP 地址(inet_rcv_saddr/inet_daddr

-端口(inet_sport/inet_dport

-TTL

-衔接通用sock与 IPv4 协议细节。

struct tcp_sockTCP 协议专用(基于 IPv4)

扩展 TCP 特有属性:

-序列号(snd_nxt/rcv_nxt

-拥塞窗口(cwnd

-连接状态(tcp_state

-重传定时器

-支撑 TCP 可靠传输(三次握手、重传、拥塞控制等)核心逻辑。

2. 四者的关系

2.1 层级逻辑

从用户态接口(socket)到内核通用核心(sock),再到 IPv4 协议(inet_sock),最终到 TCP 细节(tcp_sock),逐层细化;

2.2 设计目的

通过「通用 + 专用」分层,既保证跨协议族复用(如sock),又能承载具体协议的复杂逻辑(如tcp_sock的拥塞控制)。

2.3 实现方法:指针转换,利用首成员地址重合特性

因「首成员与结构体起始地址完全重合(偏移量 0)」,内核通过宏快速从通用结构定位专用结构:

struct socket 与 struct sock:通过 socket->sk 直接关联(sock 是 socket 的成员指针)。

inet_sk(sk):从 struct sock* sk 转换为 struct inet_sock*(通过sock的首成员sk定位);

tcp_sk(sk):从 struct sock* sk 先转inet_sock*,再通过inet_sock的首成员inet定位 struct tcp_sock*

2.4 底层结构体代码
// 1. 顶层抽象:struct socket(用户态与内核态的桥梁)
struct socket {
    socket_state state;        // 套接字状态(如未连接、已连接、监听等)
    unsigned short type;       // 套接字类型(SOCK_STREAM/TCP、SOCK_DGRAM/UDP等)
    const struct proto_ops *ops; // 协议操作表(绑定/连接/发送等底层实现)
    struct sock *sk;           // 关键指针:指向内核核心套接字结构(struct sock)
    struct file *file;         // 关联的文件结构体(通过文件描述符暴露给用户态)
};


// 2. 通用核心:struct sock(所有协议族的基础逻辑体)
struct sock {
    struct sock_common __sk_common; // 通用属性(协议族、地址/端口占位等)
    struct sk_buff_head sk_receive_queue; // 接收缓冲区队列
    struct sk_buff_head sk_write_queue;   // 发送缓冲区队列
    struct wait_queue_head_t *sk_sleep;   // 等待队列(用于I/O复用)
    const struct proto *sk_prot;          // 传输层协议操作表(如TCP/UDP的底层逻辑)
    // 其他通用状态字段...
};


// 3. IPv4协议族专用:struct inet_sock(扩展IPv4特有属性)
struct inet_sock {
    struct sock sk;            // 第一个成员:嵌入通用sock结构(地址偏移为0)
    __be32 inet_rcv_saddr;     // 本地IPv4地址(网络字节序)
    __be32 inet_daddr;         // 对端IPv4地址(网络字节序)
    __be16 inet_sport;         // 本地端口(网络字节序)
    __be16 inet_dport;         // 对端端口(网络字节序)
    int inet_ttl;              // IPv4报文TTL(生存时间)
    // 其他IPv4专属字段...
};

// 指针转换宏:从struct sock*获取对应的struct inet_sock*
// 原理:利用sk是inet_sock的首成员,地址完全重合
#define inet_sk(sk) container_of(sk, struct inet_sock, sk)


// 4. TCP协议专用:struct tcp_sock(扩展TCP特有属性)
struct tcp_sock {
    struct inet_sock inet;     // 第一个成员:嵌入IPv4的inet_sock结构(地址偏移为0)
    u32 snd_nxt;               // 下一个待发送的序列号
    u32 rcv_nxt;               // 下一个期望接收的序列号
    u32 cwnd;                  // 拥塞窗口(拥塞控制核心)
    u32 ssthresh;              // 慢启动阈值
    enum tcp_states tcp_state; // TCP连接状态(如ESTABLISHED、SYN_SENT等)
    // 其他TCP专属字段(重传定时器、窗口机制等)...
};

// 指针转换宏:从struct sock*获取对应的struct tcp_sock*
// 原理:先通过inet_sk(sk)拿到inet_sock*,再利用inet是tcp_sock的首成员,地址完全重合
#define tcp_sk(sk) container_of(inet_sk(sk), struct tcp_sock, inet)
 2.5 底层内核操控的实现代码
struct socket *sockt;  // 假设已获取socket结构体指针
struct sock *sk = sockt->sk;  // 直接通过sk指针访问struct sock
// 访问sock的参数:如接收队列长度
unsigned int rcv_len = sk->sk_receive_queue.qlen;


struct inet_sock *inet = inet_sk(sk);  // 从sock*转换为inet_sock*
// 访问inet_sock的参数:如本地IP地址、端口
__be32 local_ip = inet->inet_rcv_saddr;
__be16 local_port = inet->inet_sport;



struct tcp_sock *tcp = tcp_sk(sk);  // 从sock*转换为tcp_sock*
// 访问tcp_sock的参数:如发送序列号、拥塞窗口
u32 next_seq = tcp->snd_nxt;
u32 cwnd = tcp->cwnd;

三、tcb建立的流程

1.服务端和客户端的tcb建立时机

主体 / 场景操作 / 阶段结构体创建情况说明
服务端 - 监听套接字

掉用

 socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) ,

立即创建完整组合:struct sock → 

struct inet_sock → 

struct tcp_sock

用于后续接收客户端 SYN 包、管理监听队列(半连接 / 全连接队列),是服务端接收连接的基础结构。注意这个不是tcb没有建立,因为tcp_sock的参数填充不完整。
服务端 - 新客户端连接第一次握手(收到客户端 SYN)仅生成临时结构 struct request_sock(半连接),不创建 sock/inet_sock/tcp_sock

存储 SYN 包关键信息(序列号、MSS 等);

因连接未完全建立,用轻量临时结构节省内存。

服务端 - 新客户端连接第三次握手(收到客户端 ACK)

为新连接创建完整组合:

struct sock → 

struct inet_sock → 

struct tcp_sock

(注意这里还没有创建socket结构体,等用户调用accept的时候才创建)

从 request_sock 继承 IP、端口、序列号等信息,用于后续数据传输,标志连接完全建立。

即tcb_sock参数配置完毕,tcb控制块建立!

客户端connect() 时

基本配置好各结构体的参数(服务端的seq还没确立),tcb控制块建立!

用于发起处理三次握手交互及后续数据传输,是客户端通信的核心结构。

2.与syn、accept队列的关联

队列类型存储的核心数据结构(Linux 内核)对应连接阶段关键信息
syn_queue

struct request_sock*

(半连接请求控制块)

三次握手第一阶段(服务端收到 SYN,未完成握手)

- 客户端 IP、端口;

- 服务端初始序列号(ISN);

- 连接状态(SYN_RCVD);

- 临时 TCP 选项(如窗口大小)。

accept_queue

struct sock*

(4个结构体已经初始化并分配完整参数)

三次握手完成后(服务端收到 ACK,连接建立),

- 完整五元组(双方 IP、端口、协议);

- 连接状态(ESTABLISHED);

- 收发缓冲区、拥塞控制参数、确认号 / 序列号;

- 与套接字文件描述符(fd)的关联。

补充:

struct inet_request_sock是struct request_sock在 IPv4 协议下的专用扩展,类似 inet_sock 对 sock 的扩展(注意:没有tcp_requeset_sock这个结构体,inet_request_sock足够记载半连接的信息了)

struct inet_request_sock {
    struct request_sock  req;  // 第一个成员:嵌入通用 request_sock(地址偏移 0)
    __be32               ir_loc_addr;  // 本地 IPv4 地址(网络字节序)
    __be32               ir_rmt_addr;  // 客户端 IPv4 地址(网络字节序)
    __be16               ir_loc_port;  // 本地端口(网络字节序)
    __be16               ir_rmt_port;  // 客户端端口(网络字节序)
    u32                  ir_seq;       // 服务器回复的 SYN+ACK 序列号
    u32                  ir_ack;       // 服务器期望客户端回复的 ACK 序列号(ir_seq + 1)
    // 其他 IPv4/TCP 握手专用字段...
};

四、细节补充:

1. 为什么accept_queue中存储的是sock?

答:

struct sock 是连接的核心标识:它包含了套接字的通用运行时状态(如连接状态、收发缓冲区、等待队列等),是内核管理连接的 “最小必要单元”。无论具体协议(如 TCP 基于 IPv4)如何,struct sock 都能统一表示一个连接的核心信息。

accept_queue 作为通用队列,只需存储最基础的 struct sock*

当需要访问协议专用属性时,内核可通过 inet_sk()tcp_sk() 等宏从 struct sock* 转换得到对应的专用结构体指针(如从队列中取出 sock* 后,用 tcp_sk(sock) 即可访问 TCP 特有参数)。

2. struct socket第三次握手时为什么没有创建?

答:

struct socket 是用户态与内核态的桥梁(关联文件描述符),但它并非连接的核心状态载体。当应用程序调用 accept() 时,内核会为队列中的 struct sock 动态创建对应的 struct socket 并关联,再返回给用户态。因此,accept_queue 中无需提前存储 struct socket

3. 元数据是什么?

3.1 定义

元数据(Metadata)的核心定义是:描述 “数据” 的数据(Data about Data)。它不直接承载业务层面的实际内容(如文件中的文本、图片像素、网络传输的文件内容),而是用于描述 “实际数据” 的属性、上下文、状态、管理规则等信息,目的是让 “实际数据” 能够被高效识别、定位、理解、使用和管理。

3.2 元数据的核心特征与作用
  1. 非载荷性:元数据本身不是用户需要的 “实际数据”(如你通过 TCP 发送的文件内容是 “数据载荷”,而描述这个 TCP 连接的 IP 地址、端口号是 “元数据”)。
  2. 描述性:聚焦于 “数据的属性”,比如数据的类型、大小、来源、创建时间、关联对象、状态等。
  3. 功能性:为数据的管理和交互提供支撑 —— 没有元数据,“实际数据” 会变成无法识别、无法操作的 “裸数据”(例如,没有文件名、文件大小的元数据,你无法在电脑上找到并打开一个文件;没有 IP 地址、端口的元数据,网络数据包无法送达目标主机)

3.3 不同场景下的元数据示例

元数据的具体形式随场景变化,以下是常见场景的例子,帮助你理解其通用性:

应用场景实际数据(Data)元数据(Metadata)示例
电脑文件系统文档内容、图片像素、视频帧文件名、文件大小、创建时间、修改时间、存储路径、文件格式(.txt/.jpg)
数据库表中的一行行业务记录(如用户信息)表名、字段类型(int/varchar)、主键、索引、数据创建时间、存储引擎
网络通信(TCP)传输的文件内容、API 请求体源 IP 地址、目标 IP 地址、源端口、目标端口、TCP 连接状态(ESTABLISHED)、序列号、数据包长度
图片文件图片的像素矩阵图片分辨率(1920×1080)、色彩模式(RGB)、压缩格式(JPEG/PNG)、拍摄设备型号

4. 网络连接的元数据

元数据类别具体元数据描述对应内核结构体核心作用
基础标识类套接字类型(如 SOCK_STREAM/TCP)struct socket定义连接的传输模式(面向连接 / 无连接)
协议族(如 AF_INET/IPv4)struct sock确定网络层协议版本,适配地址格式
传输层协议(如 IPPROTO_TCP)struct inet_sock标识连接使用的传输层协议(TCP/UDP 等)
地址端口类本地 IPv4 地址struct inet_sock标识本地通信端点的网络层地址
对端 IPv4 地址struct inet_sock标识对端通信端点的网络层地址
本地端口号(网络字节序)struct inet_sock标识本地进程的传输层端口
对端端口号(网络字节序)struct inet_sock标识对端进程的传输层端口
状态控制类套接字整体状态(如已连接 / 未连接)struct socket反映套接字的全局状态(用户态可感知)
TCP 连接状态(如 ESTABLISHED/SYN_SENT)struct tcp_sock维护 TCP 协议特有的连接阶段(三次握手 / 四次挥手)
TCP 发送序列号(snd_nxt)struct tcp_sock确保 TCP 数据传输的有序性和可靠性
TCP 接收序列号(rcv_nxt)struct tcp_sock标记下一个待接收的 TCP 数据序列,避免重复
资源配置类接收缓冲区队列(大小 / 长度)struct sock暂存待用户读取的网络数据,协调 I/O 速度
发送缓冲区队列(大小 / 长度)struct sock暂存待发送的网络数据,适配协议栈处理速度
TCP 拥塞窗口(cwnd)struct tcp_sock控制 TCP 发送速率,避免网络拥塞
TCP 慢启动阈值(ssthresh)struct tcp_sock切换 TCP 拥塞控制策略(慢启动 / 拥塞避免)
IPv4 报文 TTL(生存时间)struct inet_sock限制 IP 报文在网络中的转发次数,防止环路
TCP 重传超时时间struct tcp_sock触发 TCP 数据重传,保障传输可靠性

当用户调用socket的时候,实际上可以理解为给元数据的载体分配了内存。后续经过用户的操作和TCP的三次握手逐渐将元数据确定完整,当元数据确定完整之后,我们就说tcb控制块建立了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值