客户端
open.h头文件
首先我们定义头文件,包含一些标准头文件和函数的声明:
#include "apue.h"
#include <errno.h>
#define CL_OPEN "open" /* client's request for server */
int csopen(char *, int);
客户进程的main函数(版本1)
main函数就是一个循环,从标准输入读取路径,然后将文件拷贝到标准输出。这个函数调用csopen来连接打开文件的服务进程,并且返回一个打开的文件描述符号。
#include "open.h"
#include <fcntl.h>
#define BUFFSIZE 8192
int main(int argc, char *argv[])
{
int n, fd;
char buf[BUFFSIZE], line[MAXLINE];
/* read filename to cat from stdin */
while (fgets(line, MAXLINE, stdin) != NULL) {
if (line[strlen(line) - 1] == '\n')
line[strlen(line) - 1] = 0; /* replace newline with null */
/* open the file */
if ((fd = csopen(line, O_RDONLY)) < 0)
continue; /* csopen() prints error from server */
/* and cat to stdout */
while ((n = read(fd, buf, BUFFSIZE)) > 0)
if (write(STDOUT_FILENO, buf, n) != n)
err_sys("write error");
if (n < 0)
err_sys("read error");
close(fd);
}
exit(0);
}
csopen函数(版本1)
创建了s-pipe管道之后,函数csopen调用fork和exec服务进程。
#include "open.h"
#include <sys/uio.h> /* struct iovec */
/*
* Open the file by sending the "name" and "oflag" to the
* connection server and reading a file descriptor back.
*/
int csopen(char *name, int oflag)
{
pid_t pid;
int len;
char buf[10];
struct iovec iov[3];
static int fd[2] = { -1, -1 };
if (fd[0] < 0) { /* fork/exec our open server first time */
if (s_pipe(fd) < 0)
err_sys("s_pipe error");
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) { /* child */
close(fd[0]);
if (fd[1] != STDIN_FILENO &&
dup2(fd[1], STDIN_FILENO) != STDIN_FILENO)
err_sys("dup2 error to stdin");
if (fd[1] != STDOUT_FILENO &&
dup2(fd[1], STDOUT_FILENO) != STDOUT_FILENO)
err_sys("dup2 error to stdout");
if (execl("./opend", "opend", (char *)0) < 0)
err_sys("execl error");
}
close(fd[1]); /* parent */
}
sprintf(buf, " %d", oflag); /* oflag to ascii */
iov[0].iov_base = CL_OPEN " "; /* string concatenation */
iov[0].iov_len = strlen(CL_OPEN) + 1;
iov[1].iov_base = name;
iov[1].iov_len = strlen(name);
iov[2].iov_base = buf;
iov[2].iov_len = strlen(buf) + 1; /* +1 for null at end of buf */
len = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len;
if (writev(fd[0], &iov[0], 3) != len)
err_sys("writev error");
/* read descriptor, returned errors handled by write() */
return(recv_fd(fd[0], write));
}
子进程关闭管道的一端,父进程关闭另外一端。exec服务进程之前,子进程首先将管道的没有关闭的那端dup到其标准输入和标准输出中。(另外一个可以选择的就是将文件描述符号 fd[1]
的ASCII表示作为一个参数传递给服务进程)
父进程给服务进程发送包含路径和open模式的请求。最后,父进程调用recv_fd来返回文件描述符号或者错误。如果错误被服务进程返回,那么向标准错误输出写相关的信息。
网友评论