欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 健康 > 美食 > linux Unix Socket 介绍

linux Unix Socket 介绍

2025/6/23 0:11:53 来源:https://blog.csdn.net/m0_57249200/article/details/148741010  浏览:    关键词:linux Unix Socket 介绍

非常好!Unix Socket 是在 Linux / Unix 系统中进行**本地进程间通信(IPC)**的一种非常高效和灵活的方式,被广泛用于:

  • 系统内部通信(如 systemd 与各服务)
  • 数据库通信(如 PostgreSQL)
  • 复杂 IPC 系统(比如 Nginx 和 FastCGI)

🧠 一、什么是 Unix Socket?

Unix Socket 是一种基于文件系统路径(如 /tmp/mysock)来标识通信端点的本地 socket 通信机制,工作原理与 TCP/UDP 非常相似,但它:

特性Unix SocketTCP/IP Socket
通信对象同一台机器的进程可跨主机
地址类型文件路径 (/tmp/sock)IP地址 + 端口
性能更高(不走网络协议栈)相对较低
常用于IPC 本地通信网络服务

它实际上使用 socket API,但地址族是 AF_UNIX(也叫 AF_LOCAL),不是 AF_INET


🧱 二、基本结构和 API 调用流程

和网络 socket 很相似:

服务端流程:

  1. 创建 socket (socket)
  2. 绑定路径地址 (bind)
  3. 开始监听 (listen)
  4. 接受连接 (accept)
  5. 通信(read / write
  6. 关闭 socket (close)

客户端流程:

  1. 创建 socket (socket)
  2. 连接服务端路径 (connect)
  3. 通信
  4. 关闭

🔧 三、最小 Unix Socket 通信示例

🔌 服务端:

// unix_server.cpp
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <iostream>
#include <cstring>int main() {int server_fd = socket(AF_UNIX, SOCK_STREAM, 0);if (server_fd < 0) { perror("socket"); return 1; }sockaddr_un addr{};addr.sun_family = AF_UNIX;strcpy(addr.sun_path, "/tmp/mysocket");unlink("/tmp/mysocket"); // 清理旧的 socket 文件if (bind(server_fd, (sockaddr*)&addr, sizeof(addr)) == -1) {perror("bind");return 1;}listen(server_fd, 5);std::cout << "[Server] Waiting for connections...\n";int client_fd = accept(server_fd, nullptr, nullptr);char buf[128] = {0};read(client_fd, buf, sizeof(buf));std::cout << "[Server] Received: " << buf << std::endl;const char *reply = "Hello from server";write(client_fd, reply, strlen(reply));close(client_fd);close(server_fd);unlink("/tmp/mysocket"); // 删除 socket 文件
}

🔌 客户端:

// unix_client.cpp
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <cstring>
#include <iostream>int main() {int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);if (sock_fd < 0) { perror("socket"); return 1; }sockaddr_un addr{};addr.sun_family = AF_UNIX;strcpy(addr.sun_path, "/tmp/mysocket");if (connect(sock_fd, (sockaddr*)&addr, sizeof(addr)) == -1) {perror("connect");return 1;}const char *msg = "Hello server!";write(sock_fd, msg, strlen(msg));char buf[128] = {0};read(sock_fd, buf, sizeof(buf));std::cout << "[Client] Received: " << buf << std::endl;close(sock_fd);
}

✅ 编译方式:

g++ unix_server.cpp -o server
g++ unix_client.cpp -o client

运行:

./server      # 一个终端
./client      # 另一个终端

📦 四、Unix Socket 类型

类型用法说明
SOCK_STREAM面向连接(类似 TCP)常用,支持 read / write
SOCK_DGRAM无连接(类似 UDP)sendto / recvfrom

示例里用的是 SOCK_STREAM,最常见。


🧠 五、Unix Socket 的优势

  • 速度快:不经过网络协议栈(相比 TCP 快 2~3 倍)
  • 文件系统集成:用文件路径表示 socket,便于权限管理
  • 支持多种通信模型:阻塞/非阻塞、epoll 事件模型等
  • 无需监听端口:安全性高,防外网攻击

🛡️ 六、权限控制与安全性

因为是普通文件路径,所以:

  • 可以使用 chmod 控制权限
  • 例如只允许某些用户连接 /tmp/mysocket
  • 系统服务也常这么做(如 /run/docker.sock

🧩 七、进阶玩法

你可以结合:

  • epoll 实现高并发 Unix socket 服务器
  • sendmsg / recvmsg 传递文件描述符(高级功能)
  • 使用 SO_PASSCRED 传递 UID/GID 验证身份
  • socketpair() 创建双向通道(比如守护进程与客户端通信)

✅ 总结一句话:

Unix Socket 是最适合本机进程间高效、可靠、灵活通信的机制,几乎所有高级 IPC 系统都在用它。


如果你想继续深入:

✅ 想不想我基于这个再帮你写一个 epoll + Unix socket 的并发服务 demo?
✅ 或者讲讲 socketpair() / sendmsg() 传 FD 的用法?
你说,我就写。
好!我们来详细讲讲两个非常实用的高级 IPC 技巧:


🧠 一、socketpair()sendmsg() 传文件描述符(FD)

这两者分别解决了两个问题:

功能技术用途
双向通信socketpair()类似管道,但支持双向
传递 FDsendmsg() / recvmsg()把一个进程中的 fd 发送给另一个进程使用

🧱 二、socketpair() —— 创建一对连接的 socket

就像一根“管道”,但是双向的、基于 socket 的,比 pipe() 更灵活。

🔧 使用方式

int sv[2];
socketpair(AF_UNIX, SOCK_STREAM, 0, sv);
// sv[0] 和 sv[1] 是两端的 socket fd,可以双向通信

通常用于:

  • 父子进程通信
  • 多线程通信(适用于 epoll
  • 模拟 TCP 连接通道但无需网络

🔄 三、进程间传递 FD 的思路

✅ 为什么要传 FD?

比如你用主进程监听连接,但你要把 连接 fd 交给另一个子进程去处理,就得“传过去”。

✅ 怎样传?

必须用 sendmsg() / recvmsg(),用带外带外数据结构 cmsghdr 传递 SCM_RIGHTS


📦 四、传 FD 的完整 demo 示例

我们写一个程序:

  • 父进程建立 socketpair
  • 父进程打开一个文件,将文件的 fd 发送给子进程
  • 子进程收到 fd,读取内容

🔹父子进程传递文件描述符示例:

// send_fd.cpp
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
#include <fcntl.h>
#include <iostream>
#include <cstring>void send_fd(int sock, int fd_to_send) {struct msghdr msg = {};struct iovec iov;char dummy = '*';iov.iov_base = &dummy;iov.iov_len = 1;msg.msg_iov = &iov;msg.msg_iovlen = 1;// 用于携带控制信息(FD)char cmsgbuf[CMSG_SPACE(sizeof(int))];msg.msg_control = cmsgbuf;msg.msg_controllen = sizeof(cmsgbuf);struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);cmsg->cmsg_level = SOL_SOCKET;cmsg->cmsg_type = SCM_RIGHTS;  // 表示发送文件描述符cmsg->cmsg_len = CMSG_LEN(sizeof(int));*((int *) CMSG_DATA(cmsg)) = fd_to_send;if (sendmsg(sock, &msg, 0) < 0)perror("sendmsg");
}int recv_fd(int sock) {struct msghdr msg = {};struct iovec iov;char dummy;iov.iov_base = &dummy;iov.iov_len = 1;msg.msg_iov = &iov;msg.msg_iovlen = 1;char cmsgbuf[CMSG_SPACE(sizeof(int))];msg.msg_control = cmsgbuf;msg.msg_controllen = sizeof(cmsgbuf);if (recvmsg(sock, &msg, 0) < 0)perror("recvmsg");struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);int fd = -1;if (cmsg && cmsg->cmsg_level == SOL_SOCKET &&cmsg->cmsg_type == SCM_RIGHTS) {fd = *((int *) CMSG_DATA(cmsg));}return fd;
}int main() {int sv[2];if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1) {perror("socketpair");return 1;}pid_t pid = fork();if (pid == 0) {// 子进程:接收文件描述符close(sv[0]);int fd = recv_fd(sv[1]);std::cout << "[Child] Got fd: " << fd << std::endl;char buf[100] = {0};lseek(fd, 0, SEEK_SET); // 确保读到开头read(fd, buf, sizeof(buf));std::cout << "[Child] File content: " << buf << std::endl;close(fd);close(sv[1]);} else {// 父进程:发送文件描述符close(sv[1]);int fd = open("test.txt", O_RDONLY);if (fd < 0) { perror("open"); return 1; }send_fd(sv[0], fd);close(fd);close(sv[0]);}return 0;
}

✅ 运行步骤

  1. 准备一个 test.txt 文件:
echo "Hello FD World!" > test.txt
  1. 编译运行:
g++ send_fd.cpp -o send_fd
./send_fd

你将看到:

[Child] Got fd: 4
[Child] File content: Hello FD World!

🔍 五、总结

技术用途特点
socketpair()本地双向通信通道快速、无需监听、适合父子进程
sendmsg() / recvmsg()跨进程传文件描述符内核传递,不依赖全局变量

