linux网络编程之socket(十五):UNIX域套接字编程和socketpair

一、UNIX Domain Socket IPC

socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket。虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。UNIX域套接字与TCP套接字相比较,在同一台主机的传输速度前者是后者的两倍。这是因为,IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。UNIX Domain Socket也提供面向流和面向数据包两种API接口,类似于TCP和UDP,但是面向消息的UNIX Domain Socket也是可靠的,消息既不会丢失也不会顺序错乱。

使用UNIX Domain Socket的过程和网络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 108struct sockaddr_un {sa_family_tsun_family; /* AF_UNIX */charsun_path[UNIX_PATH_MAX]; /* pathname */};

二、回射/客户服务器程序

通信的流程跟前面说过的tcp/udp 是类似的,下面直接来看程序:

C++ Code

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102/*************************************************************************>FileName:echoser_tcp.c>Author:Simba>Mail:dameng34@163.com>CreatedTime:Sun03Mar201306:13:55PMCST************************************************************************/#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<errno.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<string.h>#include<sys/un.h>#defineERR_EXIT(m)\do{\perror(m);\exit(EXIT_FAILURE);\}while(0)voidecho_ser(intconn){charrecvbuf[1024];intn;while(1){memset(recvbuf,0,sizeof(recvbuf));n=read(conn,recvbuf,sizeof(recvbuf));if(n==-1){if(n==EINTR)continue;ERR_EXIT("readerror");}elseif(n==0){printf("clientclose\n");break;}fputs(recvbuf,stdout);write(conn,recvbuf,strlen(recvbuf));}close(conn);}/*unixdomainsocket与TCP套接字相比较,在同一台主机的传输速度前者是后者的两倍。*/intmain(void){intlistenfd;if((listenfd=socket(PF_UNIX,SOCK_STREAM,0))<0)ERR_EXIT("socketerror");unlink("/tmp/testsocket");//地址复用structsockaddr_unservaddr;memset(&servaddr,0,sizeof(servaddr));servaddr.sun_family=AF_UNIX;strcpy(servaddr.sun_path,"/tmp/testsocket");if(bind(listenfd,(structsockaddr*)&servaddr,sizeof(servaddr))<0)ERR_EXIT("binderror");if(listen(listenfd,SOMAXCONN)<0)ERR_EXIT("listenerror");intconn;pid_tpid;while(1){conn=accept(listenfd,NULL,NULL);if(conn==-1){if(conn==EINTR)continue;ERR_EXIT("accepterror");}pid=fork();if(pid==-1)ERR_EXIT("forkerror");if(pid==0){close(listenfd);echo_ser(conn);exit(EXIT_SUCCESS);}close(conn);}return0;}

C++ Code

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364/*************************************************************************>FileName:echocli_tcp.c>Author:Simba>Mail:dameng34@163.com>CreatedTime:Sun03Mar201306:13:55PMCST************************************************************************/#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<stdio.h>#include<stdlib.h>#include<unistd.h>#include<errno.h>#include<sys/types.h>#include<sys/socket.h>#include<netinet/in.h>#include<string.h>#include<sys/un.h>#defineERR_EXIT(m)\do{\perror(m);\exit(EXIT_FAILURE);\}while(0)voidecho_cli(intconn){charsendbuf[1024]={0};charrecvbuf[1024]={0};while(fgets(sendbuf,sizeof(sendbuf),stdin)!=NULL){write(conn,sendbuf,strlen(sendbuf));read(conn,recvbuf,sizeof(recvbuf));fputs(recvbuf,stdout);memset(recvbuf,0,sizeof(recvbuf));memset(sendbuf,0,sizeof(sendbuf));}close(conn);}intmain(void){intsock;if((sock=socket(PF_UNIX,SOCK_STREAM,0))<0)ERR_EXIT("socketerror");structsockaddr_unservaddr;memset(&servaddr,0,sizeof(servaddr));servaddr.sun_family=AF_UNIX;strcpy(servaddr.sun_path,"/tmp/testsocket");if(connect(sock,(structsockaddr*)&servaddr,sizeof(servaddr))<0)ERR_EXIT("connecterror");echo_cli(sock);return0;}

server 使用fork 的形式来接受多个连接,server调用bind 会创建一个文件,如下所示:

simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ls -l /tmp/test\ socketsrwxrwxr-x 1 simba simba 0 Jun 12 15:27 /tmp/test socket

即文件类型为s,表示SOCKET文件,与FIFO(命名管道)文件,类型为p,类似,都表示内核的一条通道,读写文件实际是在读写内核通道。程序中调用unlink(解除硬链接) 是为了在开始执行程序时删除以前创建的文件,以便在重启服务器时不会提示address in use。其他方面与以前说过的回射客户服务器程序没多大区别,不再赘述。

三、UNIX域套接字编程注意点

1、bind成功将会创建一个文件,权限为0777 & ~umask2、sun_path最好用一个绝对路径3、UNIX域协议支持流式套接口与报式套接口4、UNIX域流式套接字connect发现监听队列满时,会立刻返回一个ECONNREFUSED,这和TCP不同,如果监听队列满,会忽略到来的SYN,这导致对方重传SYN。

四、socketpair 函数

功能:创建一个全双工的流管道原型int socketpair(int domain, int type, int protocol, int sv[2]);参数domain: 协议家族type: 套接字类型protocol: 协议类型sv: 返回套接字对返回值:成功返回0;失败返回-1

实际上socketpair 函数跟pipe 函数是类似的,也只能在同个主机上具有亲缘关系的进程间通信,但pipe 创建的匿名管道是半双工的,而socketpair 可以认为是创建一个全双工的管道。

可以使用socketpair 创建返回的套接字对进行父子进程通信:

C++ Code

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869/*************************************************************************>FileName:echoser.c>Author:Simba>Mail:dameng34@163.com>CreatedTime:Fri01Mar201306:15:27PMCST************************************************************************/#include<stdio.h>#include<sys/types.h>#include<sys/socket.h>#include<unistd.h>#include<stdlib.h>#include<errno.h>#include<arpa/inet.h>#include<netinet/in.h>#include<string.h>#defineERR_EXIT(m)\do{\perror(m);\exit(EXIT_FAILURE);\}while(0)intmain(void){intsockfds[2];if(socketpair(PF_UNIX,SOCK_STREAM,0,sockfds)<0)ERR_EXIT("sockpair");pid_tpid;pid=fork();if(pid==-1)ERR_EXIT("fork");if(pid>0){intval=0;close(sockfds[1]);while(1){++val;printf("sendingdata:%d\n",val);write(sockfds[0],&val,sizeof(val));read(sockfds[0],&val,sizeof(val));printf("recvdata:%d\n",val);sleep(1);}}elseif(pid==0){intval;close(sockfds[0]);while(1){read(sockfds[1],&val,sizeof(val));++val;write(sockfds[1],&val,sizeof(val));}}return0;}

输出如下:

simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./socketpairsending data: 1recv data : 2sending data: 3recv data : 4sending data: 5recv data : 6sending data: 7recv data : 8sending data: 9recv data : 10sending data: 11recv data : 12sending data: 13recv data : 14sending data: 15recv data : 16……………………………..

即父进程持有sockfds[0] 套接字进行读写,而子进程持有sockfds[1] 套接字进行读写。

参考:

《Linux C 编程一站式学习》

《TCP/IP详解 卷一》

《UNP》用敬业的精神去面对每一份挑战,

linux网络编程之socket(十五):UNIX域套接字编程和socketpair

相关文章:

你感兴趣的文章:

标签云: