🚀 使用 UNIX 域套接字 (AF_UNIX) 实现高效进程通信

在 Linux 和其他类 UNIX 系统中,进程间通信 (IPC) 的方法有很多种,例如管道、消息队列、共享内存等。然而,当你的应用程序需要在 同一台机器上的不同进程间进行高效、低延迟的数据交换时,UNIX 域套接字 (AF_UNIX) 往往是最佳选择。
它的工作方式类似于网络套接字,但所有数据传输都发生在内核内部,避免了网络协议栈的开销,因此速度更快、效率更高。


💡 为什么选择 UNIX 域套接字?

在深入代码之前,我们先来聊聊 AF_UNIX 的优势:

  1. 高性能:数据在内核中直接传递,无需经过网络层,减少了数据复制和上下文切换,显著提升了通信速度。
  2. 低延迟:由于没有网络开销,通信延迟极低,非常适合对实时性要求高的应用。
  3. 安全性:UNIX 域套接字在文件系统中表现为一个特殊文件。这意味着你可以使用标准的文件权限来控制哪些用户或进程可以访问它,提供了比某些其他 IPC 机制更细粒度的安全控制。
  4. API 熟悉度:如果你熟悉网络套接字(AF_INET),那么AF_UNIX 的 API会让你感到非常亲切。socket(), bind(), listen(), accept(), connect(), read(), write() 等函数都通用,降低了学习成本

🛠️ 如何工作?(核心概念)

UNIX 域套接字的运作方式很直观:

  1. 文件路径作为地址:与网络套接字使用 IP 地址和端口号不同,AF_UNIX 套接字使用文件系统路径作为其唯一的标识符。服务器会绑定到一个特定的文件路径(例如 /tmp/my_socket),客户端则通过这个路径来连接服务器。

  2. 服务器端:
    创建一个 AF_UNIX 类型的套接字。
    将套接字绑定到文件系统中的一个路径。这个操作会在指定路径下创建一个特殊的套接字文件。
    开始监听传入的连接请求。
    接受客户端的连接,每次接受都会返回一个新的套接字文件描述符,用于与该客户端单独通信。
    使用 read()write() 进行数据传输。

  3. 客户端:
    创建一个 AF_UNIX 类型的套接字。
    连接到服务器绑定的文件路径。
    使用read() write() 进行数据传输。


🧑‍💻 代码实战:构建一个简单的 Echo 服务器

下面将通过 C 语言代码,一步步实现一个 UNIX 域套接字服务器和对应的客户端。服务器将接收客户端发送的消息,并原样返回(Echo)。

  1. 服务器端 (server.c)
    服务器的职责是创建、绑定、监听并接受连接,然后处理数据
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h> // 包含 sockaddr_un 结构体
#include <unistd.h>#define SOCKET_PATH "/tmp/my_unix_socket" // 服务器将绑定的套接字文件路径
#define BUFFER_SIZE 256int main() {int server_fd, client_fd;struct sockaddr_un server_addr, client_addr;socklen_t client_len;char buffer[BUFFER_SIZE];int bytes_read;// 1. 创建UNIX域套接字 (AF_UNIX, 流式套接字 SOCK_STREAM)server_fd = socket(AF_UNIX, SOCK_STREAM, 0);if (server_fd == -1) {perror("socket");exit(EXIT_FAILURE);}// 2. 设置服务器地址结构体memset(&server_addr, 0, sizeof(server_addr));server_addr.sun_family = AF_UNIX;// 将套接字路径复制到 sun_path 字段strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);// 3. 重要的清理步骤:删除旧的套接字文件(如果存在)// 如果服务器上次异常退出,可能留下这个文件,导致绑定失败unlink(SOCKET_PATH); // 4. 将套接字绑定到指定的文件路径if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("bind");close(server_fd);exit(EXIT_FAILURE);}// 5. 开始监听连接请求,队列长度为 5if (listen(server_fd, 5) == -1) {perror("listen");close(server_fd);exit(EXIT_FAILURE);}printf("🚀 服务器已启动,监听在 %s...\n", SOCKET_PATH);while (1) {client_len = sizeof(client_addr);// 6. 接受新的客户端连接,这是一个阻塞调用client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_len);if (client_fd == -1) {perror("accept");continue; // 继续等待下一个连接}printf("🤝 接受到一个新连接。\n");// 7. 与客户端进行数据通信(Echo 功能)while ((bytes_read = read(client_fd, buffer, sizeof(buffer) - 1)) > 0) {buffer[bytes_read] = '\0'; // 确保字符串以 null 结尾printf("➡️ 接收到客户端消息: %s", buffer);// 将收到的消息原样回传给客户端if (write(client_fd, buffer, bytes_read) == -1) {perror("write");break; // 写入失败则退出循环}printf("⬅️ 已回复客户端。\n");}if (bytes_read == -1) {perror("read");} else if (bytes_read == 0) {printf("🔴 客户端已关闭连接。\n");}// 8. 关闭与当前客户端的连接close(client_fd);}// 9. 关闭服务器监听套接字,并清理套接字文件(通常在程序退出时执行)close(server_fd);unlink(SOCKET_PATH); return 0;
}
  1. 客户端 (client.c)
    客户端负责创建套接字并连接到服务器,然后发送和接收数据。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h> // 包含 sockaddr_un 结构体
#include <unistd.h>#define SOCKET_PATH "/tmp/my_unix_socket" // 服务器的套接字文件路径
#define BUFFER_SIZE 256int main() {int client_fd;struct sockaddr_un server_addr;char buffer[BUFFER_SIZE];int bytes_read;// 1. 创建UNIX域套接字client_fd = socket(AF_UNIX, SOCK_STREAM, 0);if (client_fd == -1) {perror("socket");exit(EXIT_FAILURE);}// 2. 设置服务器地址结构体memset(&server_addr, 0, sizeof(server_addr));server_addr.sun_family = AF_UNIX;strncpy(server_addr.sun_path, SOCKET_PATH, sizeof(server_addr.sun_path) - 1);// 3. 连接到服务器if (connect(client_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {perror("connect");close(client_fd);exit(EXIT_FAILURE);}printf("✅ 成功连接到服务器。\n");// 4. 与服务器进行数据通信while (1) {printf("请输入消息(输入 'exit' 退出): ");if (fgets(buffer, sizeof(buffer), stdin) == NULL) {break; // 读取失败或EOF}// 检查用户是否输入 'exit'if (strcmp(buffer, "exit\n") == 0) {break;}// 将消息发送给服务器if (write(client_fd, buffer, strlen(buffer)) == -1) {perror("write");break;}// 从服务器接收回复bytes_read = read(client_fd, buffer, sizeof(buffer) - 1);if (bytes_read == -1) {perror("read");break;} else if (bytes_read == 0) {printf("🚫 服务器已关闭连接。\n");break;}buffer[bytes_read] = '\0'; // 确保字符串以 null 结尾printf("⬅️ 收到服务器回复: %s", buffer);}// 5. 关闭客户端套接字close(client_fd);printf("👋 客户端已退出。\n");return 0;
}

🖥️ 编译与运行

在你的 Linux 终端中,按照以下步骤编译和运行:

编译服务器和客户端:

gcc server.c -o server
gcc client.c -o client

启动服务器:
打开一个终端窗口,运行服务器程序。

./server

你应该会看到输出 🚀 服务器已启动,监听在 /tmp/my_unix_socket…

启动客户端:
打开另一个终端窗口,运行客户端程序。

./client

客户端会显示 ✅ 成功连接到服务器。

现在,你可以在客户端终端输入消息,服务器会接收到并将其原样返回给客户端!


🧹 清理注意事项

UNIX 域套接字文件 (/tmp/my_unix_socket 在我们的例子中) 会在服务器绑定时创建。如果服务器非正常退出 (例如,被 Ctrl+C 中断),这个文件可能不会被自动删除。下次你尝试启动服务器时,可能会遇到 “Address already in use” (地址已被占用) 的错误。

