当前位置: 首页 > news >正文

从内核数据结构的角度理解socket

目录

一、socket核心:内核数据结构的三角关系

(1)struct socket:网络协议的抽象接口

(2)struct sock:传输层协议的控制中心

(3)struct file与files struct:文件系统的接入层

二、Socket调用全流程

(1)socket():创建核心结构,奠定基础协议

(2)bind():绑定地址,确定网络身份

(3)监听与连接:TCP 特有流程,依赖队列管理

1.listen():初始化连接队列,进入监听状态

2.connect():客户端发起连接,触发三次握手

3.accept():提取已完成连接,创建新套接字

(4)数据传输:read()/write() 与缓冲区管理

1.TCP 数据传输:可靠传输的缓冲区协作

2.UDP 数据传输:简单无状态的队列管理

(5)close():释放资源,终止连接


        在Linux网络编程中,socket是连接用户态和内核态的核心接口。从socket创建套接字到close关闭套接字,每一步都伴随着内核数据结构的创建、修改与销毁。本文将从内核数据结构的角度,详解socket的调用流程,重点对比TCP、UDP在数据结构上的设计差异,揭示网络是如何统一到文件系统中的。

        从数据结构视角看,Socket 的调用过程本质是内核通过 “三层结构”(files_structstruct filestruct socketstruct sock)实现的资源管理流程。TCP 因需可靠传输和连接管理,其 struct sock 设计复杂,包含大量状态和队列字段;UDP 作为无连接协议,结构精简,聚焦高效数据转发。

        这种 “统一框架(文件系统 + socket 抽象)+ 差异化实现(协议专属 struct sock)” 的设计,既保证了用户态接口的一致性(均通过 fd 操作),又满足了不同协议的特性需求,是 Linux 内核 “抽象复用” 与 “按需扩展” 思想的经典体现。理解这些数据结构的变化,能帮助我们更深入地掌握网络编程的本质,优化程序性能与可靠性。

一、socket核心:内核数据结构的三角关系

        Linux遵循一切皆文件的设计哲学,socket作为特殊的网络文件,其管理依赖三个核心数据结构的协作,这也是理解socket操作的基础。

(1)struct socket:网络协议的抽象接口

        struct socket是Socket的“管理层”结构,负责衔接用户态接口与内核协议逻辑。其中的ops字段指向协议专属的操作集(如TCP的tcp_prot_ops UDP 的udp_prot_ops),sk字段指向协议实现的核心数据结构struct sock。

        这个结构体也是最上层操作的,聚焦于对外(用户)暴露状态等。当调用如bind的时候,会从这个结构体中找到对应的操作集,然后调用其中的操作函数,从而修改struct sock的成员变量。

        struct socket中暴露给用户的状态较为简单,只有“未连接、已连接、正在连接、监听”等通用流程状态,目的是为了让用户知道该套接字处于流程的哪一步。而struct sock则更加复杂详细,在TCP中会有TCP_CLOSED\ESTABLISHED\SYN_SENT等11个状态;在UDP中由于其不是面向连接的协议,所以状态也会较为简单,通常只有“空闲、已绑定”等基础状态。

        至于这里的struct file指针,则是“一切皆文件”的体现,socket本质也是一种文件,所有每一个socket套接字会有一个fd文件描述符,而struct file中的private date也会指向该socket,是一种双向查找的关系。

(2)struct sock:传输层协议的控制中心

        struct sock是传输层协议的 “实干层” 结构,存储了协议运行的关键状态与资源。TCP 因需要可靠传输和连接管理,其 struct sock包含大量特有字段(如连接队列、滑动窗口);而 UDP 作为无连接协议,结构更精简,仅保留基础的地址和缓冲区信息。

        这里可以看到是利用联合体实现的多态。不过公共的字段(如发送缓冲区、接收缓冲区、IP地址、操作集等)。联合体的特点是他本身不知道自己是什么类型,谁填充这个字段,谁就负责管理他,所以这里有一个协议操作集,他们用于操作struct socket本身。体现一个层级架构。

        操作集在用户调用socket系统调用的时候,已经告诉了内核这个套接字应用什么协议SOCK_DGRAM、SOCK_STREAM,所以内核就会根据其填充他的操作集ops。

        如果未来要新增传输协议,比如替代TCP的某一部分,只需要定义自己的struct proto_ops,然后稍稍修改一下内核中socket创建时ops的指向即可。

(3)struct file与files struct:文件系统的接入层

        socket需要通过文件描述符fd被用户操作,这依赖文件系统的核心结构。

        struct file是 Socket 接入文件系统的 “适配器”,通过private_data关联struct socket;filese_struct则管理进程的所有fd,确保每个 Socket 对应唯一的fd索引。

        而在struct file中我们之前说过有个成员是f_ops,他是这个文件的操作集。不同类型的文件操作集指向的函数是不一样的。

二、Socket调用全流程

(1)socket():创建核心结构,奠定基础协议

        用户态调用

