读书笔记:第4章 管道和FIFO (4)

        《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

你可能感兴趣的:(读书笔记,《UNIX网络编程》)