有名管道(FIFO)实现无亲缘关系的客户服务器

 FIFO(也称为有名管道)是first in first out的意思,也就是队列的特点。有名管道也是一个单向的数据流,但它与管道又有写区别。有名管道是有名字的,每一个FIFO对应于一个路径名,正因为这一点,有名管道可以允许非亲缘关系的进程访问同一个FIFO。另外,有名管道存在于磁盘当中,而管道存在于内存当中,通信结束后,有名管道的文件本身仍热存在,但是管道已经释放了。有名管道与文件也是有区别的,文件的话,当读取其中的内容之后,信息依然存在,但是有名管道中,通信结束之后,信息就会丢失。
    有名管道的使用是得首先通过mkfifo()函数创建一个fifo,然后通过open()、write()、read()等函数进行操作,当最后使用完时,可以调用unlink()函数对fifo文件进行删除。
    下面先看一个有名管道的简单应用程序:

  1. /*fifo.c*/
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <fcntl.h>

  9. #define FIFO_NAME "fifo_test"
  10. #define MAX 80

  11. int main()
  12. {
  13.     pid_t pid;
  14.     int fd;
  15.     char buf[MAX]={0};

  16.     unlink(FIFO_NAME);               //为了防止FIFO_NAME文件存在
  17.     mkfifo(FIFO_NAME, 0744);         //创建fifo
  18.     pid = fork();
  19.     if(pid == 0) {
  20.         fd = open(FIFO_NAME, O_RDONLY);
  21.         while(read(fd, buf, MAX) != 0) {
  22.             printf("read from fifo:%s\n", buf);
  23.         }
  24.         close(fd);
  25.     } else if(pid > 0) {
  26.         fd = open(FIFO_NAME, O_WRONLY);
  27.         while(fgets(buf, MAX, stdin) != NULL) {
  28.             buf[strlen(buf)-1] = 0; //deal with enter key
  29.             write(fd, buf, MAX);
  30.         }
  31.         close(fd);
  32.         wait(NULL);                 //等待子进程退出
  33.         unlink(FIFO_NAME);
  34.     } else {
  35.         perror("fork error");
  36.         exit(1);
  37.     }

  38.     return 0;
  39. }
    这个程序的一次执行结果如下:
  1. ^_^[sunny@sunny-laptop ~/summer]121$ ./a.out 
  2. sun                            //从键盘输入
  3. read from fifo:sun
  4. ls                             //从键盘输入
  5. read from fifo:ls
  6. open                           //从键盘输入
  7. read from fifo:open
  8. lovelinux                      //从键盘输入
  9. read from fifo:lovelinux       //显示完毕之后,键入ctrol+D结束
  10. ^_^[sunny@sunny-laptop ~/summer]122$
    程序说明:这个程序中,创建了一个进程,父进程以写的方式打开fifo,循环从键盘接收输入的字符串,之后去掉“\n”,将键盘接收到的字符串写入fifo中。对于子进程,以只读的方式打开fifo文件,之后从中读取写入的字符数据。这里应当注意一下:fifo写入的时候是写到fifo的末尾,读取的时候是从fifo的开始进行读取,而且fifo进行读写操作的时候,只有读端和写端同时都打开的时候才能进行相应的操作,否则,读取端和写入端都会陷入等待状态,并且有可能发生死锁状态,这点切记。
    下面,进入我们的正题——通过有名管道实现无亲缘关系的客户与服务器模型。
    下面的这个程序也是比较简单,不过不用急,我们慢慢对代码进行修改,由简单变复杂。这个程序主要是实现没有亲缘关系的两个进程进行通信,其中的client进程向fifo中循环写入数据,而在另外一个终端中运行的server进程,循环从fifo中读取数据并且显示出来。
  1. /*fifo_server.c*/
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <fcntl.h>

  9. #define FIFO_NAME "fifo_test"
  10. #define MAX 1024

  11. int main()
  12. {
  13.     int fd;
  14.     char buf[MAX]={0};

  15.     fd = open(FIFO_NAME, O_RDONLY);
  16.     while(read(fd, buf, MAX) != 0) {
  17.         printf("read from client:%s\n", buf);
  18.     }

  19.     return 0;
  20. }
  1. /*fifo_client.c*/
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <fcntl.h>

  9. #define FIFO_NAME "fifo_test"
  10. #define MAX 1024

  11. int main()
  12. {
  13.     int fd;
  14.     char buf[MAX]={0};
  15.     
  16.     unlink(FIFO_NAME);
  17.     mkfifo(FIFO_NAME, 0744);
  18.     fd = open(FIFO_NAME, O_WRONLY);
  19.     while(fgets(buf, MAX, stdin) != NULL) {
  20.         buf[strlen(buf)-1] = 0; //是为了去掉回车符
  21.         write(fd, buf, MAX);
  22.     }
  23.     unlink(FIFO_NAME);
  24.     printf("connect over,exit successfully!\n");
  25.     return 0;
  26. }
    编译这两个程序:
  1. O_O[sunny@sunny-laptop ~/summer/fifo_rw]11$ gcc fifo_server.-o server
  2. ^_^[sunny@sunny-laptop ~/summer/fifo_rw]12$ gcc fifo_client.-o client
    在一个终端执行:
    1. ^_^[sunny@sunny-laptop ~/summer/fifo_rw]13$ ./client 
    2. sun                    //输入的数据
    3. open                   //输入的数据
    4. connect over,exit successfully!
    5. ^_^[sunny@sunny-laptop ~/summer/fifo_rw]14$
    在另外一个终端执行:
    1. ^_^[sunny@sunny-laptop ~/summer/fifo_rw]2$ ./server 
    2. read from client:sun
    3. read from client:open
    这个简单的例子是半双工通信。为了实现全双工通信,我们需要两个fifo。程序代码如下:
  1. /*fifo_client.c*/
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <fcntl.h>

  9. #define FIFO_NAME1 "fifo_test1"
  10. #define FIFO_NAME2 "fifo_test2"
  11. #define MAX 1024

  12. int main()
  13. {
  14.     int fd1, fd2;
  15.     char buf[MAX]={0};
  16.     
  17.     unlink(FIFO_NAME1);
  18.     unlink(FIFO_NAME2);
  19.     mkfifo(FIFO_NAME1, 0744);
  20.     mkfifo(FIFO_NAME2, 0744);
  21.     fd1 = open(FIFO_NAME1, O_WRONLY);
  22.     fd2 = open(FIFO_NAME2, O_RDONLY);
  23.     while(fgets(buf, MAX, stdin) != NULL) {
  24.         buf[strlen(buf)-1] = 0; //是为了去掉回车符
  25.         write(fd1, buf, MAX);
  26.         read(fd2, buf, MAX);
  27.         printf("client read from server:%s\n", buf);
  28.     }
  29.     unlink(FIFO_NAME1);
  30.     unlink(FIFO_NAME2);
  31.     printf("connect over,exit successfully!\n");
  32.     return 0;
  33. }
  1. /*fifo_server.c*/
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <fcntl.h>

  9. #define FIFO_NAME1 "fifo_test1"
  10. #define FIFO_NAME2 "fifo_test2"
  11. #define MAX 1024

  12. int main()
  13. {
  14.     int fd1, fd2;
  15.     char buf[MAX]={0};

  16.     fd1 = open(FIFO_NAME1, O_RDONLY);
  17.     fd2 = open(FIFO_NAME2, O_WRONLY);
  18.     while(read(fd1, buf, MAX) != 0) {
  19.         printf("read from client:%s\n", buf);
  20.         fgets(buf, MAX, stdin);
  21.         buf[strlen(buf)-1] = 0;
  22.         write(fd2, buf, MAX);
  23.     }

  24.     return 0;
  25. }
    编译并运行这个全双工通信的例子:
    编译链接:
  1. ^_^[sunny@sunny-laptop ~/summer/fifo_rw1]96$ gcc fifo_client.-o client
  2. ^_^[sunny@sunny-laptop ~/summer/fifo_rw1]97$ gcc fifo_server.-o server
    执行客户端(client):
  1. ^_^[sunny@sunny-laptop ~/summer/fifo_rw1]98$ ./client 
  2. hello world                    //从键盘输入
  3. client read from server:hello who are you?
  4. I am sunny.                    //从键盘输入
  5. client read from server:Oh 
  6. bye                            //从键盘输入
  7. client read from server:bye
  8. connect over,exit successfully!
  9. ^_^[sunny@sunny-laptop ~/summer/fifo_rw1]99$
    执行服务器端(server):
  1. ^_^[sunny@sunny-laptop ~/summer/fifo_rw1]5$ ./server 
  2. read from client:hello world
  3. hello who are you?            //从键盘输入
  4. read from client:I am sunny.
  5. Oh                            //从键盘输入
  6. read from client:bye
  7. bye                           //从键盘输入ctrol+d
  8. ^_^[sunny@sunny-laptop ~/summer/fifo_rw1]6$
    程序说明:这两个程序先执行client之后,再启动server(否则会发生一些问题,大家可以试验一下)。之后现在client端输入数据,转到server端发现从中读取了数据,紧接着在server端输入,就这样循环着

这篇文章出自ChinaUnix一博主,原文地址如下:
http://blog.chinaunix.net/uid-22566367-id-1695917.html

你可能感兴趣的:(unix,服务器,客户端,进程通信,命名管道)