Unix 域协议是一种用于同一台计算机上进程间通信(IPC)的技术。它提供了一种比基于网络的套接字更高效的方式来在本地进程中交换数据。Unix 域协议使用文件系统作为通信的媒介,并且只限于在同一台计算机上运行的进程之间进行通信。
Unix 域套接字类型
Unix 域套接字支持两种主要类型:
- 流套接字 (SOCK_STREAM):类似于 TCP 套接字,提供面向连接的服务,保证数据按顺序传输,并且不会丢失或重复。
- 数据报套接字 (SOCK_DGRAM):类似于 UDP 套接字,不保证数据按顺序传输,也不提供错误恢复机制。
Unix 域套接字的优点
- 高效:由于数据不需要离开本机,所以 Unix 域套接字比网络套接字更快。
- 安全:通信仅限于本地,减少了网络攻击的风险。
- 简单:使用与网络套接字相同的 API。
Unix 域套接字的缺点
- 局限性:只能在同一台计算机上使用。
- 可移植性:仅限于 Unix 和类 Unix 系统,不适用于 Windows 等其他操作系统。
Unix 域套接字的使用
Unix 域套接字的使用与网络套接字相似,但有一些关键的不同之处:
创建 Unix 域套接字
1#include <sys/un.h>
2#include <sys/socket.h>
3
4// 创建 Unix 域套接字
5int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
6if (sockfd == -1) {
7 perror("socket");
8 exit(EXIT_FAILURE);
9}
地址结构
Unix 域套接字使用 struct sockaddr_un
来表示地址:
1struct sockaddr_un addr;
2memset(&addr, 0, sizeof(addr));
3addr.sun_family = AF_UNIX;
4strncpy(addr.sun_path, "/path/to/socket", sizeof(addr.sun_path) - 1);
绑定套接字
1// 绑定套接字
2if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
3 perror("bind");
4 exit(EXIT_FAILURE);
5}
监听连接
1// 监听连接
2if (listen(sockfd, 5) == -1) {
3 perror("listen");
4 exit(EXIT_FAILURE);
5}
接受连接
1// 接受连接
2int client_sock = accept(sockfd, NULL, NULL);
3if (client_sock == -1) {
4 perror("accept");
5 exit(EXIT_FAILURE);
6}
发送和接收数据
1// 发送数据
2const char *message = "Hello, World!";
3if (send(client_sock, message, strlen(message), 0) == -1) {
4 perror("send");
5 exit(EXIT_FAILURE);
6}
7
8// 接收数据
9char buffer[1024];
10ssize_t bytes_received = recv(client_sock, buffer, 1024, 0);
11if (bytes_received == -1) {
12 perror("recv");
13 exit(EXIT_FAILURE);
14}
15buffer[bytes_received] = '\0';
16printf("Received: %s\n", buffer);
关闭套接字
1// 关闭套接字
2close(sockfd);
示例代码
下面是一个简单的 Unix 域套接字服务器端和客户端示例,演示了如何使用 Unix 域套接字进行进程间通信。
服务器端 (server.c)
1#include <sys/un.h>
2#include <sys/socket.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <unistd.h>
6#include <string.h>
7
8#define SOCKET_PATH "/tmp/unix_socket"
9
10int main() {
11 int sockfd, new_sock;
12 struct sockaddr_un addr, cli_addr;
13 char buffer[1024];
14 socklen_t len;
15
16 // 创建 Unix 域套接字
17 sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
18 if (sockfd == -1) {
19 perror("socket");
20 exit(EXIT_FAILURE);
21 }
22
23 // 清空地址结构
24 memset(&addr, 0, sizeof(addr));
25 addr.sun_family = AF_UNIX;
26 strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1);
27
28 // 绑定套接字
29 if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
30 perror("bind");
31 exit(EXIT_FAILURE);
32 }
33
34 // 监听连接
35 if (listen(sockfd, 5) == -1) {
36 perror("listen");
37 exit(EXIT_FAILURE);
38 }
39
40 // 接受连接
41 len = sizeof(cli_addr);
42 new_sock = accept(sockfd, (struct sockaddr *)&cli_addr, &len);
43 if (new_sock == -1) {
44 perror("accept");
45 exit(EXIT_FAILURE);
46 }
47
48 // 读取数据
49 ssize_t bytes_received = recv(new_sock, buffer, 1024, 0);
50 if (bytes_received == -1) {
51 perror("recv");
52 exit(EXIT_FAILURE);
53 }
54 buffer[bytes_received] = '\0';
55 printf("Received: %s\n", buffer);
56
57 // 发送响应
58 const char *response = "Hello, Client!";
59 if (send(new_sock, response, strlen(response), 0) == -1) {
60 perror("send");
61 exit(EXIT_FAILURE);
62 }
63
64 // 关闭套接字
65 close(new_sock);
66 close(sockfd);
67
68 return 0;
69}
客户端 (client.c)
1#include <sys/un.h>
2#include <sys/socket.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <unistd.h>
6#include <string.h>
7
8#define SOCKET_PATH "/tmp/unix_socket"
9
10int main() {
11 int sockfd;
12 struct sockaddr_un addr;
13 char buffer[1024];
14
15 // 创建 Unix 域套接字
16 sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
17 if (sockfd == -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, SOCKET_PATH, sizeof(addr.sun_path) - 1);
26
27 // 连接到服务器
28 if (connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
29 perror("connect");
30 exit(EXIT_FAILURE);
31 }
32
33 // 发送数据
34 const char *message = "Hello, Server!";
35 if (send(sockfd, message, strlen(message), 0) == -1) {
36 perror("send");
37 exit(EXIT_FAILURE);
38 }
39
40 // 读取响应
41 ssize_t bytes_received = recv(sockfd, buffer, 1024, 0);
42 if (bytes_received == -1) {
43 perror("recv");
44 exit(EXIT_FAILURE);
45 }
46 buffer[bytes_received] = '\0';
47 printf("Received: %s\n", buffer);
48
49 // 关闭套接字
50 close(sockfd);
51
52 return 0;
53}
编译和运行
为了编译上述代码,你可以使用以下命令:
1gcc -o server server.c
2gcc -o client client.c
然后先运行服务器:
1./server
接着运行客户端:
1./client
您应该能够在服务器和客户端之间看到数据的交换。
注意事项
- 在创建 Unix 域套接字之前,请确保不存在旧的套接字文件。如果存在,可以手动删除或在程序开始时删除。
- 请确保 Unix 域套接字文件具有适当的文件权限,以防止未经授权的访问。
- 在实际应用中,可能需要处理更复杂的错误情况,比如处理连接失败的情况。
- 使用 Unix 域套接字时,需要确保文件描述符集合在调用期间不会被其他线程或信号处理程序修改。
Unix 域套接字是一种非常高效和安全的进程间通信方式,非常适合在同一台计算机上运行的进程之间的通信。