socket API原本是为网络通讯设计的,后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket。
虽然网络socket也可用于同一台主机的进程间通讯,但是UNIX Domain Socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。
UNIX域套接字与TCP套接字相比较,在同一台主机的传输速度前者是后者的两倍。IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。UNIX Domain Socket也提供面向流和面向数据包两种API接口,类似于TCP和UDP,但是面向消息的UNIXDomain Socket也是可靠的,消息既不会丢失也不会顺序错乱。
我们先来看下地址格式:
<span style="font-family:FangSong_GB2312;font-size:18px;">struct sockaddr_un {
sa_family_t sun_family; /* AF_UNIX */
char sun_path[108]; /* pathname */
};</span>
UNIX Domain Socket与因特网域套接字socket编程最明显的不同在于地址格式不同,用结构体sockaddr_un表示,因特网域套接字socket地址是IP地址加端口号,而UNIX Domain Socket的地址是一个socket类型的文件在文件系统中的路径。
创建套接字的函数如下:
<span style="font-family:FangSong_GB2312;font-size:18px;">#include <sys/socket.h>
int socket(int domain, int type, int protocol);/*成功返回文件(套接字)描述符,出错返回-1*/ </span>
其中的domain参数用于指定通信域,如果为PF_UNIX的话,那么创建unix域套接字,type套接字类型主要有面向连接的字节流SOCK_STREAM和长度固定的无连接的不可靠数据报报文传递SOCK_DGRAM。参数protocol通常为0。
下面有两组代码(第一组type为SOCK_STREAM的服务端和客户端,第二组为SOCK_DGRAM)。
第一组server.c:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define UNIX_DOMAIN "UNIX.domain"
int main(void)
{
socklen_t clt_addr_len;
int listen_fd;
int com_fd;
int ret;
int i;
static char recv_buf[1024];
int len;
struct sockaddr_un clt_addr;
struct sockaddr_un srv_addr;
listen_fd=socket(PF_UNIX,SOCK_STREAM,0);
if(listen_fd<0)
{
perror("cannot create communication socket");
return 1;
}
//set server addr_param
srv_addr.sun_family=AF_UNIX;
strncpy(srv_addr.sun_path,UNIX_DOMAIN,sizeof(srv_addr.sun_path)-1);
unlink(UNIX_DOMAIN);
//bind sockfd & addr
ret=bind(listen_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));
if(ret==-1)
{
perror("cannot bind server socket");
close(listen_fd);
unlink(UNIX_DOMAIN);
return 1;
}
//listen sockfd
ret=listen(listen_fd,1);
if(ret==-1)
{
perror("cannot listen the client connect request");
close(listen_fd);
unlink(UNIX_DOMAIN);
return 1;
}
//have connect request use accept
len=sizeof(clt_addr);
com_fd=accept(listen_fd,(struct sockaddr*)&clt_addr,&len);
if(com_fd<0)
{
perror("cannot accept client connect request");
close(listen_fd);
unlink(UNIX_DOMAIN);
return 1;
}
//read and printf sent client info
printf("/n=====info=====/n");
for(i=0;i<4;i++)
{
memset(recv_buf,0,1024);
int num=read(com_fd,recv_buf,sizeof(recv_buf));
printf("Message from client (%d)) :%s/n",num,recv_buf);
}
close(com_fd);
close(listen_fd);
unlink(UNIX_DOMAIN);
return 0;
}
client.c:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define UNIX_DOMAIN "UNIX.domain"
int main(void)
{
int connect_fd;
int ret;
char snd_buf[1024];
int i;
static struct sockaddr_un srv_addr;
//creat unix socket
connect_fd=socket(PF_UNIX,SOCK_STREAM,0);
if(connect_fd<0)
{
perror("cannot create communication socket");
return 1;
}
srv_addr.sun_family=AF_UNIX;
strcpy(srv_addr.sun_path,UNIX_DOMAIN);
//connect server
ret=connect(connect_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));
if(ret==-1)
{
perror("cannot connect to the server");
close(connect_fd);
return 1;
}
memset(snd_buf,0,1024);
strcpy(snd_buf,"message from client");
//send info server
for(i=0;i<4;i++)
write(connect_fd,snd_buf,sizeof(snd_buf));
close(connect_fd);
return 0;
}
第二组server.c:
/*
* Demo how to implement a simple Local server
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define MAXLINE 50
#define IPC_PATH "unix_local_socket"
typedef struct sockaddr SA;
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_un local_addr, remote_addr;
socklen_t remote_len;
char buf_rcv[MAXLINE];
ssize_t rcvlen;
char buf_snd[MAXLINE] = "Welcome to UDP Server";
/* step1: create socket
* note the socket is creaed as type of SOCK_DGRAM to
* work in UDP
*/
if ((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
perror("socket failed");
exit(-1);
}
/* unlink may return error when the file does not exist, so
* we just ignore the return value
*/
unlink(IPC_PATH);
/* step2: bind the sockect to */
bzero(&local_addr, sizeof(local_addr));
local_addr.sun_family = AF_UNIX;
strcpy (local_addr.sun_path, IPC_PATH);
if (bind(sockfd, (SA *) &local_addr, sizeof(local_addr)) < 0) {
perror("bind failed");
exit(-1);
}
remote_addr = local_addr;
for (;;) {
/* step3: recvfrom */
bzero(buf_rcv, sizeof(buf_rcv)); /* clean up buffer to receive data */
remote_len = sizeof(remote_addr);
if ((rcvlen = recvfrom(sockfd, buf_rcv, sizeof(buf_rcv), 0, NULL, NULL)) < 0) {
perror("recvfrom failed");
exit(-1);
}
if (rcvlen == MAXLINE) buf_rcv[MAXLINE-1] = 0x00; /* in case of overflow */
printf("recvfrom successed: %s\n", buf_rcv);
break; /* break when receive the data from the client */
}
return 0;
}
client.c:
/*
* Demo how to implement a simple Local client
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define MAXLINE 50
#define IPC_PATH "unix_local_socket"
typedef struct sockaddr SA;
int main(int argc, char **argv)
{
int sockfd, nbyte;
struct sockaddr_un remote_addr;
char buf_snd[MAXLINE] = "Hello from UDP client";
/* step1: create socket
* note the socket is creaed as type of SOCK_DGRAM to
* work in UDP
*/
if ((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
perror("socket failed");
exit(-1);
}
/* step2: directly send w/o bind */
bzero(&remote_addr, sizeof(remote_addr));
remote_addr.sun_family = AF_UNIX;
strcpy (remote_addr.sun_path, IPC_PATH);
if (-1 == sendto(sockfd, buf_snd, sizeof(buf_snd), 0, (SA *)&remote_addr, sizeof(remote_addr))) {
perror("sendto failed");
exit(-1);
}
printf("C> recvfrom begin ...\n");
if ((nbyte = recvfrom(sockfd, buf, MAXLINE, 0, NULL, NULL)) < 0) {
perror("C> recvfrom error");
exit(-1);
}
return 0;
}