進程間通信——FIFO(多個客戶進程,一個服務進程)


FIFO簡介

FIFO就是Unix的一種復合POSIX標准的進程間通信機制。他又稱為命名管道,跟管道的不同點是,每個FIFO都有一個路徑名與之關聯。

FIFO雖然有路徑名,但是他這中文件是在內核態(管道也是在內核態),跟文件系統沒有關系。

單個服務器進程,多個客戶端進程與服務器進通信。客戶端進程想服務器進程發送請求(客戶端進程通過write寫FIFO),服務端處理(通過read讀客戶進程的請求)之后返回相應內容(通過write寫入專用FIFO)。

單個服務器進程多個客戶端進程通信框架

客戶進程和服務器進程都知道專用FIFO的路徑名,關鍵是怎樣創建客戶和服務器進程專用的FIFO。因為,一個系統的進程id是確定的並且互斥,所以我們可以通過進程id去尋找FIFO,客戶進程和服務進程之間的FIFO用進程id來創建,在客戶和服務器進程之間協商一個格式(比如“fifo.pid”格式)。

多個客戶進程向服務器進程請求服務

本示例代碼,實現的是將客戶進程請求打開一個文件,具體就是客戶進程傳文件名,服務器進程將文件內容返回。

客戶端代碼:

 1 /*
 2  * client.c
 3  *
 4  *  Created on: Nov 2, 2016
 5  *      Author: sujunjun
 6  */
 7 
 8 
 9 #include<stdio.h>
10 #include<unistd.h>
11 #include<sys/stat.h>
12 #include<limits.h>
13 #include<fcntl.h>
14 #include<stdlib.h>
15 #include<errno.h>
16 #include<string.h>
17 
18 #define FIFO_SERVER "fifo.server"
19 #define SIZE PIPE_BUF
20 #define FILE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)
21 
22 int main(){
23 
24     pid_t pid=getpid();
25     char buf[SIZE];
26     char client_fifo_name[SIZE];//from server
27     snprintf(client_fifo_name,sizeof(client_fifo_name),"fifo.%ld",(long)pid);
28     if(mkfifo(client_fifo_name,FILE_MODE)<0 && errno!=EEXIST){
29             printf("mk error \n");
30             exit(1);
31     }
32     snprintf(buf,sizeof(buf),"%ld ",(long)pid);
33     int len=strlen(buf);
34     char *pathname=buf+len;
35     fgets(pathname,SIZE-len,stdin);
36     len =strlen(buf);/f include \n
37 
38     int writefd=open(FIFO_SERVER,O_WRONLY);
39     write(writefd,buf,len-1);//len-1==not include \n
40 
41     int readfd=open(client_fifo_name,O_RDONLY);
42     int r;
43     while((r=read(readfd,buf,SIZE))>0){
44         write(STDOUT_FILENO,buf,r);
45     }
46     close(readfd);
47     unlink(client_fifo_name);//delete this temp file
48     return 0;
49 }
View Code

服務器代碼:

 1 /*
 2  * server.c
 3  *
 4  *  Created on: Nov 2, 2016
 5  */
 6 
 7 #include<stdio.h>
 8 #include<unistd.h>
 9 #include<sys/stat.h>
10 #include<errno.h>
11 #include<stdlib.h>
12 #include<fcntl.h>
13 #include<limits.h>
14 #include<string.h>
15 
16 #define FIFO_SERVER "fifo.server"//all known
17 
18 #define FILE_MODE (S_IWUSR|S_IRUSR|S_IRGRP|S_IROTH)
19 
20 #define SIZE PIPE_BUF
21 int main(){
22     if(mkfifo(FIFO_SERVER,FILE_MODE)<0 && errno!=EEXIST){
23         printf("mk error \n");
24         exit(1);
25     }
26 
27     int readfd=open(FIFO_SERVER,O_RDONLY);
28     int writefd=open(FIFO_SERVER,O_WRONLY);
29 
30     if(readfd==-1||writefd==-1){
31         printf("open error \n");
32         exit(1);
33     }
34 
35     int r;char buf[SIZE];char *pathname;pid_t pid;char client_fifo_name[SIZE];
36     while((r=read(readfd,buf,SIZE))>0){
37         buf[r]='\0';
38         if((pathname=strchr(buf,' '))==NULL){
39             printf("not include pid\n");
40             continue;
41         }
42         *pathname++='\0';//*pid_s=0  pid_s++
43         pid=atol(buf);
44         snprintf(client_fifo_name,sizeof(client_fifo_name),"fifo.%ld",(long)pid);
45         if((writefd=open(client_fifo_name,O_WRONLY))<0){
46             printf("error\n");
47             continue;
48         }
49         int fd;
50         if((fd=open(pathname,O_RDONLY))<0){
51             printf("open this file error\n");
52             write(writefd,"open this file error\n",sizeof("open this file error\n"));
53             close(writefd);
54         }else {
55             while((r=read(fd,buf,SIZE))>0){
56                 write(writefd,buf,r);
57             }
58             close(fd);
59         }
60         close(writefd);
61     }
62     return 0;
63 }
View Code

運行示例:

編譯兩個文件

gcc client.c -o client

gcc server.c -o server

./server

./client  

./client  

./client

多開幾個客戶進程,請求打開不同的文件,就會看到顯示的文件內容。

總結

本例子設計的是一個迭代服務器,如果要設計一個並發服務器,還需要一些多線程編程的知識。

還是要對FIFO一些特性要熟悉,比如open一個FIFO的時候,阻塞和非阻塞open之間的區別。還有FIFO的一次請求字節數必須小於等於PIPE_BUF(在limits.h中),這樣能保證一次讀請求是原子操作。如果不是原子的,那么兩次請求的字節內容將無法區分。因為服務器進程是一直在循環讀請求字節(服務器進程里有一個大循環,read用readline代替就更好理解了)。

snprintf()函數還是比較方便,java中提供的一些字符串與數字之間轉換的便利操作,其實在C語言中也有,只不過被我們忽視了。snprintf函數就為我們提供了一個很好的數字轉換為字符串的函數。例如:snprintf(buf,buf的長度,"process id is %ld",(long)getpid());最后buf中存放的是"process id is 1234"(進程具體ID).

另外,迭代服務器存在一些弊端,就是在處理服務型攻擊的時候比較麻煩,這個不如並發服務器好處理。

服務型攻擊,就拿咱們的這個代碼來說,如果咱們的客戶進程只是請求服務,卻從不打開自己進程與服務器專用FIFO來讀服務回傳的內容,這個時候,服務器可能就處於停頓狀態。這就叫服務型攻擊(DoS型攻擊)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM