基础文章6:APUE chap17 高级进程间通信



17.1 引言
17.2 基于STREAMS的管道
17.3 UNIX域套接字
     17.3.1 命名UNIX域套接字
     17.3.2 唯一连接
17.4 传送文件描述符
     17.4.1 经由基于STREAMS的管道传送文件描述符
     17.4.2 经由UNIX域套接字传送文件描述符
               sendmsg和recvmsg使用
               在UNIX域套接字上发送凭证

17.5 OPEN服务器版本1

     
17.1 引言
     前面两章讨论了UNIX系统提供的各种IPC,包括管道和套接字。本章介绍两种高级IPC:基于STREAMS的管道(STREAMS-based pipe)以及UNIX域套接字(UNIX domain socket)。

17.2 基于STREAMS的管道


17.3 UNIX域套接字

     UNIX域套接字用于在同一台机器上运行的进程之间的通信。虽然因特网域套接字可用于同一目的,但UNIX域套接字的效率更高。UNIX域套接字仅仅复制数据;他们并不执行协议处理,不需要添加或删除网络报头,无需计算检验和,不要产生顺序号,无需发送确认报文。
     小结:采用UNIX域套接字进行进程间通信的原因在于 UNIX域套接字仅仅复制数据。

     UNIX域套接字提供流和数据报两种接口。UNIX域数据报服务是可靠地,既不会丢失消息也不会传递出错。
     
     UNIX域套接字是套接字和管道之间的混合物。为了创建一对非命名的、相互连接的UNIX域套接字,用户可以使用它们面向网络的域套接字接口,也可以使用socketpair函数。
     
#include <sys/socket.h>
int socketpair(int domain,int type,int protocol,int sockfd[2]);
       return value: 若成功返回0,若出错返回-1.


     为了得到全双工管道,我们必须直接调用socketpair
     小结:管道的缺陷有两个:1)半双工 2)必须由共同祖先的进程才能通信。为了解决这个缺陷使用UNIX域套接字,利用socketpair函数创建全双工管道

     17.3.1 命名UNIX域套接字
     17.3.2 唯一连接
17.4 传送文件描述符  
     

     概述部分:关于文件共享的介绍
     17.4.1 经由基于STREAMS的管道传送文件描述符
     17.4.2 经由UNIX域套接字传送文件描述符

struct iovec {                    /* Scatter/gather array items */
               void  *iov_base;              /* Starting address */
               size_t iov_len;               /* Number of bytes to transfer */
           };

           struct msghdr {
               void         *msg_name;       /* optional address */
               socklen_t     msg_namelen;    /* size of address */
               struct iovec *msg_iov;        /* scatter/gather array */
               size_t        msg_iovlen;     /* # elements in msg_iov */
               void         *msg_control;    /* ancillary data, see below */
               socklen_t     msg_controllen; /* ancillary data buffer len */
               int           msg_flags;      /* flags on received message */
           };

 struct cmsghdr {(控制信息首部结构体 control-message-header)
               socklen_t     cmsg_len;     /* data byte count, including hdr */
               int           cmsg_level;   /* originating protocol */
               int           cmsg_type;    /* protocol-specific type */
           /* followed by
               unsigned char cmsg_data[]; */
           };
 Here  msg_name  and msg_namelen specify the source address if the socket is unconnected.msg_name may be given as a null pointer if no names are desired or required.
如果socket是无连接的,那么msg_name和msg_namelen用于指定源地址。

The fields msg_iov and msg_iovlen describe scatter-gather locations, as discussed in readv(2)
msg_iovmsg_iovlen可以指定多个缓冲区构成的数组(散布读和聚集写)。
msg_flags字段包含了说明所接受到消息的标识,这些标识如表16-9中。
msg_control和msg_controllen控制信息的传送和接收。


msg_control字段指向cmsghdr(控制信息首部)结构,
msg_controllen字段包含控制信息的字节数。

为了发送文件描述符,
cmsg_len设置为cmsghdr结构的长度加一个整型(描述符)的长度;
cmsg_level字段设置为SOL_SOCKET,
cmsg_type字段设置为SCM_RIGHTS,用以指明我们在传送访问权。(SCM:套接字级控制消息,socket_level_control_message)。访问权仅能通过UNIX域套接字传送。描述符紧随cmsg_type字段之后存放,用CMSG_DATA宏获得该整型量的指针。


