博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Unix环境高级编程(十八)高级进程间通信
阅读量:7240 次
发布时间:2019-06-29

本文共 9992 字,大约阅读时间需要 33 分钟。

  本章主要介绍了基于STREAM的管道和UNIX域套接字,这些IPC可以在进程间传送打开文件描述符。服务进程可以使用它们的打开文件描述符与指定的名字相关联,客户进程可以使用这些名字与服务器进程通信。

1、基于STREAMS的管道

  STREAMS pipe是一个双向(全双工)管道,单个STREAMS管道就能向父、子进程提供双向的数据流。如下图所示:

下面采用STREAMS管道实现加法协同进程实例,程序如下:

1  1 #include 
2 2 #include
3 3 #include
4 4 #include
5 5 #include
6 6 #include
7 7 8 8 #define MAXLINE 1024 9 9 10 10 static void sig_pipe(int signo)11 11 {12 12 printf("SIGPIPE caught\n");13 13 exit(1);14 14 }15 15 int s_pipe(int fd[2])16 16 {17 17 return pipe(fd);18 18 }19 19 int main()20 20 {21 21 int n;22 22 int fd[2];23 23 pid_t pid;24 24 char line[MAXLINE];25 25 26 26 signal(SIGPIPE,sig_pipe);27 27 s_pipe(fd);28 28 if((pid = fork()) == -1)29 29 {30 30 perror("fork() error");31 31 exit(-1);32 32 }33 33 if(pid == 0) //子进程用fd[1]34 34 {35 35 close(fd[0]);36 36 dup2(fd[1],STDIN_FILENO);37 37 dup2(fd[1],STDOUT_FILENO);38 38 execl(".//add","add",(char *)0);39 39 exit(0);40 40 }41 41 else //父进程用fd[0]42 42 {43 43 close(fd[1]);44 44 while(fgets(line,MAXLINE,stdin) != NULL)45 45 {46 46 n = strlen(line);47 47 if(write(fd[0],line,n) != n)48 48 {49 49 perror("write() error to pipe");50 50 exit(-1);51 51 }52 52 if((n =read(fd[0],line,MAXLINE)) < 0)53 53 {54 54 perror("read() error to pipe");55 55 exit(-1);56 56 }57 57 if(n==0)58 58 {59 59 printf("child close pipe\n");60 60 break;61 61 }62 62 line[n] = '\0';63 63 fputs(line,stdout);64 64 }65 65 exit(0);66 66 }67 67 }

 在APUE2 P469中这样讲:“solaris支持STREAMS管道,Linux的可选附加包也提供了STREAMS管道。”这个包没有安装上,程序不能正常运行。

1.2命名的STREAMS管道

  管道仅在相关进程之间使用,例如子进程集成父进程的管道。无关进程可以使用FIFO进行通信,但是这仅仅提供单向通信。STREAMS机制提供了一种有效的途径,使得进程可以给予管道一个文件系统的名字,避免了单向FIFO的问题。操作函数原型如下:

#include <stropts.h>

int fattach(int fildes, const char *path);  //给STREAMS管道一个系统文件中的名字
int fdetach(const char *path); //撤销STREAMS管道文件与文件系统中名字的关联

2、UNIX域套接字

  UNIX域套接字用于在同一台机器上运行的进程之间的通信,UNIX域套接字仅仅复制数据,不执行协议处理,不需要添加或删除网络报头,无需计算校验和,不要产生顺序号,无需发送确认报文。UNIX域套接字是套接字和管道之间的结合物,提供流和数据报两种接口。

UINX域套接字的好处:

(1)在同一台主机上进行通信时,是不同主机间通信的两倍

(2)UINX域套接口可以在同一台主机上,不同进程之间传递套接字描述符

(3)UINX域套接字可以向服务器提供客户的凭证(用户id或者用户组id)

UINX域套接字使用的地址通常是文件系统中一个文件路径,这些文件不是不同的文件,只能作为域套接字通信,不能读写。创建函数如下:

