《UNIX网络编程:卷2》P35-P37:图4-8、4-9、4-10 使用两个管道的客户-服务器程序
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <fcntl.h> #include <sys/wait.h> void client(int readfd, int writefd); void server(int readfd, int writefd); /* P35 mainpipe.c */ int main(int argc, char *argv[]) { int pipe1[2], pipe2[2]; pid_t childpid; if (pipe(pipe1) < 0) { // 创建管道1 fprintf(stderr, "pipe error: %s\n", strerror(errno)); exit(1); } if (pipe(pipe2) < 0) { // 创建管道2 fprintf(stderr, "pipe error: %s\n", strerror(errno)); exit(1); } if ((childpid = fork()) < 0) { // 创建新进程 fprintf(stderr, "fork error: %s\n", strerror(errno)); exit(1); } else if (childpid == 0) { // 子进程 close(pipe1[1]); // 关闭管道1的写端 close(pipe2[0]); // 关闭管道2的读端 server(pipe1[0], pipe2[1]); exit(0); } // 父进程 close(pipe1[0]); // 关闭管道1的读端 close(pipe2[1]); // 关闭管道2的写端 client(pipe2[0], pipe1[1]); waitpid(childpid, NULL, 0); // 获取已终止子进程的终止状态 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函数创建两个管道并用fork生成一个子进程。客户然后作为父进程运行,服务器则作为子进程运行。第一个管道用于从客户向服务器发送路径名,第二个管道用于从服务器向客户发送该文件的内容(或者一个出错消息)。
运行程序:
$ ./mainpipe Makefile 一个由四行文件构成的文件 mainpipe: gcc mainpipe.c -o mainpipe -Wall clean: rm mainpipe $ ./mainpipe /etc/shadow 一个我们不能读的文件 /etc/shadow: can't open, Permission denied $ ./mainpipe /no/such/file 一个不存在的文件 /no/such/file: can't open, No such file or directory