【网络编程基础】Linux下进程通信方式(共享内存,管道,消息队列,Socket)

在网络课程中,有讲到Socket编程,对于tcp讲解的环节,为了加深理解,自己写了Linux下进程Socket通信,在学习的过程中,又接触到了其它的几种方式。记录一下。

管道通信(匿名,有名)

管道通信,在一个进程之中,只能单一的对其写或者是读,而不可以及执行写操作又执行读操作。这一点,我们可以将其想象成我们的水管,分别连着不同的两端,在有水流的时候,一端只能进行输入,另一端只能进行输出,而不可以同时输入和输出。

管道又分为有名管道和匿名管道,两者的区别在于对于匿名管道,其只能在具有亲缘关系的父子进程之间进行消息通信。管道的通信是借助于在我们的磁盘上生成一个文件,然后对文件的读写来实现我们的通信,而且数据被读之后,在文件中将会被删除掉。

匿名

匿名管道的实现较为简单,实现代码:

 1 #include <unistd.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <memory.h>
 5 #include <errno.h>
 6 
 7 #define BUFFER_SIZE 9
 8 
 9 
10 int main(int argc, char const *argv[])
11 {
12     int n;
13     int fd[2];
14     pid_t pid;
15     char sendBuffer[BUFFER_SIZE]="welcome!";
16     char receiveBuff[BUFFER_SIZE];
17     memset(receiveBuff,0,sizeof(receiveBuff));
18 
19     if (pipe(fd)==-1)
20     {
21         fprintf(stderr, "pipe:%s
",strerror(errno));
22         exit(1);
23     }
24 
25     if ((pid=fork())<0)
26     {
27         fprintf(stderr, "fork:%s
",strerror(errno));
28         exit(1);
29     }else if (pid>0)//parent process 
30     {
31         close(fd[0]);//close the read part
32         write(fd[1],sendBuffer,BUFFER_SIZE);
33         printf("parent process send:%s
", sendBuffer);
34     }else
35     {
36         close(fd[1]);//close the write part
37         read(fd[0],receiveBuff,BUFFER_SIZE); 
38         printf("child process receive:%s
",receiveBuff);
39     }
40     return 0;
41 }
42 
43 /*
44 [Test]
45 gcc -o pipe pipe.c 
46 ./pipe 
47 parent process send:welcome!
48 child process receive:welcome
49 */

1.创建管道2.通过fork函数分叉出子进程3.写数据4.读数据5.关闭管道读端和写端。这里也除了管道函数,还要特别说一下的是fork函数。

fork函数返回的数据在父进程中是子进程pid,而对子进程返回的是0,这并不是说,子进程的pid就是零,还有一个函数是getpid(),执行这个函数后,在子进程和父进程中得到的都是该进程pid,而不是fork函数的返回值。fork函数执行的时候,是将父进程中全部的数据置入自己进程之中,和父进程中的数据不是同步的了。

通过pipe函数我们创建了一个管道,管道接收的参数是一个长度为2的一维数组,此时,此时在0位返回的是读端口,1位返回的是写端口,当我们要对数据进行写入的时候,我们需要关闭其读端口,如果对其进行读操作,我们要关闭写端口。类似于读文件的操作,制定数据和大小,然后通过read和write函数对其进行读写。

有名

通过对于匿名管道的分析,再到有名管道,为什么有名管道可以被非亲缘进程找到利用?因为它有名呀,对的,如果在家中,父亲要和儿子谈话,只需说出来就好了,因为信道上的接听者只有父亲和儿子,所以即使不指儿子的名字和儿子说,儿子也是知道是在和他讲,但是如果要和别人讲话,而且由很多人同时在侦听信道的时候,如果我们不指定名字,他们就不会知道我们是在跟谁讲话。所以对于有名管道,我们首先要对其指定一个名字,然后指定作为写端或者是读端,然后对其进行操作。

 1 #include <unistd.h>
 2 #include <fcntl.h>
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5 #include <limits.h>
 6 #include <sys/stat.h>
 7 #include <sys/types.h>
 8 #include <errno.h>
 9 
10 #define FIFO_NAME "/home/louxj/workspace/networking/ipc/myfifo"
11 #define PIPE_BUFF 12
12 
13 int main(int argc, char const *argv[])
14 {
15     int res=0;
16     int pipe_id;
17     char buffer[PIPE_BUFF]="hello,fifo";
18 
19     if (access(FIFO_NAME,F_OK)==0)//test wether the file exits
20     {
21         if ((res=mkfifo(FIFO_NAME,0777)))!=0)
22         {
23             fprintf(stderr, "mkfifo:%s
", strerror(errno));
24         }
25         if ((pipe_id=open(FIFO_NAME,O_RDWR))!=-1)
26         {
27             if (write(pipe_id,buffer,sizeof(buffer)))
28             {
29                 printf("write success
");
30                 close(pipe_id);
31             }else
32             {
33                 fprintf(stderr, "write:%s
",strerror(errno));
34                 exit(1);
35             }
36         }else
37         {
38             fprintf(stderr, "open:%s
",strerror(errno));
39         }
40     }
41     return 0;
42 }

因为管道是通过本地磁盘上的文件进行信息的交换,因此我们需要给予其本地磁盘上的一个文件目录,然后根据该目录通过access()函数来获取该管道,如果获取失败,那么我们就按照给定的目录创建一个,创建管道的时候,我们需要制定是对其进行读还是写,创建好之后,通过指定模式打开我们的管道,此时会得到一个管道号,然后根据获得管道标志号,作为参数,进行read和write操作。下面是读端的执行代码。

 1 #include <unistd.h>
 2 #include <fcntl.h>
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5 #include <limits.h>
 6 #include <sys/stat.h>
 7 #include <sys/types.h>
 8 #include <errno.h>
 9 
10 #define FIFO_NAME "/home/louxj/workspace/networking/ipc/myfifo"
11 #define PIPE_BUFF 12
12 
13 int main(int argc, char const *argv[])
14 {
15     char buffer[PIPE_BUFF];
16     int pipe_id;
17     int size = sizeof(buffer);
18     
19     if ((pipe_id=open(FIFO_NAME,O_RDWR))!=-1)
20     {
21         read(pipe_id,(void *)buffer,size);
22         printf("%s
",(char *)buffer);
23     }else
24     {
25         fprintf(stderr, "open:%s
", strerror(errno));
26         exit(1);
27     }
28     return 0;
29 }

Socket通信

Socket通信,不仅仅是一台主机上的两个进程可以进行通信,还可以让处在因特网中的两个进程进行通信。在两个进程进行通信的时候,首先本地的进程在运行的时候会绑定一个端口,然后我们本地为该进程生成一个缓冲区,返回一个值,即为socket作为对其进行标记,每当本地进程和远程一个进程建立连接的时候,就会根据远程进程的信息和本地进程的信息生成一个socket,然后双方借助于socket就可以进行通信,运输层得到的数据写入socket标志的缓冲区,然后在里面进行相应的操作之后将其提交给网络层。相比其它的几种处理方式,该中方式比较麻烦。多于服务端,通过listen阻塞监听,监听到有连接请求,通过accept函数得到一个本地与之对应的缓冲区,然后创建一个进程用来和该连接进行交互,然后通过receive来接收信息,由于c语言大一学过去之后,基本没怎么再看过,所以写来的时候还是遇到了几个小坑。这里实现的是当连接建立后,服务端给本地端发送一个连接建立提示,然后客户端可以向服务端发送消息,服务端给予一个I don't know的回复。

服务端代码

  1 /////////////////////////////////////////
  2 //   the server code for TCP socket    //
  3 /////////////////////////////////////////
  4 
  5 #include <netinet/in.h> //for sockaddr_in
  6 #include <sys/types.h>  //for socket
  7 #include <sys/socket.h> //for socket
  8 #include <stdio.h>      //for printf
  9 #include <stdlib.h>     //for exit
 10 #include <string.h>     //for memset
 11 #include <time.h>       //for time_t and time
 12 #include <fcntl.h>
 13 #include <errno.h>      //for errno
 14 
 15 #define SERVER_PORT 5790
 16 #define LENGTH_OF_QUEUE 20
 17 #define BUFFER_SIZE 1024
 18 
 19 int main(int argc, char const *argv[])
 20 {
 21     struct sockaddr_in server_addr;//define a universal socket struct
 22 
 23     bzero(&server_addr,sizeof(server_addr));//clear the memory to zero
 24     server_addr.sin_family=AF_INET;//IPv4 protocol
 25     server_addr.sin_addr.s_addr=htons(INADDR_ANY);//set the IP address with localhost IP address
 26     server_addr.sin_port=htons(SERVER_PORT);//set the nameed port
 27 
 28     //time_t now;
 29     
 30 
 31     //create the socket for the server
 32     int server_socket;
 33     if ((server_socket=socket(AF_INET,SOCK_STREAM,0))==-1)
 34     {
 35         fprintf(stderr, "socket:%s
", strerror(errno));
 36         exit(1);
 37     }
 38 
 39     //bind the socket with the socket address
 40     if ((bind(server_socket,(struct sockaddr*)&server_addr,sizeof(server_addr)))==-1)
 41     {
 42         fprintf(stderr, "bind:%s
", strerror(errno));
 43         exit(1);
 44     }
 45 
 46     //set the server's socket to listen for the request from the clients
 47     if ((listen(server_socket,LENGTH_OF_QUEUE))==-1)
 48     {
 49         fprintf(stderr, "listen:%s
", strerror(errno));
 50         exit(1);
 51     }
 52 
 53     //set the server running all the time
 54     while(1)
 55     {
 56         struct sockaddr_in client_addr;//define a socket struct for each client
 57         socklen_t length=sizeof(client_addr);
 58         int new_server_socket;
 59         if ((new_server_socket=accept(server_socket,(struct sockaddr*)&client_addr,&length))<0)
 60         {
 61             fprintf(stderr, "accept:%s
", strerror(errno));
 62             break;
 63         }
 64 
 65         //set the notice info to send to the client
 66         char buffer[BUFFER_SIZE];
 67         bzero(buffer,BUFFER_SIZE);
 68         strcpy(buffer,"Hello,coming from the server!");
 69         strcat(buffer,"
");
 70 
 71         send(new_server_socket,buffer,BUFFER_SIZE,0);
 72 
 73         bzero(buffer,BUFFER_SIZE);
 74 
 75         //receive the data to send to the client
 76         length=recv(new_server_socket,buffer,BUFFER_SIZE,0);
 77         if (length<0)
 78         {
 79             fprintf(stderr, "recv:%s
", strerror(errno));
 80             exit(1);
 81         }
 82         //display the data receive from the client
 83         printf("%s
",buffer);
 84 
 85         //send the local file stream to the client
 86         int stream;
 87         if ((stream=open("send_data",O_RDWR))==-1)
 88         {
 89             fprintf(stderr, "open:%s
", strerror(errno));
 90             exit(1);
 91         }else
 92         {
 93             printf("The file send_data was opened.
");
 94         }
 95          
 96 
 97         int lengthsize=0;
 98         int i=0;
 99         while((lengthsize=read(stream,buffer,sizeof(buffer)))>0)
100         {
101             printf("lengthsize=%d
",lengthsize);
102             //printf("%s
", buffer);
103             if (send(new_server_socket,buffer,lengthsize,0)<0)
104             {
105                 printf("send file failed!
");
106                 break;
107             }
108             else
109             {
110                 printf("the %d time to send...
",++i);
111             }
112             bzero(buffer,BUFFER_SIZE);
113         }
114         if (close(stream)==-1)
115         {
116             fprintf(stderr, "close%s
", strerror(errno));
117             exit(1);
118         }
119         close(new_server_socket);//close the connection with the client
120     }
121 
122     //close the socket for listening
123     close(server_socket);
124     return 0;
125 }
126 
127 /*
128 [Test]
129 gcc -o tcp_server tcp_server.c
130 ./tcp_server 
131 Hello,this is client A!
132 The file send_data was opened.
133 lengthsize=1024
134 the 1 time to send...
135 lengthsize=1024
136 the 2 time to send...
137 lengthsize=1024
138 the 3 time to send...
139 lengthsize=1024
140 the 4 time to send...
141 lengthsize=1024
142 the 5 time to send...
143 lengthsize=805
144 the 6 time to send...
145 */

其中涉及到创建进程与结束进程等还是比较复杂的。下面通过一个流程图说明其工作原理

对于其中的一些函数,有自己的英文注释,想锻炼下英文表达能力,但是表达在语法和意思上还是有些错误的。
客户端工作代码

  1 /////////////////////////////////////////
  2 //    the client code for TCP socket   //
  3 /////////////////////////////////////////
  4 
  5 #include <netinet/in.h> //for sockaddr_in
  6 #include <sys/types.h>  //for socket type
  7 #include <sys/socket.h> //for socket()
  8 #include <stdio.h>      //for printf
  9 #include <stdlib.h>     //for exit
 10 #include <string.h>     //for memset
 11 #include <time.h>       //for time_t and time
 12 #include <arpa/inet.h>  //for INTERNET definition
 13 #include <fcntl.h>
 14 #include <errno.h>      //for errno
 15 
 16 #define SERVER_PORT 5790
 17 #define BUFFER_SIZE 1024
 18 
 19 int main(int argc, char const *argv[])
 20 {
 21     if (argc!=2)
 22     {
 23         printf("Usage:%s ServerIPAddress
",argv[0]);
 24         exit(1);
 25     }
 26     
 27 
 28     struct sockaddr_in client_addr;//define a universal socket struct
 29     bzero(&client_addr,sizeof(client_addr));//clear the memory to zero
 30     client_addr.sin_family=AF_INET;//internet protocol family
 31     client_addr.sin_addr.s_addr=htons(INADDR_ANY);//INADDR_ANY represents the localhost ip address
 32     client_addr.sin_port=htons(0);//0 represents the system will automatically allocate a free port 
 33 
 34     int client_socket;
 35     if ((client_socket=socket(AF_INET,SOCK_STREAM,0))<0)
 36     {
 37         fprintf(stderr, "socket:%s
", strerror(errno));
 38         exit(1);
 39     }
 40 
 41     if (bind(client_socket,(struct sockaddr*)&client_addr,sizeof(client_addr))<0)
 42     {
 43         fprintf(stderr, "bind:%s
", strerror(errno));
 44         exit(1);
 45     }
 46 
 47     struct sockaddr_in server_addr;
 48     bzero(&server_addr,sizeof(server_addr));
 49     server_addr.sin_family=AF_INET;//IPv4 protocol
 50     if (inet_aton(argv[1],&server_addr.sin_addr)==0)//convert the input ip address 
 51     {
 52         fprintf(stderr, "server address:%s
", strerror(errno));
 53         exit(1);
 54     }
 55     server_addr.sin_port=htons(SERVER_PORT);
 56 
 57 
 58     socklen_t server_addr_lentth=sizeof(server_addr);
 59     if (connect(client_socket,(struct sockaddr*)&server_addr,server_addr_lentth)<0)
 60     {
 61         fprintf(stderr, "can't connect to:%s because that %s:
", argv[1],strerror(errno));
 62         exit(1);
 63     }
 64 
 65     char buffer[BUFFER_SIZE];
 66     bzero(buffer,BUFFER_SIZE);
 67 
 68     int length;
 69     if ((length=recv(client_socket,buffer,BUFFER_SIZE,0))<0)
 70     {
 71         fprintf(stderr, "recv:%s
", strerror(errno));
 72         exit(1);
 73     }
 74     printf("
%s
",buffer);
 75 
 76     bzero(buffer,BUFFER_SIZE);
 77 
 78     strcpy(buffer,"Hello,this is client A!
");
 79     send(client_socket,buffer,BUFFER_SIZE,0);
 80 
 81     int stream;
 82     if ((stream=open("receive_data",O_RDWR))==-1)
 83     {
 84         printf("The file data was not opened!
");
 85     }else
 86         bzero(buffer,BUFFER_SIZE);
 87 
 88     length=0;
 89     while(length=recv(client_socket,buffer,BUFFER_SIZE,0))
 90     {
 91         if (length<0)
 92         {
 93             printf("Receive data from server %s failed!
", argv[1]);
 94             break;
 95         }
 96 
 97         int write_lenth=write(stream,buffer,sizeof(buffer));
 98         if (write_lenth<length)
 99         {
100             printf("File write Failed!
");
101             break;
102         }
103         bzero(buffer,BUFFER_SIZE);
104     }
105     printf("Receive File From Server [%s] Finished.
",argv[1]);
106 
107     //close the file 
108     close(stream);
109 
110     //close the socket 
111     close(client_socket);
112     return 0;
113 }
114 
115 /*
116 [Test]
117 gcc -o tcp_client tcp_client.c
118 ./tcp_client 127.0.0.1
119 Hello,coming from the server!
120 Receive File From Server [127.0.0.1] Finished.
121 */

共享内存通信方式

一、什么是共享内存
  顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存。共享内存是在两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常安排为同一段物理内存。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址,就好像它们是由用C语言函数malloc分配的内存一样。而如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。
 
  特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问,例如前面说到的信号量。
 
二、共享内存的使用
与信号量一样,在Linux中也提供了一组函数接口用于使用共享内存,而且使用共享共存的接口还与信号量的非常相似,而且比使用信号量的接口来得简单。它们声明在头文件 sys/shm.h中。
 
1、shmget函数
该函数用来创建共享内存,它的原型为:
 
  1. int shmget(key_t key, size_t size, int shmflg);  
第一个参数,与信号量的semget函数一样,程序需要提供一个参数key(非0整数),它有效地为共享内存段命名,shmget函数成功时返回一个与key相关的共享内存标识符(非负整数),用于后续的共享内存函数。调用失败返回-1.
 
不相关的进程可以通过该函数的返回值访问同一共享内存,它代表程序可能要使用的某个资源,程序对所有共享内存的访问都是间接的,程序先通过调用shmget函数并提供一个键,再由系统生成一个相应的共享内存标识符(shmget函数的返回值),只有shmget函数才直接使用信号量键,所有其他的信号量函数使用由semget函数返回的信号量标识符。
 
第二个参数,size以字节为单位指定需要共享的内存容量
 
第三个参数,shmflg是权限标志,它的作用与open函数的mode参数一样,如果要想在key标识的共享内存不存在时,创建它的话,可以与IPC_CREAT做或操作。共享内存的权限标志与文件的读写权限一样,举例来说,0644,它表示允许一个进程创建的共享内存被内存创建者所拥有的进程向共享内存读取和写入数据,同时其他用户创建的进程只能读取共享内存。
 
2、shmat函数
第一次创建完共享内存时,它还不能被任何进程访问,shmat函数的作用就是用来启动对该共享内存的访问,并把共享内存连接到当前进程的地址空间。它的原型如下:
 
  1. void *shmat(int shm_id, const void *shm_addr, int shmflg);  
第一个参数,shm_id是由shmget函数返回的共享内存标识。
第二个参数,shm_addr指定共享内存连接到当前进程中的地址位置,通常为空,表示让系统来选择共享内存的地址。
第三个参数,shm_flg是一组标志位,通常为0。
 
调用成功时返回一个指向共享内存第一个字节的指针,如果调用失败返回-1.
 
3、shmdt函数
该函数用于将共享内存从当前进程中分离。注意,将共享内存分离并不是删除它,只是使该共享内存对当前进程不再可用。它的原型如下:
 
  1. int shmdt(const void *shmaddr);  
参数shmaddr是shmat函数返回的地址指针,调用成功时返回0,失败时返回-1.
 
4、shmctl函数
与信号量的semctl函数一样,用来控制共享内存,它的原型如下:
 
  1. int shmctl(int shm_id, int command, struct shmid_ds *buf);  
第一个参数,shm_id是shmget函数返回的共享内存标识符。
 
第二个参数,command是要采取的操作,它可以取下面的三个值 :
    IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。
    IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值
    IPC_RMID:删除共享内存段
 
第三个参数,buf是一个结构指针,它指向共享内存模式和访问权限的结构。
shmid_ds结构至少包括以下成员:
 
 
  1. struct shmid_ds  
  2. {  
  3.     uid_t shm_perm.uid;  
  4.     uid_t shm_perm.gid;  
  5.     mode_t shm_perm.mode;  
  6. };  

共享内存的通信方式,系统根据我的偏好设置在内存中开辟一块空间,并对其进行相应的指定,然后我们的另一个进程可以按照同样的指定,也就是其标记,对其进行访问。创建共享内存,得到共享内存后,想内存中写入数据,然后另个进程得到内存,然后从中读出数据。

写入方代码实现:

 1 #include <unistd.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <sys/shm.h>
 5 #include <sys/ipc.h>
 6 #include <string.h>
 7 #include <errno.h>
 8 
 9 #define PATH "/tmp"
10 #define SIZE 1024
11 #define ID 0
12 
13 int main(int argc, char const *argv[])
14 {
15     void *shmAddr=NULL;
16     char dataAddr[]="Hello";
17     key_t key=ftok(PATH,ID);//create an unique key for the current IPC
18     
19     //create a shared memory area in the current process memory
20     int shmid;
21     if ((shmid=shmget(key,SIZE,0666|IPC_CREAT))==-1)
22     {
23         fprintf(stderr, "shmget:%s
", strerror(errno));
24         exit(1);
25     }
26 
27 
28 
29     shmAddr=shmat(shmid,(void*)0,0);//map the shared memory to the process memory
30     if(shmAddr==(void*)-1)
31     {
32         fprintf(stderr, "shmat:%s
", strerror(errno));
33     }    
34     strcpy(shmAddr,dataAddr);//copy the dataAddr to shmAddr
35     
36 
37 
38 
39     //disconnect the process memory with the shared memory
40     if (shmdt(shmAddr)==-1)
41     {
42         fprintf(stderr, "shmdt:%s
", strerror(errno));
43     }
44     return 0;
45 }
46 
47 /*
48 [Test]
49 gcc -o shm_write shm_write.c
50 ./shm_write
51 */

读取方代码实现:

 1 #include <unistd.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <sys/shm.h>
 5 #include <sys/ipc.h>
 6 #include <string.h>
 7 #include <errno.h>
 8 
 9 #define PATH "/tmp"
10 #define SIZE 1024
11 #define ID 0
12 
13 int main(int argc, char const *argv[])
14 {
15     char * shmAddr;
16     char * dataAddr="world";
17     key_t key=ftok(PATH,ID);
18 
19     //create a shared memory area int the current pprocess memory
20     int shmid;
21     if ((shmid=shmget((key_t)key,SIZE,0666|IPC_CREAT))==-1)
22     {
23         fprintf(stderr, "shmget:%s
", strerror(errno));
24         exit(1);
25     }
26 
27 
28     shmAddr=shmat(shmid,(void*)0,0);//map the shared memory to the process memory
29     if(shmAddr==(void*)-1)
30     {
31         fprintf(stderr, "shmat:%s
", strerror(errno));
32     }    
33 
34     printf("%s
",shmAddr);
35 
36 
37     shmdt(shmAddr);
38     shmctl(shmid,IPC_RMID,NULL);
39 
40     return 0;
41 }
42 
43 /*
44 [Test]
45 gcc -o shm_read shm_read.c
46 ./shm_read
47 Hello
48 */


 

消息队列通信

消息队列和有名管道有些类似的地方,最大相同点在于它可以用于在不同的进程之间进行通信,但是管道有一个劣势是,对于接收端其对与管道内的数据只能是接受,而不可以对其进行过滤选择,同时在写和读的时候还会出现堵塞.对于消息队列其在接收的时候也是会发生堵塞的,解除阻塞,只有当其接收到合适的消息或者该队列被删除了,这个阻塞才会解除。对于消息队列的通信流程是创建消息-》获得队列--》向队列中发送消息--》从队列中取出消息。

接收端实现代码:

 1 #include <unistd.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <string.h>
 5 #include <errno.h>
 6 #include <sys/stat.h>
 7 #include <sys/types.h>
 8 #include <sys/ipc.h>
 9 #include <sys/msg.h>
10 
11 #define MSG_FILE "/tmp"       //a pathname for generating a unique key
12 #define BUFFER 512            //set the buffer size to 255 bytes
13 #define PERM S_IRUSR|S_IWUSR  //allow the user to read and write
14 
15 struct msgbuffer
16 {    
17     long mtype;
18     char mtext[BUFFER+1];
19 };
20 typedef struct msgbuffer msgbuf;
21 
22 int main(int argc, char const *argv[])
23 {
24     //create a unique key
25     key_t key;    
26     if ((key=ftok(MSG_FILE,BUFFER))==-1)
27     {
28          fprintf(stderr, "ftok:%s
", strerror(errno));
29          exit(1);
30     }else
31     {
32         printf("generate a key=%d
", key);
33     }
34 
35     //get a message queue 
36     int msgid;    
37     msgbuf msg;
38     if ((msgid=msgget(key,PERM|IPC_CREAT))==-1)
39     {
40         fprintf(stderr, "msgget:%s
", strerror(errno));
41          exit(1);
42     }
43 
44     //get a message from the queue everytime
45     int i;
46     for (i = 0; i < 3; ++i)
47     {
48         msgrcv(msgid,&msg,sizeof(msgbuf),1,0);
49         printf("Receiver receive: %s
", msg.mtext);
50     }
51 
52     //send the message to notice the sender
53     msg.mtype=2;
54     char * myask="3 messages have received from you";
55     strncpy(msg.mtext,myask,BUFFER);
56     msgsnd(msgid,&msg,sizeof(msgbuf),IPC_NOWAIT);
57 
58     return 1;
59 }
60 
61 
62 /*
63 [Test]
64 gcc -o msg_receiver msg_receiver.c
65 ./msg_reveiver
66 generate a key=589827
67 Receiver receive: I'm sender,there are some messages for you.
68 Receiver receive: Message 1
69 Receiver receive: Message 2
70 */

发送端实现代码:

 1 #include <unistd.h>
 2 #include <stdio.h>
 3 #include <stdlib.h>
 4 #include <string.h>
 5 #include <errno.h>
 6 #include <sys/stat.h>
 7 #include <sys/types.h>
 8 #include <sys/ipc.h>
 9 #include <sys/msg.h>  //for msg_get(),msg_rcv() 
10 
11 #define MSG_FILE "/tmp"       //a pathname for generating a unique key
12 #define BUFFER 512            //set the buffer size to 255 bytes
13 #define PERM S_IRUSR|S_IWUSR  //allow the user to read and write
14 
15 struct msgbuffer
16 {    
17     long mtype;
18     char mtext[BUFFER+1];
19 };
20 typedef struct msgbuffer msgbuf;
21 
22 //create three message
23 char* message[3]={"I'm sender,there are some messages for you.","Message 1","Message 2"};
24 
25 int main(int argc, char const *argv[])
26 {
27     msgbuf msg;
28     key_t key;
29     int msgid;
30 
31     //create a unique key
32     if ((key=ftok(MSG_FILE,BUFFER))==-1)
33      {
34          fprintf(stderr, "ftok:%s
", strerror(errno));
35          exit(1);
36      }else
37     {
38         printf("generate a key=%d
", key);
39     }
40 
41     //get a message queue 
42     if ((msgid=msgget(key,PERM|IPC_CREAT))==-1)
43     {
44         fprintf(stderr, "msgget:%s
", strerror(errno));
45          exit(1);
46     }
47 
48     //set the type of the message
49     msg.mtype=1;
50 
51     //send three messages to the receiver
52     int i;
53     for (i = 0; i <3; ++i)
54     {
55          strncpy(msg.mtext,message[i],BUFFER);
56          msgsnd(msgid,&msg,sizeof(msgbuf),IPC_NOWAIT);
57     }
58 
59     //receive the response from the receiver
60     memset(&msg,'',sizeof(msgbuf));//clear the msgbuf 
61     /*receive the message,the third arguments show the type of message will be received */
62     msgrcv(msgid,&msg,sizeof(msgbuf),2,0);
63     printf("This is a message from the receiver,%s
", msg.mtext);
64 
65 
66     //delete the message queue.
67     if (msgctl(msgid,IPC_RMID,0)==-1)
68     {
69         fprintf(stderr, "ftok:%s
", strerror(errno));
70          exit(1);
71     }
72     return 1;
73 }
74 
75 /*
76 [Test]
77 gcc -o msg_sender msg_sender.c
78 ./msg_sender
79 generate a key=589827
80 This is a message from the receiver,3 messages have received from you
81 */

对于消息通信,其中的几个函数,这里在说明一下,msgrcv和msgsnd两个函数,对于其中的参数,第一个指定的是我们的消息队列的标示符,然后第二个是消息区域,然后是我们的消息长度,然后是消息类型,最后一个是用来指定是否堵塞。

上述为Linux下进程间通信的四种方式,实际开发中,我们传输数据类型和进程间的关系选择合适的方式。

 

 

 

 

原文地址:https://www.cnblogs.com/lou424/p/5018966.html