int fd = socket(AF_INET, SOCK_STREAM, 0); // 创建 TCP 套接字
// 或
int fd = socket(AF_INET, SOCK_DGRAM, 0);  // 创建 UDP 套接字

内核操作与结构变化

核心差异:TCP 的 struct sock 会预分配连接管理所需的内存(如 request_sock_queue),而 UDP 直接初始化缓冲区队列,结构更轻量。

(2)bind():绑定地址,确定网络身份

   用户态调用

struct sockaddr_in addr = {.sin_family = AF_INET,.sin_port = htons(8080),.sin_addr.s_addr = INADDR_ANY
};
bind(fd, (struct sockaddr*)&addr, sizeof(addr));

        这里有sockaddr_in和sockaddr两个结构体,他们体现了C语言版本的继承。无论是父类还是子类都是相同的大小,但是第一位成员永远是协议族,当访问到第一个成员之后,就可以按照该协议进行解析这个sockaddr类,从而获取有效信息。这也是必须要填入协议族的原因。

在sockaddr中是由2位协议类型+14位空白字节组成的。可以看做基类

而sockaddr_in则是对他进行重写,前两位仍然表示协议族,后面14个字节则划分成了端口号、IPV4地址、和8位空白字段。可以看做是子类。

除了他们,还有许多的“子类”。如sockaddr_in6、sockaddr_un、sockaddr_nl等,他们分别用于不同的场景。

内核操作与结构变化

核心差异:绑定操作对 TCP 和 UDP 逻辑相同,均是填充地址信息,无协议特有逻辑。

(3)监听与连接:TCP 特有流程,依赖队列管理

1.listen():初始化连接队列,进入监听状态

用户态调用

listen(fd, 10); // 最大已完成连接队列长度为 10

内核操作与结构变化

​​​​​​​

核心差异:UDP 无 listen() 操作,其 struct sock 无连接队列字段,无需初始化。

2.connect():客户端发起连接,触发三次握手

用户态调用(客户端):

connect(fd, (struct sockaddr*)&server_addr, sizeof(server_addr));

内核操作与结构变化

​​​​​​​​​​​​​​

核心差异:UDP 的 connect() 仅在 struct sock 中记录远程地址(sk_daddr),无三次握手,不改变状态(仍为无连接)。

3.accept():提取已完成连接,创建新套接字

用户态调用(服务器):

int client_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &len);

内核操作与结构变化

核心差异:UDP 无 accept() 操作,数据传输直接使用绑定的套接字,无需创建新套接字。

(4)数据传输:read()/write() 与缓冲区管理

TCP 和 UDP 的数据传输均依赖struct sock中的数据发送 / 接收队列(sk_write_queue/sk_receive_queue),但因协议特性,管理逻辑差异显著,且实现细节也大不相同。

1.TCP 数据传输:可靠传输的缓冲区协作

2.UDP 数据传输:简单无状态的队列管理

(5)close():释放资源,终止连接

用户态调用

close(fd);

内核操作与结构变化

http://www.dtcms.com/a/328750.html

相关文章:

  • Android Activity 的对话框(Dialog)样式
  • RxJava 在 Android 中的深入解析:使用、原理与最佳实践
  • 基于Apache Flink的实时数据处理架构设计与高可用性实战经验分享
  • 【cs336学习笔记】[第5课]详解GPU架构,性能优化
  • 深入 Linux 线程:从内核实现到用户态实践,解锁线程创建、同步、调度与性能优化的完整指南
  • iscc2025区域赛wp
  • 服务器通过生成公钥和私钥安全登录
  • Android 在 2020-2025 都做哪些更新?
  • 如何提供对外访问的IP(内网穿透工具)
  • 【Android】ChatRoom App 技术分析
  • OpenAI 回应“ChatGPT 用多了会变傻”
  • Control Center 安卓版:个性化手机控制中心
  • ClickHouse从入门到企业级实战全解析课程简介
  • 1688商品数据抓取:Python爬虫+动态页面解析
  • 基于elk实现分布式日志
  • Windows11 运行IsaacSim GPU Vulkan崩溃
  • 三极管的基极为什么需要下拉电阻
  • Pycharm选好的env有包,但是IDE环境显示无包
  • Excel多级数据结构导入导出工具
  • Nuxt 3 跨域问题完整解决方案(开发 + 生产环境)
  • Appium-移动端自动测试框架详解
  • 【MCP开发】Nodejs+Typescript+pnpm+Studio搭建Mcp服务
  • 【数据可视化-88】航空公司航班数据分析与可视化:Python + pyecharts洞察航空旅行趋势
  • 通用安全指南
  • 关于在img标签的src里面直接使用“~/assets/images/xxx“可以,但是若将这个路径写成变量的形式就会报错
  • Java Stream API 中常用方法复习及项目实战示例
  • BGP综合实验_Te. BGP笔记
  • 七大排序算法全解析:从入门到精通
  • 开源模型应用落地-用LLaMA-Factory点亮Qwen3-4B的“读心术”(十九)
  • Java开发环境搭建(WIN+IDEA+Maven)