Unix 域套接字(本地套接字)

Unix 域套接字(Unix Domain Sockets),也称为本地套接字(Local Sockets),是一种用于同一主机上进程间通信(IPC)的机制。Unix 域套接字提供了一种高效的进程间通信方式,它利用文件系统作为传输媒介,而不是网络栈,因此可以避免网络层的开销。下面详细介绍 Unix 域套接字的概念、用途、API 以及示例代码。

概述

Unix 域套接字是一种只在 Unix 和类 Unix 操作系统(包括 Linux)中可用的套接字类型。它允许在同一主机上的进程之间通过文件系统进行通信,而不必通过网络层。Unix 域套接字可以分为两种类型:

  1. 流式套接字 (SOCK_STREAM):提供面向连接的服务,类似于 TCP。
  2. 数据报套接字 (SOCK_DGRAM):提供无连接的服务,类似于 UDP。

特点

  • 高效性:由于通信发生在同一主机上,不需要经过网络层,因此效率更高。
  • 安全性:通信数据不离开本机,减少了外部攻击的风险。
  • 简单性:API 与传统的网络套接字相似,但无需处理 IP 地址和端口。

API

Unix 域套接字主要使用的函数包括:

  • socket():

    • int socket(int domain, int type, int protocol): 创建一个套接字。
    • 参数domain指定域,对于 Unix 域套接字为AF_UNIXtype指定套接字类型,如SOCK_STREAMSOCK_DGRAMprotocol通常设为0。
  • bind():

    • int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen): 绑定套接字到一个地址。
    • 参数sockfd是套接字描述符,addr是地址结构的指针,addrlen是地址结构的大小。
  • listen():

    • int listen(int sockfd, int backlog): 将套接字标记为监听状态。
    • 参数sockfd是套接字描述符,backlog是连接队列的最大长度。
  • accept():

    • int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen): 接受传入的连接。
    • 参数sockfd是监听套接字描述符,addraddrlen用于返回客户端的地址信息。
  • connect():

    • int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen): 连接到一个服务器。
    • 参数sockfd是套接字描述符,addr是服务器地址结构的指针,addrlen是地址结构的大小。
  • send()recv():

    • ssize_t send(int sockfd, const void *buf, size_t len, int flags): 发送数据。
    • ssize_t recv(int sockfd, void *buf, size_t len, int flags): 接收数据。
    • 参数sockfd是套接字描述符,buf是缓冲区指针,len是数据长度,flags用于指定发送或接收的选项。
  • close():

    • int close(int sockfd): 关闭套接字。
    • 参数sockfd是套接字描述符。

示例代码

下面是一个简单的 Unix 域套接字示例,演示了如何在服务器和客户端之间进行通信。

服务器端 (server.c)

复制代码
1#include <sys/socket.h>
2#include <sys/un.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <unistd.h>
6#include <string.h>
7
8#define SOCK_PATH "/tmp/unix_socket_example.sock"
9
10int main() {
11    int server_sock, client_sock;
12    struct sockaddr_un addr;
13    char buf[1024];
14
15    // 创建 Unix 域套接字
16    server_sock = socket(AF_UNIX, SOCK_STREAM, 0);
17    if (server_sock == -1) {
18        perror("socket");
19        exit(EXIT_FAILURE);
20    }
21
22    // 清空地址结构
23    memset(&addr, 0, sizeof(addr));
24    addr.sun_family = AF_UNIX;
25    strncpy(addr.sun_path, SOCK_PATH, sizeof(addr.sun_path) - 1);
26
27    // 绑定套接字
28    if (bind(server_sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
29        perror("bind");
30        exit(EXIT_FAILURE);
31    }
32
33    // 监听连接
34    if (listen(server_sock, 5) == -1) {
35        perror("listen");
36        exit(EXIT_FAILURE);
37    }
38
39    // 接受连接
40    socklen_t len = sizeof(addr);
41    client_sock = accept(server_sock, (struct sockaddr *)&addr, &len);
42    if (client_sock == -1) {
43        perror("accept");
44        exit(EXIT_FAILURE);
45    }
46
47    // 接收数据
48    ssize_t bytes_received = recv(client_sock, buf, sizeof(buf), 0);
49    if (bytes_received == -1) {
50        perror("recv");
51        exit(EXIT_FAILURE);
52    }
53    buf[bytes_received] = '\0';
54
55    // 输出收到的数据
56    printf("Received: %s\n", buf);
57
58    // 关闭连接
59    close(client_sock);
60    close(server_sock);
61
62    // 删除套接字文件
63    unlink(SOCK_PATH);
64
65    return 0;
66}

客户端 (client.c)

复制代码
1#include <sys/socket.h>
2#include <sys/un.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <unistd.h>
6#include <string.h>
7
8#define SOCK_PATH "/tmp/unix_socket_example.sock"
9
10int main() {
11    int sock;
12    struct sockaddr_un addr;
13    char buf[] = "Hello, Unix Domain Socket!";
14
15    // 创建 Unix 域套接字
16    sock = socket(AF_UNIX, SOCK_STREAM, 0);
17    if (sock == -1) {
18        perror("socket");
19        exit(EXIT_FAILURE);
20    }
21
22    // 清空地址结构
23    memset(&addr, 0, sizeof(addr));
24    addr.sun_family = AF_UNIX;
25    strncpy(addr.sun_path, SOCK_PATH, sizeof(addr.sun_path) - 1);
26
27    // 连接到服务器
28    if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
29        perror("connect");
30        exit(EXIT_FAILURE);
31    }
32
33    // 发送数据
34    if (send(sock, buf, strlen(buf), 0) == -1) {
35        perror("send");
36        exit(EXIT_FAILURE);
37    }
38
39    // 关闭连接
40    close(sock);
41
42    return 0;
43}

编译和运行

为了编译上述代码,你可以使用以下命令:

复制代码
1gcc -o server server.c
2gcc -o client client.c

然后运行这两个程序:

复制代码
1./server &
2./client

注意事项

  • 在使用 Unix 域套接字之前,确保检查所有 API 调用的返回值,以确保操作成功。
  • 在关闭套接字之后,记得删除套接字文件,以避免占用不必要的系统资源。
  • 如果套接字文件不再需要,应使用 unlink() 删除它,以避免占用不必要的系统资源。
  • 在实际应用中,可能需要处理更复杂的错误情况,比如处理连接失败的情况。

Unix 域套接字提供了一种简单而强大的机制来进行进程间的通信,非常适合那些需要快速访问共享数据的应用场景。理解和熟练掌握这些 API 对于开发可靠的多进程应用程序非常重要。

相关推荐
Peter_chq几秒前
Django快速入门
开发语言·数据库·后端·python·django
唐青枫1 分钟前
Linux syslog 使用教程
linux
字节王德发2 小时前
如何用Python和Selenium实现表单的自动填充与提交?
开发语言·python·selenium
genispan3 小时前
python基础8 单元测试
开发语言·python·单元测试
熬夜学编程的小王3 小时前
【Linux篇】初识Linux指令(下篇)
linux·运维·服务器·linux指令
小卓笔记6 小时前
keepalived应用
linux·服务器·数据库
钢铁男儿7 小时前
Python 生成数据(随机漫步)
开发语言·python·信息可视化
正经教主7 小时前
【菜鸟飞】在vsCode中安装python的ollama包出错的问题
开发语言·人工智能·vscode·python·ai·编辑器
Dongliner~7 小时前
【QT:多线程、锁】
开发语言·qt
Hurry67 小时前
Rocky Linux 9.x 基于 kubeadm部署k8s 1.32
linux·运维·kubernetes