【網絡編程基礎】Linux下進程通訊方式(共享內存,管道,消息隊列,Socket)

在網絡課程中,有講到Socket編程,對於tcp講解的環節,爲了加深理解,本身寫了Linux下進程Socket通訊,在學習的過程當中,又接觸到了其它的幾種方式。記錄一下。web

管道通訊(匿名,有名)

管道通訊,在一個進程之中,只能單一的對其寫或者是讀,而不能夠及執行寫操做又執行讀操做。這一點,咱們能夠將其想象成咱們的水管,分別連着不一樣的兩端,在有水流的時候,一端只能進行輸入,另外一端只能進行輸出,而不能夠同時輸入和輸出。編程

管道又分爲有名管道和匿名管道,二者的區別在於對於匿名管道,其只能在具備親緣關係的父子進程之間進行消息通訊。管道的通訊是藉助於在咱們的磁盤上生成一個文件,而後對文件的讀寫來實現咱們的通訊,並且數據被讀以後,在文件中將會被刪除掉。數組

匿名

匿名管道的實現較爲簡單,實現代碼:網絡

 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\n",strerror(errno));
22         exit(1);
23     }
24 
25     if ((pid=fork())<0)
26     {
27         fprintf(stderr, "fork:%s\n",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\n", 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\n",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函數。socket

fork函數返回的數據在父進程中是子進程pid,而對子進程返回的是0,這並非說,子進程的pid就是零,還有一個函數是getpid(),執行這個函數後,在子進程和父進程中獲得的都是該進程pid,而不是fork函數的返回值。fork函數執行的時候,是將父進程中所有的數據置入本身進程之中,和父進程中的數據不是同步的了。tcp

經過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\n", 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\n");
30                 close(pipe_id);
31             }else
32             {
33                 fprintf(stderr, "write:%s\n",strerror(errno));
34                 exit(1);
35             }
36         }else
37         {
38             fprintf(stderr, "open:%s\n",strerror(errno));
39         }
40     }
41     return 0;
42 }

由於管道是經過本地磁盤上的文件進行信息的交換,所以咱們須要給予其本地磁盤上的一個文件目錄,而後根據該目錄經過access()函數來獲取該管道,若是獲取失敗,那麼咱們就按照給定的目錄建立一個,建立管道的時候,咱們須要制定是對其進行讀仍是寫,建立好以後,經過指定模式打開咱們的管道,此時會獲得一個管道號,而後根據得到管道標誌號,做爲參數,進行read和write操做。下面是讀端的執行代碼。ui

 

 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\n",(char *)buffer);
23     }else
24     {
25         fprintf(stderr, "open:%s\n", strerror(errno));
26         exit(1);
27     }
28     return 0;
29 }

 

Socket通訊

 

Socket通訊,不只僅是一臺主機上的兩個進程能夠進行通訊,還可讓處在因特網中的兩個進程進行通訊。在兩個進程進行通訊的時候,首先本地的進程在運行的時候會綁定一個端口,而後咱們本地爲該進程生成一個緩衝區,返回一個值,即爲socket做爲對其進行標記,每當本地進程和遠程一個進程創建鏈接的時候,就會根據遠程進程的信息和本地進程的信息生成一個socket,而後雙方藉助於socket就能夠進行通訊,運輸層獲得的數據寫入socket標誌的緩衝區,而後在裏面進行相應的操做以後將其提交給網絡層。相比其它的幾種處理方式,該中方式比較麻煩。多於服務端,經過listen阻塞監聽,監聽到有鏈接請求,經過accept函數獲得一個本地與之對應的緩衝區,而後建立一個進程用來和該鏈接進行交互,而後經過receive來接收信息,因爲c語言大一學過去以後,基本沒怎麼再看過,因此寫來的時候仍是遇到了幾個小坑。這裏實現的是當鏈接創建後,服務端給本地端發送一個鏈接創建提示,而後客戶端能夠向服務端發送消息,服務端給予一個I don't know的回覆。this