int send_fd(int fd,int fd_to_send)
{
       struct iovec iov[1];
       struct msghdr msg;
       char buf[2];

       iov[0].iov_base=buf;
       iov[0].iov_len=2;
       msg.msg_iov=iov;
       msg.msg_iovlen=1;
       msg.msg_name=NULL;
       msg.msg_namelen=0;

       if(fd_to_send<0)
       {
              msg.msg_control=NULL;
              msg.msg_controllen=0;
              buf[1]=-fd_to_send;
              if(buf[1]==0)
                     buf[1]=1;
       }
       else
       {
              if(cmptr==NULL && (cmptr=malloc(CONTROLLEN))==NULL)
                     return(-1);

              cmptr->cmsg_level=SOL_SOCKET;
              cmptr->cmsg_type=SCM_RIGHTS;
              cmptr->cmsg_len=CONTROLLEN;
              msg.msg_control=cmptr;
              msg.msg_controllen=CONTROLLEN;
              *(int *)CMSG_DATA(cmptr)=fd_to_send;
              buf[1]=0;
       }

       buf[0]=0;
       if(sendmsg(fd,&msg,0)!=2)
              return(-1);

       return(0);
}

int send_err(int fd,int errcode,const char * msg)
{
       int n;

       if((n=strlen(msg))>0)
              if(writen(fd,msg,n)!=n)
                     return(-1);

       if(errcode>=0)
              errcode=-1;

       if(send_fd(fd,errcode)<0)
              return(-1);

       return(0);
}

int recv_fd(int fd,ssize_t (*userfunc)(int,const void *,size_t))
{
       int newfd,nr,status;
       char * ptr;
       char buf[MAXLINE];
       struct iovec iov[1];
       struct msghdr msg;
       status=-1;
       for(;;)
       {
              iov[0].iov_base=buf;
              iov[0].iov_len=sizeof(buf);
              msg.msg_iov=iov;
              msg.msg_iovlen=1;
              msg.msg_name=NULL;
              msg.msg_namelen=0;
              if(cmptr==NULL && (cmptr=malloc(CONTROLLEN))==NULL)
                     return(-1);
              msg.msg_control=cmptr;
              msg.msg_controllen=CONTROLLEN;
              if((nr=recvmsg(fd,&msg,0))<0)
              {
                     printf("recvmsg error");
              }
              else if(nr==0)
              {
                     printf("connection closed by server");
                     return(-1);
              }

              for(ptr=buf;ptr<&buf[nr];)
              {
                     if(*ptr++==0)
                     {
                            if(ptr!=&buf[nr-1])
                                   printf("message format error");
                            status=*ptr & 0xff;
                            if(status==0)
                            {
                                   if(msg.msg_controllen!=CONTROLLEN)
                                          printf("status=0 but no fd");
                                   newfd=*(int *)CMSG_DATA(cmptr);
                            }
                            else
                            {
                                   newfd=-status;
                            }
                            nr-=2;
                     }

              }
              if(nr>0 && (*userfunc)(STDERR_FILENO,buf,nr)!=nr)
                     return(-1);
              if(status>=0)
                     return(newfd);
       }
}


在UNIX域套接字上发送凭证

open服务器版本1
/*client.c
* gcc -Wall client.c -o client
*/


#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<errno.h>
#include<sys/uio.h>
//#include"error.c"

#define MAXLINE 200               
#define CL_OPEN "open"
#define BUFFSIZE 8192
#define CONTROLLEN CMSG_LEN(sizeof(int))

static struct cmsghdr * cmptr=NULL;
int csopen(char *,int);
int s_pipe(int fd[2]);
int recv_fd(int fd,ssize_t (*userfunc)(int,const void *,size_t));

int main(int argc,char * argv[])
{
       int n,fd;
       char buf[BUFFSIZE],line[MAXLINE];

     printf("\n\nPlease input the name of the file:");
       while(fgets(line,MAXLINE,stdin)!=NULL)
       {
              if(line[strlen(line)-1]=='\n')
                     line[strlen(line)-1]=0;

            printf("\n\nnow do the csopen:line :%s\n",line);
              if((fd=csopen(line,O_RDONLY))<0)
                     continue;
              while((n=read(fd,buf,BUFFSIZE))>0)
              {
                     if(write(STDOUT_FILENO,buf,n)!=n)
                            printf("client:write error");
              }
              if(n<0)
                     printf("client:read error");
              close(fd);

       }
       exit(0);
}

