简介
UNIX 域套接字(UDS):UNIX Domain socket。
Unix域协议并不是一个实际的协议族,而是在单个主机上执行客户/服务器通讯的一种方式,单个主机上执行通信,也就是所谓的进行间通信(IPC),所以Unix域套接字协议可以视作IPC方法之一。
虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率,因为UDS不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。UNIX域套接字与TCP套接字相比较,在同一台主机的传输速度前者是后者的两倍。
IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。
Unix域提供两中套接字API:字节流套接字(类似TCP)和数据报套接字(类似DUP)。但是面向数据包的UDS也是可靠的,消息既不会丢失也不会顺序错乱。
Unix域套接字地址结构
使用UDS的过程和网络socket十分相似,也要先调用socket()创建一个socket文件描述符,address family指定为AF_UNIX,type可以选择SOCK_DGRAM或SOCK_STREAM,protocol参数仍然指定为0即可。
UNIX Domain Socket与网络socket编程最明显的不同在于地址格式不同,用结构体sockaddr_un表示,网络编程的socket地址是IP地址加端口号,而UNIX Domain Socket的地址是一个socket类型的文件在文件系统中的路径(普通文件系统中的路径名),这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。
#define UNIX_PATH_MAX 108
#include <sys/un.h>
struct sockaddr_un {
sa_family_t sum_family;// AF_LOCAL或者AF_UNIX
char sun_path[104];//字符串指代路径(null终止)
};
sun_path
表示与套接字关联的地址,以NULL结尾,如果未指定地址通则通过以空字符串作为路径名指示,也就是说sun_path[0]
值为0,这个效果就好像Ipv4的INADDR_ANY
和IPv6的ADDR_ANY_INIT
注意:UNIX域流式套接字connect发现监听队列满时,会立刻返回一个ECONNREFUSED,这和TCP不同,如果监听队列满,会忽略到来的SYN,这导致对方重传SYN。
UDS编程
socketpair函数
这个是UNIX域套接字特有的函数,它创建两个连接起来的套接字。
为了创建一对非命名的、相互连接的UNIX域套接字,用户可以使用它们面向网络的域套接字接口,也可使用socketpair函数
#include <sys/socket.h>
int socketpair(int family , int type , int protocol ,int sockfd[2]);
//返回:成功则为0,出错则为-1
family: 为AF_LOCAL
或者AF_UNIX
。
type:既可以是SOCK_STREAM
也可以是SOCK_DGRAM
。
protocol:必须为0。
sockfd[2]:新创建的两个套接字别在sockfd[0]和sockfd[1]中返回; sockfd[2]可用于同进程通信也可用于不同进程间通信
socketpair 编程
#include <sys/un.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#define ON 0
void parent_process(int fd)
{
char buff[100] = {0};
while(1)
{
memset(buff,0,sizeof(buff));
read(fd,buff,50);
printf("parent,%s\n",buff);
memset(buff,0,sizeof(buff));
strcpy(buff,"hello,I am parent");
write(fd,buff,strlen(buff));
sleep(1);
}
}
void child_process(int fd)
{
char msg[100] = {0};
while(1)
{
memset(msg,0,sizeof(msg));
strcpy(msg,"hello,I am child");
write(fd,msg,strlen(msg));
read(fd,msg,100);
printf("child,%s\n",msg);
sleep(1);
}
}
void one_process_exchange(int wfd,int rfd)
{
char msg[100] = {0};
strcpy(msg,"一个进程通信");
write(wfd,msg,strlen(msg));
memset(msg,0,sizeof(msg));
read(rfd,msg,100);
printf("%s",msg);
}
int main()
{
int srv[2];
int ret = 0;
if( socketpair( AF_UNIX,SOCK_DGRAM,0,srv)<0 )
{
perror("socketpair:");
return 0;
}
#if ON
one_process_exchange(srv[0],srv[1]);//测试同进程间通信
#else
pid_t chldpid = fork(); //测试不同进程间通信
if ( chldpid == 0)
{
close(srv[1]);
child_process( srv[0] );
}
else if ( chldpid > 0 )
{
close(srv[0]);
parent_process( srv[1] );
}
else
{
perror("fork");
}
#endif
}
同进程间通信结果:
root@liuxinju-VirtualBox:/home/liuxinju/work/linuxapi# ./a.out
一个进程通信
不同进程间通信:
root@liuxinju-VirtualBox:/home/liuxinju/work/linuxapi# ./a.out
parent,hello,I am child
child,hello,I am parent
parent,hello,I am child
child,hello,I am parent
parent,hello,I am child
child,hello,I am parent
^C