#include <sys/socket.h>

int socketpair(int domain, int type, int protocolint " sv [2]);

利用UNIX域实现全双工管道,程序如下:

1 #include 
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #define MAXLINE 1024 9 10 static void sig_pipe(int signo)11 {12 printf("SIGPIPE caught\n");13 exit(1);14 }15 int s_pipe(int fd[2])16 {17 return socketpair(AF_UNIX,SOCK_STREAM,0,fd); //创建UNIX域套接字18 }19 int main()20 {21 int n;22 int fd[2];23 pid_t pid;24 char line[MAXLINE];25 26 signal(SIGPIPE,sig_pipe);27 s_pipe(fd);28 if((pid = fork()) == -1)29 {30 perror("fork() error");31 exit(-1);32 }33 if(pid == 0)34 {35 close(fd[0]);36 dup2(fd[1],STDIN_FILENO);37 dup2(fd[1],STDOUT_FILENO);38 execl(".//add","add",(char *)0);39 exit(0);40 }41 else42 {43 close(fd[1]);44 while(fgets(line,MAXLINE,stdin) != NULL)45 {46 n = strlen(line);47 if(write(fd[0],line,n) != n)48 {49 perror("write() error to pipe");50 exit(-1);51 }52 if((n =read(fd[0],line,MAXLINE)) < 0)53 {54 perror("read() error to pipe");55 exit(-1);56 }57 if(n==0)58 {59 printf("child close pipe\n");60 break;61 }62 line[n] = '\0';63 fputs(line,stdout);64 }65 exit(0);66 }67 }

add是单独的程序,共程序调用,执行结果如下:

2.1命令UNIX域套接字

  UNIX域套接字的地址有sockaddr_run结构表示。

  #define UNIX_PATH_MAX 108

  struct sockaddr_un {
    sa_family_t sun_family; /* AF_UNIX */
    char sun_path[UNIX_PATH_MAX]; /* pathname };

写个程序,将一个地址绑定一UNIX域套接字,程序如下:

#include 
#include
#include
#include
#include
#include
#include
int main(){ int fd,size; struct sockaddr_un un; un.sun_family = AF_UNIX; strcpy(un.sun_path,"foo.socket"); fd = socket(AF_UNIX,SOCK_STREAM,0); size = offsetof(struct sockaddr_un,sun_path) + strlen(un.sun_path); if(bind(fd,(struct sockaddr*)&un,size)<0) { perror("bind() error"); exit(-1); } printf("UNIX domain socket bound\n"); exit(0);}

程序执行结果如下:

从结果可以看出,如果绑定地址时候,文件已经存在,那么bind请求将会失败,关闭套接字时,并不删除该文件。因此必须保证在应用程序终止前,对该文件进行解除链接操作。

2.2唯一连接

  服务器进程可以使用标准bind、listen和accept函数为客户进程安排一个唯一的UNIX域连接,客户进程使用connect与服务器进程联系,服务器进程接受了connect请求后,在服务器进程和客户进程之间就存在了唯一的连接。

参考

 

 

 

unix域套接字

unix域套接字实际上不是一个实际的协议,他只是在同一台主机上客户和服务器之间通信时,使用与在不同主机上客户和服务器间通信时相同的API   

unix域套接字分为两种:字节流套接字和数据报套接字

unix域套接字的好处:

1 在同一台主机上进行通信时,是不同主机间通信的两倍

2 unix域套接口可以在同一台主机上,不同进程之间传递套接字描述符

3 unix域套接字可以向服务器提供客户的凭证(用户id或者用户组id)

unix域套接字使用的地址通常是文件系统中一个文件路径(套接口文件:APUE中的4.3节文件类型,是以s开头的),这些文件不是不同的文件,只能作为域套接字通信,不能读写

并且是以s开头的文件

unix域套接字的地址结构是:

[cpp]
  1. struct sockaddr_un  
  2. {  
  3.     uint8_t sun_len;  
  4.     sa_family_sun_family;//AF_LOCAL  
  5.     char sun_path[104];//必须是以空结尾的字符串(路径+文件名)  
  6. }  

int socketpair(int family, int type, intprotocol, int sockfd[2]);

这个函数创建两个互相连接的套接字(socketfd[2])family 是AF_LOCAL, type可以是SOCK_STREAM (字节流)或者SOCK_DGRAM(数据报),协议是0,之后就能够或者两个互相连接的套接字

以SOCK_STREAM调用的socketpair函数得到的套接字叫做流管道(stream pipe),是全双工的,就是这两个套接字是可读可写的

1在unix域套接字进行bind的时候建立套接口文件,其默认的权限值是0777,并被当前的umask修改,看上图就知道,umask是0022 的到的文件权限是0755

2关于bind创建文件中地址参数 sockaddr_un 中的sun_path需要是绝对路径,这样才能不用考虑相对的概念。防止客户端程序也用相对路径,但是和服务器不在同一个目录的考虑

3 connect中的地址中的路径名必须是套接口文件,而且已经被服务端绑定的,以下情况会出错:1 文件路径存在但是不是套接口文件2 路径名存在且是套接口文件,但是没有和该文件绑定的套接口3 就是type必须和服务器相同

4 connect连接unix套接口的时候的权限检查和open函数一样的

5 unix域字节流套接口和TCP一样都给进程提供一个无记录边界的字节流接口

6 unix域套接口connect发现等待队列满了,就直接返回ECONNREFUSRD错误,说连接拒绝错误

7 unix域数据报套接口和UDP一样提供一个保留记录边界的不可靠数据报服务

unix域套接字的客户端:

[cpp]
  1. #include <sys/un.h>  
  2. #include <stdio.h>  
  3. #include <errno.h>  
  4. #include <stdlib.h>  
  5. #include <sys/types.h>  
  6. #include <sys/socket.h>  
  7.   
  8. #define MAX_SEND 1025  
  9. #define UNIX_PATH "/tmp/sinfor"  
  10.   
  11. void dump_unix(int sock_fd)  
  12. {  
  13.     char tmp[MAX_SEND] = {0};  
  14.     char recv[MAX_SEND] = {0};  
  15.     while(fgets(tmp, MAX_SEND, stdin) != NULL)  
  16.     {  
  17.         write(sock_fd, tmp, strlen(tmp));  
  18.         read(sock_fd, recv, MAX_SEND);  
  19.         printf("data : %s\n", recv);  
  20.         bzero(tmp,MAX_SEND);  
  21.         bzero(recv, MAX_SEND);  
  22.     }  
  23. }  
  24. int main(int argc, char** argv)  
  25. {  
  26.     int conn_sock = socket(AF_LOCAL, SOCK_STREAM, 0);  
  27.     if(conn_sock == -1)  
  28.     {  
  29.         perror("socket fail ");  
  30.         return -1;  
  31.     }  
  32.     struct sockaddr_un addr;  
  33.     bzero(&addr, sizeof(addr));  
  34.     addr.sun_family = AF_LOCAL;  
  35.     strcpy((void*)&addr.sun_path, UNIX_PATH);  
  36.     if(connect(conn_sock, (struct sockaddr*)&addr, sizeof(addr)) < 0)  
  37.     {  
  38.         perror("connect fail ");  
  39.         return -1;  
  40.     }  
  41.     dump_unix(conn_sock);  
  42.     close(conn_sock);  
  43.     return 0;  
  44. }  

unix域套接字的服务器;

[cpp]
  1. #include <sys/un.h>  
  2. #include <stdio.h>  
  3. #include <signal.h>  
  4. #include <errno.h>  
  5. #include <stdlib.h>  
  6. #include <sys/types.h>  
  7. #include <sys/wait.h>  
  8. #include <sys/socket.h>  
  9.   
  10. #define MAX_RECV 1025  
  11. #define UNIX_SERV_PATH "/tmp/sinfor"  
  12. void client_dump(int sock_fd)  
  13. {  
  14.     char rec[MAX_RECV] = {0};  
  15.     int size;  
  16.     while((size = read(sock_fd, rec, MAX_RECV)) != 0)  
  17.     {  
  18.         printf("**********1111************\n");  
  19.         printf("recv data is %s\n", rec);  
  20.         write(sock_fd, rec, size);  
  21.     }  
  22. }  
  23. void sig_son(int num)  
  24. {  
  25.     printf("son is %d\n", getpid());  
  26.     wait(NULL);  
  27.     //return NULL;  
  28. }  
  29. int main(int argc, char** argv)  
  30. {  
  31.     int acc_sock, dump_sock;  
  32.     acc_sock = socket(AF_LOCAL, SOCK_STREAM, 0);  
  33.     if(acc_sock == -1)  
  34.     {  
  35.         perror("socket func fail ");  
  36.         return -1;  
  37.     }  
  38.     struct sockaddr_un ser_addr, cli_addr;  
  39.     ser_addr.sun_family = AF_LOCAL;  
  40.     strcpy(ser_addr.sun_path, UNIX_SERV_PATH);  
  41.     unlink(UNIX_SERV_PATH);  
  42.     bind(acc_sock, (struct sockaddr*)&ser_addr, sizeof(ser_addr));  
  43.     listen(acc_sock, 5);  
  44.     signal(SIGCHLD, sig_son);  
  45.     while(1)  
  46.     {  
  47.         int len = sizeof(cli_addr);  
  48.         dump_sock = accept(acc_sock, (struct sockaddr*)&cli_addr, &len);  
  49.         if(dump_sock == -1)  
  50.         {  
  51.             if(errno == EINTR)  
  52.             {  
  53.                 continue;  
  54.             }  
  55.             else  
  56.             {  
  57.                 perror("accept fail");  
  58.                 return -1;  
  59.             }  
  60.         }  
  61.         if(fork() == 0)  
  62.         {  
  63.             close(acc_sock);  
  64.             client_dump(dump_sock);       
  65.             close(dump_sock);  
  66.             exit(0);  
  67.         }  
  68.         close(dump_sock);  
  69.     }  
  70.     close(acc_sock);  
  71.     return 0;  
  72. }  

 

 

  1. /*
  2. domain_socket.h
  3. @Author: duanjigang @2006-4-11
  4. @Desp: declaratin of methods used for unix-domain-socket communication
  5. */
  6. #ifndef _H_
  7. #define _H_
  8. #include <stdio.h>
  9. #include <unistd.h>
  10. #include <sys/un.h>
  11. #include <sys/socket.h>
  12. #define MSG_SIZE 1024
  13. int init_send_socket(struct sockaddr_un * addr,char * path)
  14. {
  15.         int sockfd,len;
  16.         sockfd=socket(AF_UNIX,SOCK_DGRAM,0);
  17.         if(sockfd<0)
  18.         {
  19.                 exit(1);
  20.         }
  21.         bzero(addr,sizeof(struct sockaddr_un));
  22.         addr->sun_family=AF_UNIX;
  23.         strcpy(addr->sun_path,path);
  24.         return sockfd;
  25. }
  26. int init_recv_socket(char * path)
  27. {
  28.         int sockfd,len;
  29.         struct sockaddr_un addr;
  30.         sockfd=socket(AF_UNIX,SOCK_DGRAM,0);
  31.         if(sockfd<0)
  32.         {
  33.            return -1;
  34.         }
  35.         bzero(&addr,sizeof(struct sockaddr_un));
  36.         addr.sun_family = AF_UNIX;
  37.         strcpy(addr.sun_path, path);
  38.         unlink(path);
  39.         len = strlen(addr.sun_path) + sizeof(addr.sun_family);
  40.         if(bind(sockfd,(struct sockaddr *)&addr,len)<0)
  41.          {
  42.           return -1;
  43.          }
  44.         return sockfd;
  45. }
  46. int receive_from_socket(int sockfd, char msg[])
  47. {
  48.       int n;
  49.       memset(msg, 0, MSG_SIZE);
  50.       n=recvfrom(sockfd, msg, MSG_SIZE, 0, NULL, NULL);
  51.       if(n<=0)
  52.       {
  53.        return -1;
  54.       }
  55.       msg[n]=0;
  56.       return n;
  57. }
  58. int send_to_socket(int sockfd, char msg[], const struct sockaddr_un * addr)
  59. {
  60.         int len;
  61.         len = strlen(addr->sun_path)+sizeof(addr->sun_family);
  62.         sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr*)addr,len);
  63.         return 1;
  64. }
  65. #endif
 
