Unix-域套接字本地套接字
Unix 域套接字(本地套接字)
Unix 域套接字(Unix Domain Sockets),也称为本地套接字(Local
Sockets),是一种用于同一主机上进程间通信(IPC)的机制。Unix
域套接字提供了一种高效的进程间通信方式,它利用文件系统作为传输媒介,而不是网络栈,因此可以避免网络层的开销。下面详细介绍 Unix
域套接字的概念、用途、API 以及示例代码。
概述
Unix 域套接字是一种只在 Unix 和类 Unix 操作系统(包括 Linux)中可用的套接字类型。它允许在同一主机上的进程之间通过文件系统进行通信,而不必通过网络层。Unix 域套接字可以分为两种类型:
- 流式套接字 (SOCK_STREAM) :提供面向连接的服务,类似于 TCP。
- 数据报套接字 (SOCK_DGRAM) :提供无连接的服务,类似于 UDP。
特点
- 高效性 :由于通信发生在同一主机上,不需要经过网络层,因此效率更高。
- 安全性 :通信数据不离开本机,减少了外部攻击的风险。
- 简单性 :API 与传统的网络套接字相似,但无需处理 IP 地址和端口。
API
Unix 域套接字主要使用的函数包括:
- socket() :
int socket(int domain, int type, int protocol)
: 创建一个套接字。- 参数
domain
指定域,对于 Unix 域套接字为AF_UNIX
,type
指定套接字类型,如SOCK_STREAM
或SOCK_DGRAM
,protocol
通常设为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
是监听套接字描述符,addr
和addrlen
用于返回客户端的地址信息。 - 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 2#include 3#include 4#include 5#include 6#include 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 2#include 3#include 4#include 5#include 6#include 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 对于开发可靠的多进程应用程序非常重要。