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

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 对于开发可靠的多进程应用程序非常重要。

 


http://www.kler.cn/a/583971.html

相关文章:

  • NLP常见任务专题介绍(4)-ConditionalGeneration和CasualLM区别
  • 关于Playwright和Selenium 的区别和选择
  • nginx部署使用【常用命令】
  • C++时间复杂度详解
  • Blackbox.Ai体验:AI编程插件如何提升开发效率
  • Docker 基础命令 - 以 Nginx 实战总结
  • 在Electron-Vue中实现macOS风格自定义标题栏
  • 数据结构与算法效率分析:时间复杂度与空间复杂度详解(C语言)
  • 【OpenCV C++】存图,如何以时间命名,“年月日-时分秒“产生唯一的文件名呢?“年月日-时分秒-毫秒“ 自动检查存储目录,若不存在自动创建存图
  • 2024年第十五届蓝桥杯软件C/C++大学A组——五子棋对弈
  • 前缀和(例题)
  • availability() missing 2 required positional arguments: ‘host‘ and ‘d‘ 怎么处理
  • ElasticSearch 入门到放弃(持续更新中)
  • JAVA学习-练习试用Java实现“对大数据集中的用户行为数据进行关联规则挖掘和频繁项集筛选”
  • Windows系统本地部署File Browser打造支持远程访问的私人网盘
  • 安卓实现魔改版 CRC32 算法
  • 如何实现Spring Boot与Oracle数据库的完美对接?
  • 智能制造:构筑网络新安全“智”造
  • 文件解析漏洞靶场通关合集
  • 使用Dockerfile打包java项目生成镜像部署到Linux_java项目打docker镜像的dockerfile