一个调用的例子
  1. /*
  2. main.c
  3. @Author: duanjigang @ 2006-4-11
  4. @Desp: Two processes communicate with unix domain socket
  5. */
  6. #include "domain_socket.h"
  7. #define PATH "/home/useless"
  8. /*
  9. 进程间通过域进行通讯-举例:父子进程,一个发送,一个接收
  10. */
  11. int main(void)
  12. {
  13.   int pid;
  14.   /*
  15.   子进程用于发送消息
  16.   */
  17.   if((pid = fork()) == 0)
  18.   {
  19.     int fd, counter = 0;
  20.     char send_buffer[MSG_SIZE];
  21.     struct sockaddr_un addr;
  22.    if( (fd = init_send_socket(&addr, PATH)) > 0)
  23.     while(1)
  24.     {
  25.        memset(send_buffer, 0 , MSG_SIZE);
  26.        /*
  27.            防止计数器越界,所以做一个复位判断
  28.            */
  29.            sprintf(send_buffer,"message for %d times",
  30.        counter++ >= 10000 ? 1 : counter);
  31.        send_to_socket(fd, send_buffer, &addr);
  32.        printf("Sender: %s\n", send_buffer);
  33.        sleep(1);
  34.     }
  35.   }/*
  36.    父进程用于接收消息
  37.   */
  38.   else
  39.   {
  40.       int fd;
  41.       char recv_buffer[MSG_SIZE];
  42.       if( (fd = init_recv_socket(PATH))> 0)
  43.       while(1)
  44.       {
  45.        memset(recv_buffer, 0, MSG_SIZE);
  46.        if(receive_from_socket(fd, recv_buffer))
  47.        {
  48.         printf("Receiver: %s\n", recv_buffer);
  49.        }
  50.       }
  51.   }
  52. }