这就是为什么在服务器代码中,我们特意加入了 unlink(SOCKET_PATH); 这一行。它确保在绑定新套接字之前,删除任何可能残留的旧套接字文件,从而避免启动失败。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/88583.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/88583.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/88583.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Axure教程】中继器间图片的传递

中继器在Axure中可以作为图片保存的数据库&#xff0c;在实际系统中&#xff0c;我们经常需要将选择数据库的图片添加到其他图片列表中&#xff0c;所以今天就教大家&#xff0c;怎么在Axure中实现中继器之间的图片传递&#xff0c;包含将一个中继器中的图片列表传递到另一个中…

专题:2025云计算与AI技术研究趋势报告|附200+份报告PDF、原数据表汇总下载

原文链接&#xff1a;https://tecdat.cn/?p42935 关键词&#xff1a;2025, 云计算&#xff0c;AI 技术&#xff0c;市场趋势&#xff0c;深度学习&#xff0c;公有云&#xff0c;研究报告 云计算和 AI 技术正以肉眼可见的速度重塑商业世界。过去十年&#xff0c;全球云服务收…

从代码学习深度强化学习 - PPO PyTorch版

文章目录 前言PPO 算法简介从 TRPO 到 PPOPPO 的两种形式:惩罚与截断代码实践:PPO 解决离散动作空间问题 (CartPole)环境与工具函数定义策略与价值网络PPO 智能体核心实现训练与结果代码实践:PPO 解决连续动作空间问题 (Pendulum)环境准备适用于连续动作的网络PPO 智能体 (连…

PortsWiggerLab: Blind OS command injection with output redirection

实验目的This lab contains a blind OS command injection vulnerability in the feedback function.The application executes a shell command containing the user-supplied details. The output from the command is not returned in the response. However, you can use o…

星云穿越与超光速飞行特效的前端实现原理与实践

文章目录 1,引言2,特效设计思路3,技术原理解析1. 星点的三维分布2. 视角推进与星点运动3. 三维到二维的投影4. 星点的视觉表现5. 色彩与模糊处理4,关键实现流程图5,应用场景与优化建议6,总结1,引言 在现代网页开发中,炫酷的视觉特效不仅能提升用户体验,还能为产品增添…

【Linux】C++项目分层架构:核心三层与关键辅助

C 项目分层架构全指南&#xff1a;核心三层 关键辅助一、核心三层架构 传统的三层架构&#xff08;或三层体系结构&#xff09;是构建健壮系统的基石&#xff0c;包括以下三层&#xff1a; 1. 表现层&#xff08;Presentation Layer&#xff09; 负责展示和输入处理&#xff0…

【机器学习】保序回归平滑校准算法

保序回归平滑校准算法&#xff08;SIR&#xff09;通过分桶合并线性插值解决广告预估偏差问题&#xff0c;核心是保持原始排序下纠偏。具体步骤&#xff1a;1&#xff09;按预估分升序分桶&#xff0c;统计每个分桶的后验CTR&#xff1b;2&#xff09;合并逆序桶重新计算均值&a…

项目开发日记

框架整理学习UIMgr&#xff1a;一、数据结构与算法 1.1 关键数据结构成员变量类型说明m_CtrlsList<PageInfo>当前正在显示的所有 UI 页面m_CachesList<PageInfo>已打开过、但现在不显示的页面&#xff08;缓存池&#xff09; 1.2 算法逻辑查找缓存页面&#xff1a;…

60 美元玩转 Li-Fi —— 开源 OpenVLC 平台入门(附 BeagleBone Black 驱动简单解析)

60 美元玩转 Li-Fi —— 开源 OpenVLC 平台入门&#xff08;附 BeagleBone Black 及驱动解析&#xff09;一、什么是 OpenVLC&#xff1f; OpenVLC 是由西班牙 IMDEA Networks 研究所推出的开源可见光通信&#xff08;VLC / Li-Fi&#xff09;研究平台。它把硬件、驱动、协议栈…

Python性能优化

Python 以其简洁和易用性著称,但在某些计算密集型或大数据处理场景下,性能可能成为瓶颈。幸运的是,通过一些巧妙的编程技巧,我们可以显著提升Python代码的执行效率。本文将介绍8个实用的性能优化技巧,帮助你编写更快、更高效的Python代码。   一、优化前的黄金法则:先测…

easyui碰到想要去除顶部栏按钮边框

只需要加上 plain"true"<a href"javascript:void(0)" class"easyui-linkbutton" iconCls"icon-add" plain"true"onclick"newCheck()">新增</a>

C++字符串详解:原理、操作及力扣算法实战

一、C字符串简介在C中&#xff0c;字符串的处理方式主要有两种&#xff1a;字符数组&#xff08;C风格字符串&#xff09;和std::string类。虽然字符数组是C语言遗留的底层实现方式&#xff0c;但现代C更推荐使用std::string类&#xff0c;其封装了复杂的操作逻辑&#xff0c;提…

CMU15445-2024fall-project1踩坑经历

p1目录&#xff1a;lRU\_K替换策略LRULRU\_K大体思路SetEvictableRecordAccessSizeEvictRemoveDisk SchedulerBufferPoolNewPageDeletePageFlashPage/FlashAllPageCheckReadPage/CheckWritePagePageGuard并发设计主逻辑感谢CMU的教授们给我们分享了如此精彩的一门课程&#xff…

【C语言进阶】带你由浅入深了解指针【第四期】:数组指针的应用、介绍函数指针

前言上一期讲了数组指针的原理&#xff0c;这一期接着上一期讲述数组指针的应用以及数组参数、函数参数。首先看下面的代码进行上一期内容的复习&#xff0c;pc应该是什么类型&#xff1f;char* arr[5] {0}; xxx pc &arr;分析&#xff1a;①首先判断arr是一个数组&#x…

在HTML中CSS三种使用方式

一、行内样式在标签<>中输入style "属性&#xff1a;属性值;"。(中等使用频率)不利于CSS样式的复用&#xff1b;违背了CSS内容和样式分离的设计理念&#xff0c;后期难以维护。<p style"color: red">这是div中的p元素</p>二、内部样式在…

汽车功能安全-软件单元验证 (Software Unit Verification)【用例导出方法、输出物】8

文章目录1 软件单元验证用例导出方法2 测试用例完整性度量标准3 验证环境要求4 软件单元验证的工作产品1 软件单元验证用例导出方法 为确保软件单元测试的测试案例规范符合9.4.2要求&#xff0c;应通过表8所列方法开发测试用例。 表8 软件单元测试用例的得出方法&#xff1a; …

MySQL内置函数(8)

文章目录前言一、日期函数二、字符串函数三、数学函数四、其它函数总结前言 其实在之前的几篇中我们也用到了内置函数&#xff0c;现在我们再来系统学习一下它&#xff01; 一、日期函数 函数名称描述current_date()获取当前日期current_time()获取当前时间current_timestamp(…

苍穹外卖项目日记(day04)

苍穹外卖|项目日记(day04) 前言: 今天主要是接口开发, 涉及的新东西不多, 需要注意的只有多表联查和修改的逻辑,今日难点: 1.菜品的停起售状态设置 2.套餐的停起售状态设置 3.动态sql中的 useGeneratedKeys 与 keyProperty 两个参数 一. 菜品的停起售状态设置 ​ 在菜品的停售中…

React之旅-05 List Key

每个React的初学者&#xff0c;在调试程序时&#xff0c;都会遇到这样的警告&#xff1a;Warning: Each child in a list should have a unique "key" prop. 如下面的代码&#xff1a; const list [Learn React, Learn GraphQL];const ListWithoutKey () > (&l…

[特殊字符] 人工智能技术全景:从基础理论到前沿应用的深度解析

&#x1f680; 人工智能技术全景&#xff1a;从基础理论到前沿应用的深度解析 在这个AI驱动的时代&#xff0c;理解人工智能的核心技术和应用场景已成为技术人员的必备技能。本文将带你深入探索AI的发展脉络、核心技术差异以及在各行业的创新应用。 文章目录&#x1f680; 人工…