✅ 衍生场景

  • 多进程网络服务器:主进程 accept() 后把连接 fd 分发给子进程
  • 守护进程管理:通过 FD 实现权限隔离和统一资源管理
  • 父进程传 pipe、socket、eventfd 等给子进程

如果你想我可以写:

  • ✅ 多进程 echo server,使用 socketpair 分发任务
  • ✅ 多进程 epoll + FD 传递高并发处理模型

你想继续往哪边深入?我可以马上写代码+讲解。




非常好,我们来系统讲解一下在 Linux 下如何通过 Unix Domain Socket(UNIX 套接字) 实现进程间通信(IPC)


🧭 目标说明

你有两个进程:

  • Server 进程:接收数据
  • Client 进程:发送数据

它们在 同一台机器的同一操作系统内核空间下运行,通过一个“socket 文件”进行通信。这个 socket 文件存在于文件系统中,例如 /tmp/mysock


🧱 方式选择:Unix Socket 是 IPC 中的高效方式

通信方式适合场景优缺点
Pipe父子进程通信简单但不能跨非亲缘进程
FIFO任意进程通信文件路径通信,半双工
Unix Socket任意进程、双工通信、支持 select/epoll🚀推荐
消息队列 / 共享内存 / 信号量高性能 / 同步控制复杂、调试麻烦
TCP/IP socket可跨主机,性能差一点多用于网络

✅ Unix Socket 通信原理

  • 使用 AF_UNIX 地址族
  • 可以像 TCP socket 一样 bind()listen()accept()connect()send()recv()
  • 通信是 面向流(SOCK_STREAM)数据报(SOCK_DGRAM)
  • 不经过网络栈,效率高

📦 实例演示(基于 SOCK_STREAM)

🚦 1. 创建 socket 服务器进程(接收端)

// server.cpp
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <iostream>
#include <cstring>#define SOCK_PATH "/tmp/mysock"int main() {int server_fd, client_fd;struct sockaddr_un addr;char buf[128];// 创建 socketserver_fd = socket(AF_UNIX, SOCK_STREAM, 0);if (server_fd < 0) {perror("socket");return 1;}// 绑定路径memset(&addr, 0, sizeof(addr));addr.sun_family = AF_UNIX;strcpy(addr.sun_path, SOCK_PATH);unlink(SOCK_PATH); // 如果之前有就删掉if (bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {perror("bind");return 2;}listen(server_fd, 5);std::cout << "[Server] Waiting for connection...\n";client_fd = accept(server_fd, NULL, NULL);std::cout << "[Server] Client connected!\n";int n = read(client_fd, buf, sizeof(buf) - 1);if (n > 0) {buf[n] = '\0';std::cout << "[Server] Received: " << buf << std::endl;}close(client_fd);close(server_fd);unlink(SOCK_PATH);return 0;
}

🚦 2. 创建 socket 客户端进程(发送端)

// client.cpp
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <iostream>
#include <cstring>#define SOCK_PATH "/tmp/mysock"int main() {int client_fd;struct sockaddr_un addr;client_fd = socket(AF_UNIX, SOCK_STREAM, 0);if (client_fd < 0) {perror("socket");return 1;}memset(&addr, 0, sizeof(addr));addr.sun_family = AF_UNIX;strcpy(addr.sun_path, SOCK_PATH);if (connect(client_fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {perror("connect");return 2;}const char *msg = "Hello from client!";write(client_fd, msg, strlen(msg));std::cout << "[Client] Sent message.\n";close(client_fd);return 0;
}

🛠 编译与运行

g++ server.cpp -o server
g++ client.cpp -o client

运行:

./server    # 先启动服务端
./client    # 启动客户端发送消息

输出:

[Server] Waiting for connection...
[Server] Client connected!
[Server] Received: Hello from client!

✅ 优势总结

  • ✅ 可以用于任意两个进程通信(不需要是父子进程)
  • ✅ 支持双向通信(read+write
  • ✅ 支持 select()epoll() 等异步模型
  • ✅ 性能远高于 TCP socket(因为不经过网络协议栈)

📦 衍生用法

你还可以用 Unix Socket 实现:

  • 🔄 RPC 通信(例如 gRPC + unix socket)
  • 📺 服务进程间的信号通知(配合 select)
  • 🔐 传送 fd(file descriptor)文件描述符
  • 📊 与 systemd 的 socket activation 通信
  • 🚀 用于嵌入式设备中代替 DBus 进行本地模块间通信

📌 是否需要我为你扩展下面内容?

  • 🔁 使用 epoll() 实现多个客户端接入?
  • 🔐 传递文件描述符(fd-passing)?
  • 💡 把 client/server 封装成库?
  • 🌟 更复杂的二进制结构体协议发送/接收?

你可以告诉我你的应用场景,我可以给你定制化。是否继续?

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词