int s_pipe(int fd[2])
{
       return(socketpair(AF_UNIX,SOCK_STREAM,0,fd));
}



int csopen(char * name,int oflag)
{
       pid_t pid;
       int len;
       char buf[10];
       struct iovec iov[3];
       static int fd[2]={-1,-1};

       if(fd[0]<0)
       {
              if(s_pipe(fd)<0)
                     printf("s_pipe error");
              if((pid=fork())<0)
                     printf("fork error");
              else if(pid==0)
              {
                     close(fd[0]);
                     if(fd[1]!=STDIN_FILENO && dup2(fd[1],STDIN_FILENO)!=STDIN_FILENO)
                            printf("dup2 error to stdin");
                     if(fd[1]!=STDOUT_FILENO && dup2(fd[1],STDOUT_FILENO)!=STDOUT_FILENO)
                            printf("dup2 error to stdout");
                     if(execl("./chap17_open_server","chap17_open_server",(char *)0)<0)
                            printf("execl error");
              }
              close(fd[1]);
       }
       sprintf(buf," %d",oflag);
       iov[0].iov_base=CL_OPEN " ";
       iov[0].iov_len=strlen(CL_OPEN)+1;
       iov[1].iov_base=name;
       iov[1].iov_len=strlen(name);
       iov[2].iov_base=buf;
       iov[2].iov_len=strlen(buf)+1;
       len=iov[0].iov_len+iov[1].iov_len+iov[2].iov_len;
       if(writev(fd[0],&iov[0],3)!=len)
              printf("writev error");

       return(recv_fd(fd[0],write));
}

int recv_fd(int fd,ssize_t (*userfunc)(int,const void *,size_t))
{
       int newfd,nr,status;
       char * ptr;
       char buf[MAXLINE];
       struct iovec iov[1];
       struct msghdr msg;
       status=-1;
       for(;;)
       {
              iov[0].iov_base=buf;
              iov[0].iov_len=sizeof(buf);
              msg.msg_iov=iov;
              msg.msg_iovlen=1;
              msg.msg_name=NULL;
              msg.msg_namelen=0;
              if(cmptr==NULL && (cmptr=malloc(CONTROLLEN))==NULL)
                     return(-1);
              msg.msg_control=cmptr;
              msg.msg_controllen=CONTROLLEN;
              if((nr=recvmsg(fd,&msg,0))<0)
              {
                     printf("recvmsg error");
              }
              else if(nr==0)
              {
                     printf("connection closed by server");
                     return(-1);
              }

              for(ptr=buf;ptr<&buf[nr];)
              {
                     if(*ptr++==0)
                     {
                            if(ptr!=&buf[nr-1])
                                   printf("message format error");
                            status=*ptr & 0xff;
                            if(status==0)
                            {
                                   if(msg.msg_controllen!=CONTROLLEN)
                                          printf("status=0 but no fd");
                                   newfd=*(int *)CMSG_DATA(cmptr);
                            }
                            else
                            {
                                   newfd=-status;
                            }
                            nr-=2;
                     }

              }
              if(nr>0 && (*userfunc)(STDERR_FILENO,buf,nr)!=nr)
                     return(-1);
              if(status>=0)
                     return(newfd);
       }
}

     
/*server.c 
* gcc -Wall server.c -o server
*/
#include<stdio.h>
#include<stdlib.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<errno.h>
//#include"error.c"

#define MAXLINE 200
#define CL_OPEN "open"
#define MAXARGC 50
#define WHITE " "
#define CONTROLLEN CMSG_LEN(sizeof(int))

char errmsg[4096];
int oflag;
char * pathname;
static struct cmsghdr * cmptr=NULL;

int cli_args(int,char **);
void request(char *,int,int);
int send_fd(int fd,int fd_to_send);
ssize_t writen(int fd,const void * ptr,size_t n);
int send_err(int fd,int errcode,const char * msg);
int buf_args(char * buf,int (*optfunc)(int,char **));


