概述
Linux 下进程通讯方式有很多, 比较典型的有套接字, 平时比较常用的套接字是基于 TCP/IP 协议的, 适用于两台不同主机上两个进程间通信, 通信之前需要指定 IP 地址. 但是如果同一台主机上两个进程间通信用套接字, 还需要指定 ip 地址, 有点过于繁琐. 这个时候就需要用到 UNIX Domain Socket, 简称 UDS,
UDS 的优势:
UDS 传输不需要经过网络协议栈, 不需要打包拆包等操作, 只是数据的拷贝过程
UDS 分为 SOCK_STREAM(流套接字)和 SOCK_DGRAM(数据包套接字), 由于是在本机通过内核通信, 不会丢包也不会出现发送包的次序和接收包的次序不一致的问题
流程介绍
如果熟悉 Socket 的话, UDS 也是同样的方式, 区别如下:
UDS 不需要 IP 和 Port, 而是通过一个文件名来表示
domain 为 AF_UNIX
UDS 中使用 sockaddr_un 表示
- struct sockaddr_un {
- sa_family_t sun_family; /* AF_UNIX */
- char sun_path[UNIX_PATH_MAX]; /* pathname */
- };
服务端: socket -> bind -> listen -> accet -> recv/send -> close
客户端: socket -> connect -> recv/send -> close
函数介绍
开始创建 socket
- int socket(int domain, int type, int protocol)
- domain(域) : AF_UNIX
type : SOCK_STREAM/ SOCK_DGRAM :
protocol : 0
SOCK_STREAM(流) : 提供有序, 可靠的双向连接字节流. 可以支持带外数据传输机制,
无论多大的数据都不会截断
SOCK_DGRAM(数据报): 支持数据报(固定最大长度的无连接, 不可靠的消息), 数据报超过最大长度, 会被截断.
获取到 socket 文件描述符之后, 还要将其绑定一个文件上
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd : 传入 sock 的文件描述符
addr : 用 sockaddr_un 表示
addrlen : 结构体长度
- struct sockaddr_un {
- sa_family_t sun_family; /* AF_UNIX */
- char sun_path[UNIX_PATH_MAX]; /* pathname */
- };
监听客户端的连接
int listen(int sockfd, int backlog);
sockfd : 文件描述符
backlog : 连接队列的长度
接受客户端的连接
int accept(int socket, struct sockaddr *restrict address, socklen_t *restrict address_len);
UDS 不存在客户端地址的问题, 因此这里的 addr 和 addrlen 参数可以设置为 NULL
Demo 程序
- uds-server.c
- #include<stdio.h>
- #include<stdlib.h>
- #include<string.h>
- #include<sys/stat.h>
- #include<sys/socket.h>
- #include<sys/types.h>
- #include<sys/un.h>
- #include<errno.h>
- #include<stddef.h>
- #include<unistd.h>
- #define MAX_CONNECT_NUM 2
- #define BUFFER_SIZE 1024
- const char *filename="uds-tmp";
- int main()
- {
- int fd,new_fd,len,i;
- struct sockaddr_un un;
- fd = socket(AF_UNIX,SOCK_STREAM,0);
- if(fd <0){
- printf("Request socket failed!\n");
- return -1;
- }
- un.sun_family = AF_UNIX;
- unlink(filename);
- strcpy(un.sun_path,filename);
- if(bind(fd,(struct sockaddr *)&un,sizeof(un)) <0 ){
- printf("bind failed!\n");
- return -1;
- }
- if(listen(fd,MAX_CONNECT_NUM) < 0){
- printf("listen failed!\n");
- return -1;
- }
- while(1){
- struct sockaddr_un client_addr;
- char buffer[BUFFER_SIZE];
- bzero(buffer,BUFFER_SIZE);
- len = sizeof(client_addr);
- //new_fd = accept(fd,(struct sockaddr *)&client_addr,&len);
- new_fd = accept(fd,NULL,NULL);
- if(new_fd < 0){
- printf("accept failed\n");
- return -1;
- }
- int ret = recv(new_fd,buffer,BUFFER_SIZE,0);
- if(ret < 0){
- printf("recv failed\n");
- }
- for(i=0; i<10; i++){
- printf("%d",buffer[i]);
- }
- close(new_fd);
- break;
- }
- close(fd);
- }
- uds-client.c
- #include<stdio.h>
- #include<stdlib.h>
- #include<string.h>
- #include<sys/stat.h>
- #include<sys/socket.h>
- #include<sys/types.h>
- #include<sys/un.h>
- #include<errno.h>
- #include<stddef.h>
- #include<unistd.h>
- #define BUFFER_SIZE 1024
- const char *filename="uds-tmp";
- int main()
- {
- struct sockaddr_un un;
- int sock_fd;
- char buffer[BUFFER_SIZE] = {1,2,3};
- un.sun_family = AF_UNIX;
- strcpy(un.sun_path,filename);
- sock_fd = socket(AF_UNIX,SOCK_STREAM,0);
- if(sock_fd < 0){
- printf("Request socket failed\n");
- return -1;
- }
- if(connect(sock_fd,(struct sockaddr *)&un,sizeof(un)) < 0){
- printf("connect socket failed\n");
- return -1;
- }
- send(sock_fd,buffer,BUFFER_SIZE,0);
- close(sock_fd);
- return 0;
- }
参考
Linux 下的 IPC-UNIX Domain Socket http://blog.csdn.net/guxch/article/details/7041052
进程通信之六 UDS http://blog.csdn.net/chn89/article/details/7288512
进程间通信简介(一) http://blog.csdn.net/wangcaisheng_/article/details/50592687
Unix 域套接字 (Unix Domain Socket) 介绍 http://blog.csdn.net/Roland_Sun/article/details/50266565
来源: http://www.jianshu.com/p/0a402920461b