服務端代碼

 

  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\n", 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\n", 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\n", 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\n", 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,"\n");
 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\n", strerror(errno));
 80             exit(1);
 81         }
 82         //display the data receive from the client
 83         printf("%s\n",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\n", strerror(errno));
 90             exit(1);
 91         }else
 92         {
 93             printf("The file send_data was opened.\n");
 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\n",lengthsize);
102             //printf("%s\n", buffer);
103             if (send(new_server_socket,buffer,lengthsize,0)<0)
104             {
105                 printf("send file failed!\n");
106                 break;
107             }
108             else
109             {
110                 printf("the %d time to send...\n",++i);
111             }
112             bzero(buffer,BUFFER_SIZE);
113         }
114         if (close(stream)==-1)
115         {
116             fprintf(stderr, "close%s\n", 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\n",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\n", 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\n", 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\n", 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:\n", 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\n", strerror(errno));
 72         exit(1);
 73     }
 74     printf("\n%s\n",buffer);
 75 
 76     bzero(buffer,BUFFER_SIZE);
 77 
 78     strcpy(buffer,"Hello,this is client A!\n");
 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!\n");
 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!\n", 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!\n");
101             break;
102         }
103         bzero(buffer,BUFFER_SIZE);
104     }
105     printf("Receive File From Server [%s] Finished.\n",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 */

 

共享內存通訊方式

1、什麼是共享內存
  顧名思義,共享內存就是容許兩個不相關的進程訪問同一個邏輯內存。共享內存是在兩個正在運行的進程之間共享和傳遞數據的一種很是有效的方式。不一樣進程之間共享的內存一般安排爲同一段物理內存。進程能夠將同一段共享內存鏈接到它們本身的地址空間中,全部進程均可以訪問共享內存中的地址,就好像它們是由用C語言函數malloc分配的內存同樣。而若是某個進程向共享內存寫入數據,所作的改動將當即影響到能夠訪問同一段共享內存的任何其餘進程。
 
   特別提醒:共享內存並未提供同步機制,也就是說,在第一個進程結束對共享內存的寫操做以前,並沒有自動機制能夠阻止第二個進程開始對它進行讀取。因此咱們一般須要用其餘的機制來同步對共享內存的訪問,例如前面說到的信號量。
 
2、共享內存的使用
與信號量同樣,在Linux中也提供了一組函數接口用於使用共享內存,並且使用共享共存的接口還與信號量的很是類似,並且比使用信號量的接口來得簡單。它們聲明在頭文件 sys/shm.h中。
 
一、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,它表示容許一個進程建立的共享內存被內存建立者所擁有的進程向共享內存讀取和寫入數據,同時其餘用戶建立的進程只能讀取共享內存。
 
二、shmat函數
第一次建立完共享內存時,它還不能被任何進程訪問,shmat函數的做用就是用來啓動對該共享內存的訪問,並把共享內存鏈接到當前進程的地址空間。它的原型以下:
 
  1. void *shmat(int shm_id, const void *shm_addr, int shmflg);  
第一個參數,shm_id是由shmget函數返回的共享內存標識。
第二個參數,shm_addr指定共享內存鏈接到當前進程中的地址位置,一般爲空,表示讓系統來選擇共享內存的地址。
第三個參數,shm_flg是一組標誌位,一般爲0。
 
調用成功時返回一個指向共享內存第一個字節的指針,若是調用失敗返回-1.
 
三、shmdt函數
該函數用於將共享內存從當前進程中分離。注意,將共享內存分離並非刪除它,只是使該共享內存對當前進程再也不可用。它的原型以下:
 
  1. int shmdt(const void *shmaddr);  
參數shmaddr是shmat函數返回的地址指針,調用成功時返回0,失敗時返回-1.
 
四、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\n", 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\n", 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\n", 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\n", 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\n", strerror(errno));
32     }    
33 
34     printf("%s\n",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\n", strerror(errno));
29          exit(1);
30     }else
31     {
32         printf("generate a key=%d\n", 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\n", 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\n", 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\n", strerror(errno));
35          exit(1);
36      }else
37     {
38         printf("generate a key=%d\n", key);
39     }
40 
41     //get a message queue 
42     if ((msgid=msgget(key,PERM|IPC_CREAT))==-1)
43     {
44         fprintf(stderr, "msgget:%s\n", 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,'\0',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\n", msg.mtext);
64 
65 
66     //delete the message queue.
67     if (msgctl(msgid,IPC_RMID,0)==-1)
68     {
69         fprintf(stderr, "ftok:%s\n", 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下進程間通訊的四種方式,實際開發中,咱們傳輸數據類型和進程間的關係選擇合適的方式。

相關文章
相關標籤/搜索