int main(int argc,char * argv[])
{
       printf("begin into server\n");
       int nread;
       char buf[MAXLINE];
       for(;;)
       {
              if((nread=read(STDIN_FILENO,buf,MAXLINE))<0)
                     printf("read error on stream pipe");
              else if(nread==0)
                     break;
              printf("server buf :%s\n",buf);
              request(buf,nread,STDOUT_FILENO);
       }

       exit(0);
}

void request(char * buf,int nread,int fd)
{
     write(STDOUT_FILENO,buf,strlen(buf)); 
     int newfd;
       if(buf[nread-1]!=0)
       {
              sprintf(errmsg,"request not null terminated:%*.*s\n",nread,nread,buf);
              send_err(fd,-1,errmsg);
              return;
       }

       if(buf_args(buf,cli_args)<0)
       {
              send_err(fd,-1,errmsg);
              return;
       }
       if((newfd=open(pathname,oflag))<0)
       {
              sprintf(errmsg,"can't open %s:%s\n",pathname,strerror(errno));
              send_err(fd,-1,errmsg);
              return;
       }

       if(send_fd(fd,newfd)<0)
              printf("send_fd error");

       close(newfd);
}

int buf_args(char * buf,int (*optfunc)(int,char **))
{
       char * ptr,* argv[MAXARGC];
       int argc;

       if((ptr=strtok(buf,WHITE))==NULL)
              return(-1);

       argv[argc=0]=buf;
       while((ptr=strtok(NULL," "))!=NULL)
       {
              if(++argc>=MAXARGC-1)
                     return(-1);
              argv[argc]=ptr;
       }
       argv[++argc]=NULL;

       return((*optfunc)(argc,argv));
}

int cli_args(int argc,char ** argv)
{
       if(argc!=3 || strcmp(argv[0],CL_OPEN)!=0)
       {
              strcpy(errmsg,"cli_args usage:<pathname><oflag>\n\n");
              return(-1);
       }
       pathname=argv[1];
       oflag=atoi(argv[2]);
       return(0);
}



int send_fd(int fd,int fd_to_send)
{
       struct iovec iov[1];
       struct msghdr msg;
       char buf[2];

       iov[0].iov_base=buf;
       iov[0].iov_len=2;
       msg.msg_iov=iov;
       msg.msg_iovlen=1;
       msg.msg_name=NULL;
       msg.msg_namelen=0;
       if(fd_to_send<0)
       {
              msg.msg_control=NULL;
              msg.msg_controllen=0;
              buf[1]=-fd_to_send;
              if(buf[1]==0)
                     buf[1]=1;
       }
       else
       {
              if(cmptr==NULL && (cmptr=malloc(CONTROLLEN))==NULL)
                     return(-1);

              cmptr->cmsg_level=SOL_SOCKET;
              cmptr->cmsg_type=SCM_RIGHTS;
              cmptr->cmsg_len=CONTROLLEN;
              msg.msg_control=cmptr;
              msg.msg_controllen=CONTROLLEN;
              *(int *)CMSG_DATA(cmptr)=fd_to_send;
              buf[1]=0;
       }

       buf[0]=0;
       if(sendmsg(fd,&msg,0)!=2)
              return(-1);

       return(0);
}

int send_err(int fd,int errcode,const char * msg)
{
       int n;

       if((n=strlen(msg))>0)
              if(writen(fd,msg,n)!=n)
                     return(-1);

       if(errcode>=0)
              errcode=-1;

       if(send_fd(fd,errcode)<0)
              return(-1);

       return(0);
}



ssize_t writen(int fd,const void * ptr,size_t n)
{
size_t nleft;
ssize_t nwritten;

nleft=n;
while(nleft>0)
{
       if((nwritten=write(fd,ptr,nleft))<0)
       {
              if(nleft==n)
                     return(-1);
              else
                     break;
       }
       else if(nwritten==0)
              break;
       nleft-=nwritten;
       ptr+=nwritten;
}

return(n-nleft);
}     

运行:$./client 
           输入文件路径 比如 open /tmp/test.txt

关于17.4~17.5小结
17.4 传送文件描述符
 讲述了如何构建struct msghdr 以及通过sendmsg和recvmsg发送和接收消息

17.5 服务器版本1
讲述了如何利用socketpair创建一对无名管道,然后通过sendmsg由服务器进程发送文件的fd给客户端进程,客户端进程通过recvmsg接收服务器进程发来的fd。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值