《UNIX网络编程:卷2》P42:图4-16 使用两个FIFO的客户-服务器程序
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <fcntl.h> #include <sys/stat.h> #include <sys/wait.h> // 用户读、用户写、组成员读和其他用户读,这些权限会被当前进程的文件模式创建掩码修正 #define FILE_MODE (S_IRUSR | S_IWUSR| S_IRGRP | S_IROTH) #define FIFO1 "/tmp/fifo.1" #define FIFO2 "/tmp/fifo.2" void client(int readfd, int writefd); void server(int readfd, int writefd); /* P42 mainfifo.c */ int main(int argc, char *argv[]) { int readfd, writefd; pid_t childpid; // 创建两个管道 if ((mkfifo(FIFO1, FILE_MODE) < 0) && (errno != EEXIST)) { fprintf(stderr, "can't create %s: %s\n", FIFO1, strerror(errno)); exit(1); } if ((mkfifo(FIFO2, FILE_MODE) < 0) && (errno != EEXIST)) { unlink(FIFO1); fprintf(stderr, "can't create %s: %s\n", FIFO2, strerror(errno)); exit(1); } if ((childpid = fork()) < 0) { unlink(FIFO1); unlink(FIFO2); fprintf(stderr, "fork error: %s\n", strerror(errno)); exit(1); } else if (childpid == 0) { // 子进程 // 打开FIFO1用于读 if ((readfd = open(FIFO1, O_RDONLY, 0)) < 0) { fprintf(stderr, "open %s error: %s\n", FIFO1, strerror(errno)); } // 打开FIFO2用于写 if ((writefd = open(FIFO2, O_WRONLY, 0)) < 0) { fprintf(stderr, "open %s error: %s\n", FIFO2, strerror(errno)); } server(readfd, writefd); exit(0); } // 父进程 // 打开FIFO1用于写 if ((writefd = open(FIFO1, O_WRONLY, 0)) < 0) { fprintf(stderr, "open %s error: %s\n", FIFO1, strerror(errno)); } // 打开FIFO2用于读 if ((readfd = open(FIFO2, O_RDONLY, 0)) < 0) { fprintf(stderr, "open %s error: %s\n", FIFO2, strerror(errno)); } client(readfd, writefd); // 等待子进程终止 waitpid(childpid, NULL, 0); // 关闭文件 close(readfd); close(writefd); // 删除文件 unlink(FIFO1); unlink(FIFO2); exit(0); } /* P36 client.c */ #define MAXLINE 1024 void client(int readfd, int writefd) { size_t len; ssize_t n; char buff[MAXLINE]; fgets(buff, MAXLINE, stdin); // 从标准输入读路径名字符串 len = strlen(buff); if (buff[len-1] == '\n') // 删除存入的换行符 len--; // 将路径字符串写入管道 if(write(writefd, buff, len) != len){ fprintf(stderr, "write error: %s\n", strerror(errno)); exit(1); } // 从管道读取由服务器写入的数据,并将其写到标准输出 while ((n = read(readfd, buff, MAXLINE)) > 0) write(STDOUT_FILENO, buff, n); } /* P37 server.c */ void server(int readfd, int writefd) { int fd; ssize_t n; char buff[MAXLINE]; // 从管道读出由客户端写入的路径名 if ((n = read(readfd, buff, MAXLINE)) < 0) { fprintf(stderr, "read error: %s\n", strerror(errno)); exit(1); } else if (n == 0) { fprintf(stderr, "end-of-file while reading pathname\n"); exit(1); } buff[n] = '\0'; // 以空字符作为结尾 // 打开所请求的文件 if ((fd = open(buff, O_RDONLY)) < 0) { // 打开文件出错 snprintf(buff + n, sizeof(buff) - n, ": can't open, %s\n", strerror(errno)); n = strlen(buff); write(writefd, buff, n); // 将出错信息写入管道 } else { // 打开文件成功,将文件内容复制到管道中 while((n = read(fd, buff, MAXLINE)) > 0) write(writefd, buff, n); close(fd); } }
main函数创建两个FIFO并用fork生成一个子进程。客户然后作为父进程运行,服务器则作为子进程运行。第一个FIFO用于从客户向服务器发送路径名,第二个FIFO用于从服务器向客户发送该文件的内容(或者一个出错消息)。
运行程序:
$ ./mainfifo Makefile 一个由四行文件构成的文件 mainfifo: gcc mainfifo.c -o mainfifo -Wall clean: rm mainfifo $ ./mainfifo /etc/shadow 一个我们不能读的文件 /etc/shadow: can't open, Permission denied $ ./mainfifo /no/such/file 一个不存在的文件 /no/such/file: can't open, No such file or directory