转载于:https://www.cnblogs.com/alantu2018/p/8466190.html

你可能感兴趣的文章
troubleshooting shuffle reduce端缓冲大小以避免OOM
查看>>
全球1.7万台Mac电脑感染新恶意软件iWorm
查看>>
SpringMVC bean validator 自定义注解
查看>>
每周跑步锻炼
查看>>
Java反射:object is not an instance of declaring c...
查看>>
GIT 远程仓库:添加远程库、从远程库克隆
查看>>
用树莓派构建你自己的微型服务器,可以外网访
查看>>
Maven简明教程(1)---下载与安装
查看>>
数据库密码爆破HexorBase
查看>>
Java 之享元模式
查看>>
邮件系统服务器搭建记录(五)(Postfix+Cyrus-sasl+Courier-authlib+Dovecot+ExtMail+MySQL)...
查看>>
Linux之fork与vfork区别
查看>>
Javascript中的Array
查看>>
我的友情链接
查看>>
中国之声
查看>>
如何在 Outlook 2007 / 2010 中統一管理不同帳號的電子郵件
查看>>
html中<radio>单选按钮控件标签用法解析及如何设置默认选中
查看>>
浪潮云上处理挖矿病毒
查看>>
npm 和 bower 的区别
查看>>
ipvsadm安装和配置
查看>>