UNIX IPC有多种形式,最初使用的便是管道pipe,管道没有名字,又称匿名管道,一般用于有亲缘关系的进程间通信,后来出现了FIFO这种管道,它是有名字的,又叫做有名管道,可用于无亲缘关系的进程间通信,这两种管道的数据传输都可以使用我们最熟悉的write、read函数来完成。
1、管道pipe
创建管道使用如下pipe函数:
#include <unistd.h>
int pipe(int pipefd[2]);
pipe函数打开两个文件描述符,pipefd[0]用于读,pipefd[1]用于写,管道创建成功时返回0,否则返回-1,并设置errno。管道提供了一个单向数据流,一般用于父子进程间,两个进程分别关掉它们的读、写文件描述符,如果需要一个双向数据流,则需要创建两个管道,下面举例说明。
// mainpipe.c
/******************************
** A Client-Server Program
** Using fork and two pipes
** Client: father process, input a pathname
** Server: child process, open the pathname and write back to client
******************************/
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <unistd.h>
#define MAXLINE (100) // buffer size
// client/server, readfd for read, writefd for write
void client(int readfd, int writefd);
void server(int readfd, int writefd);
int main(int argc, char **argv)
{
int pipe1[2];
int pipe2[2];
pid_t childfd;
if (-1 == pipe(pipe1) || -1 == pipe(pipe2)) { // create two pipes
printf("create pipe error: %s", strerror(errno));
exit(EXIT_FAILURE);
}
if (0 == (childfd = fork())) { // child
// using pipe1[0] for read and pipe2[1] for write
// so close unused pipe1[1] and pipe2[0]
if (-1 == close(pipe1[1]) || -1 == close(pipe2[0])) {
printf("close pipe error: %s", strerror(errno));
exit(EXIT_FAILURE);
}
server(pipe1[0], pipe2[1]); // child is a server
exit(EXIT_SUCCESS);
}
// using pipe2[0] for read and pipe1[1] for write
// so close unused pipe1[0] and pipe2[1]
if (-1 == close(pipe1[0]) || -1 == close(pipe2[1])) {
printf("close pipe error: %s", strerror(errno));
exit(EXIT_FAILURE);
}
client(pipe2[0], pipe1[1]); // father is a client
if (-1 == waitpid(childfd, NULL, 0)) { // wait for child to terminate
perror("waitpid error");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
void client(int readfd, int writefd)
{
size_t len;
ssize_t n;
char buff[MAXLINE] = { 0 };
fgets(buff, MAXLINE, stdin); // using fgets to input from stdin
len = strlen(buff);
if ('\n' == buff[len - 1]) {
--len; // delete newline from fgets
}
if (-1 == write(writefd, buff, len)) { // write pathname to pipe
printf("write error: %s", strerror(errno));
}
while (0 < (n = read(readfd, buff, MAXLINE))) { // read data from pipe
if (-1 == write(STDOUT_FILENO, buff, n)) {
printf("write error: %s", strerror(errno)); // write data to stdout
}
}
}
void server(int readfd, int writefd)
{
int fd;
ssize_t n;
char buff[MAXLINE + 1] = { 0 };
if (-1 == (n = read(readfd, buff, MAXLINE))) { // read pathname from pipe
printf("read error: %s", strerror(errno));
}
else if (0 == n) {
perror("end-of-file while reading pathname");
}
buff[n] = '\0'; // null terminate
if (-1 == (fd = open(buff, O_RDONLY))) {
perror("open error");
snprintf(buff + n, sizeof(buff) - n, ": can't oepn, %s\n", strerror(errno));
n = strlen(buff);
write(writefd, buff, n); // open failed and tell client
}
else {
while (0 < (n = read(fd, buff, MAXLINE))) { // open succeeded and copy file to pipe
if (-1 == write(writefd, buff, n)) {
printf("write error: %s", strerror(errno));
}
}
if (-1 == close(fd)) {
printf("close fd error: %s", strerror(errno));
}
}
}
mainpipe.c是一个简单的Client-Server程序,Client通过Server获取文件信息。为了实现双向数据流,即Client输入文件名给Server,而Server又打开这个文件并传输文件内容给Client,这里创建了两个管道。fork之后,子进程为Client,父进程为Server,父子进程分别关闭它们不需要的文件描述符,然后开始通信。
上面提到的管道是一个半双工管道,也就是说一个文件描述符只能用于读,一个文件描述符只能用于写,如果拿某个文件描述符同时进行读写,把它当作全双工管道来用,就会出错,strerror(errno)的输出为“Bad file descriptor”,不过有的系统支持全双工管道的pipe,如SVR4,而socketpair则是具有全双工管道功能的另一个函数。
如果我们在shell终端输入一些支持管道的命令,比如说“ls | sort”,这也是一种单向数据流,半双工管道,把ls的标准输出(一个进程)复制到了sort的标准输入(另一个进程)。
stdio.h中还提供了另一个使用管道的函数popen,它创建一个管道并启动另外一个进程,就好像函数内部封装了pipe和fork一样,该管道也是单双工的,或者读,或者写。
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
popen的command参数是一个shell命令,type可以是r或者w,表示读或者写,pclose用于关闭打开的文件流。下面以一个例子说明popen的用法,实现shell的cat命令,所以type参数应该为r。
// mainpopen.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAXLINE (100) // buffer size
int main(int argc, char **argv)
{
size_t n;
char buff[MAXLINE];
char command[MAXLINE];
FILE *fp;
fgets(buff, MAXLINE, stdin); // get file name
n = strlen(buff);
if ('\n' == buff[n - 1]) {
--n; // delete newline from fgets
}
snprintf(command, sizeof(command), "cat %s", buff); // shell command
if (NULL == (fp = popen(command, "r"))) { // create pipe for read
perror("popen error");
exit(EXIT_FAILURE);
}
while(NULL != fgets(buff, MAXLINE, fp)) {
fputs(buff, stdout);
}
pclose(fp); // close from popen
exit(EXIT_SUCCESS);
}
2、有名管道FIFO
FIFO即first in first out,类似于pipe,也是半双工的单向数据流,与pipe不同的是它关联一个路径名,因此也叫做有名管道,可以用于没有亲缘关系的进程间通信。下面是与FIFO相关的几个函数。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int mkfifo(const char *pathname, mode_t mode);
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
int unlink(const char *pathname);
创建FIFO使用mkfifo函数(在shell终端可使用mkfifo命令创建FIFO),pathname是这个FIFO的路径名,mode为文件权限模式,类似于open函数的第三个参数,受当前进程掩码umask的影响,其真正的权限为(mode & ~umask),创建成功时返回0,失败时返回-1并设置相应的errno。创建失败时,有可能是pathname已经存在了,这时会产生一个EEXIST错误,等同于open函数的flags参数设置了(O_CREAT | O_EXCL)一样,要么创建,要么返回文件已经存在的错误。如果pathname已经存在,可使用open函数来打开并进行IPC通信,open打开时只能是读或者写,即O_RDONLY或者O_WRONLY,而不能是O_RDWR。通信完毕后,从文件系统中删除pathname,使用unlink函数。
下面使用FIFO来代替上面Client-Server程序中的pipe,client和server函数不变,只需修改main函数即可。
// mainfifo.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MAXLINE (100) // buffer size
#define FIFO1 "/tmp/fifo.1"
#define FIFO2 "/tmp/fifo.2"
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
// client/server, readfd for read, writefd for write
void client(int readfd, int writefd);
void server(int readfd, int writefd);
int main(int argc, char **argv)
{
int readfd;
int writefd;
pid_t childpid;
if ((0 > mkfifo(FIFO1, FILE_MODE)) && (EEXIST != errno)) { // make fifo, errno can be EEXIST
printf("mkfifo %s error: %s", FIFO1, strerror(errno));
exit(EXIT_FAILURE);
}
if ((0 > mkfifo(FIFO2, FILE_MODE)) && (EEXIST != errno)) { // make fifo, errno can be EEXIST
printf("mkfifo %s error: %s", FIFO2, strerror(errno));
unlink(FIFO1); // remove unuesd fifo
exit(EXIT_FAILURE);
}
if (0 == (childpid = fork())) { // child
if (0 > (readfd = open(FIFO1, O_RDONLY, 0))) {
printf("open %s error: %s", FIFO1, strerror(errno));
}
if (0 > (writefd = open(FIFO2, O_WRONLY, 0))) {
printf("open %s error: %s", FIFO2, strerror(errno));
}
server(readfd, writefd); // child is a server
exit(EXIT_SUCCESS);
}
if (0 > (writefd = open(FIFO1, O_WRONLY, 0))) {
printf("open %s error: %s", FIFO1, strerror(errno));
}
if (0 > (readfd = open(FIFO2, O_RDONLY, 0))) {
printf("open %s error: %s", FIFO2, strerror(errno));
}
client(readfd, writefd); // father is a client
if (-1 == waitpid(childpid, NULL, 0)) { // wait for child to terminate
perror("waitpid error");
}
close(readfd);
close(writefd);
unlink(FIFO1);
unlink(FIFO2);
exit(EXIT_SUCCESS);
}
void client(int readfd, int writefd)
{
size_t len;
ssize_t n;
char buff[MAXLINE] = { 0 };
fgets(buff, MAXLINE, stdin); // using fgets to input from stdin
len = strlen(buff);
if ('\n' == buff[len - 1]) {
--len; // delete newline from fgets
}
if (-1 == write(writefd, buff, len)) { // write pathname to pipe
printf("write error: %s", strerror(errno));
}
while (0 < (n = read(readfd, buff, MAXLINE))) { // read data from pipe
if (-1 == write(STDOUT_FILENO, buff, n)) {
printf("write error: %s", strerror(errno)); // write data to stdout
}
}
}
void server(int readfd, int writefd)
{
int fd;
ssize_t n;
char buff[MAXLINE + 1] = { 0 };
if (-1 == (n = read(readfd, buff, MAXLINE))) { // read pathname from pipe
printf("read error: %s", strerror(errno));
}
else if (0 == n) {
perror("end-of-file while reading pathname");
}
buff[n] = '\0'; // null terminate
if (-1 == (fd = open(buff, O_RDONLY))) {
perror("open error");
snprintf(buff + n, sizeof(buff) - n, ": can't oepn, %s\n", strerror(errno));
n = strlen(buff);
write(writefd, buff, n); // open failed and tell client
}
else {
while (0 < (n = read(fd, buff, MAXLINE))) { // open succeeded and copy file to pipe
if (-1 == write(writefd, buff, n)) {
printf("write error: %s", strerror(errno));
}
}
if (-1 == close(fd)) {
printf("close fd error: %s", strerror(errno));
}
}
}
上面例子中的FIFO应用是在父子进程间进行的,下面再对其进行修改,在两个无亲缘关系的进程间完成。
// server_main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MAXLINE (100) // buffer size
#define FIFO1 "/tmp/fifo.1"
#define FIFO2 "/tmp/fifo.2"
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
// server, readfd for read, writefd for write
void server(int readfd, int writefd);
int main(int argc, char **argv)
{
int readfd;
int writefd;
if ((0 > mkfifo(FIFO1, FILE_MODE)) && (EEXIST != errno)) { // make fifo, errno can be EEXIST
printf("mkfifo %s error: %s", FIFO1, strerror(errno));
exit(EXIT_FAILURE);
}
if ((0 > mkfifo(FIFO2, FILE_MODE)) && (EEXIST != errno)) { // make fifo, errno can be EEXIST
printf("mkfifo %s error: %s", FIFO2, strerror(errno));
unlink(FIFO1); // remove unuesd fifo
exit(EXIT_FAILURE);
}
if (0 > (readfd = open(FIFO1, O_RDONLY, 0))) {
printf("open %s error: %s", FIFO1, strerror(errno));
}
if (0 > (writefd = open(FIFO2, O_WRONLY, 0))) {
printf("open %s error: %s", FIFO2, strerror(errno));
}
server(readfd, writefd); // child is a server
exit(EXIT_SUCCESS);
}
void server(int readfd, int writefd)
{
int fd;
ssize_t n;
char buff[MAXLINE + 1] = { 0 };
if (-1 == (n = read(readfd, buff, MAXLINE))) { // read pathname from pipe
printf("read error: %s", strerror(errno));
}
else if (0 == n) {
perror("end-of-file while reading pathname");
}
buff[n] = '\0'; // null terminate
if (-1 == (fd = open(buff, O_RDONLY))) {
perror("open error");
snprintf(buff + n, sizeof(buff) - n, ": can't oepn, %s\n", strerror(errno));
n = strlen(buff);
write(writefd, buff, n); // open failed and tell client
}
else {
while (0 < (n = read(fd, buff, MAXLINE))) { // open succeeded and copy file to pipe
if (-1 == write(writefd, buff, n)) {
printf("write error: %s", strerror(errno));
}
}
if (-1 == close(fd)) {
printf("close fd error: %s", strerror(errno));
}
}
}
// client_main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MAXLINE (100) // buffer size
#define FIFO1 "/tmp/fifo.1"
#define FIFO2 "/tmp/fifo.2"
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
// client readfd for read, writefd for write
void client(int readfd, int writefd);
int main(int argc, char **argv)
{
int readfd;
int writefd;
if (0 > (writefd = open(FIFO1, O_WRONLY, 0))) {
printf("open %s error: %s", FIFO1, strerror(errno));
}
if (0 > (readfd = open(FIFO2, O_RDONLY, 0))) {
printf("open %s error: %s", FIFO2, strerror(errno));
}
client(readfd, writefd); // father is a client
close(readfd);
close(writefd);
unlink(FIFO1);
unlink(FIFO2);
exit(EXIT_SUCCESS);
}
void client(int readfd, int writefd)
{
size_t len;
ssize_t n;
char buff[MAXLINE] = { 0 };
fgets(buff, MAXLINE, stdin); // using fgets to input from stdin
len = strlen(buff);
if ('\n' == buff[len - 1]) {
--len; // delete newline from fgets
}
if (-1 == write(writefd, buff, len)) { // write pathname to pipe
printf("write error: %s", strerror(errno));
}
while (0 < (n = read(readfd, buff, MAXLINE))) { // read data from pipe
if (-1 == write(STDOUT_FILENO, buff, n)) {
printf("write error: %s", strerror(errno)); // write data to stdout
}
}
}
然后就可以在shell终端测试了:
$ gcc -o server server_main.c
$ gcc -o client client_main.c
$ ./sever &
$ ./client
3、管道和FIFO的使用注意事项
上面的例子只是简单的介绍了pipe和FIFO的用法,其实在使用过程中,还有一些东西是需要注意的。
阻塞与否——
设置非阻塞时,可以通过open函数的O_NONBLOCK标志实现,也可以通过fcntl函数完成,使用fcntl时一般先通过F_GETFL命令获取文件当前状态flags,然后再将(flags |= O_NONBLOCK)通过G_SETFL命令设置非阻塞,否则可能会出问题。
OPEN_MAX——
一个进程在任意时刻打开文件描述符的最大个数,其值可通过sysconf(_SC_OPEN_MAX)函数查询,使用setrlimit函数来修改,在shell终端也可以使用”getconf OPEN_MAX”命令来查询,通过ulimit命令修改。
PIPE_BUF——
可原子地写往pipe或FIFO的最大数据量,其值可使用pathconf(path, _PC_PIPE_BUF)或fpathconf函数获得,在shell终端使用”getconf PIPE_BUF ”命令也可以查询某个path的PIPE_BUF。
// pipeconf.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char **argv)
{
if (2 != argc) {
printf("usage: %s <path>\n", argv[0]);
exit(EXIT_FAILURE);
}
printf("OPEN_MAX = %ld, PIPE_BUF = %ld\n", sysconf(_SC_OPEN_MAX), pathconf(argv[1], _PC_PIPE_BUF));
exit(EXIT_SUCCESS);
}
4、其它
单个服务器,多个客户——
FIFO是一种只能在单台主机上使用的IPC形式。尽管在文件系统中有名字,它们也只能用在本地文件系统上,而不能用在通过NFS安装的文件系统上。
注意函数阻塞技巧。
// mainclient.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MAXLINE (100) // buffer size
#define SERV_FIFO "/tmp/fifo.serv" // server's well-known FIFO
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) // (FILE_MODE & ~umask)
int main(int argc, char **argv)
{
int readfifo, writefifo;
char *ptr, buff[MAXLINE], fifoname[MAXLINE];
pid_t pid;
ssize_t n;
size_t len;
pid = getpid();
snprintf(fifoname, sizeof(fifoname), "/tmp/fifo.%ld", (long)pid);
// create FIFO with our PID as part of name
if ((0 > mkfifo(fifoname, FILE_MODE)) && (EEXIST != errno)) {
printf("mkfifo %s error: %s", fifoname, strerror(errno));
exit(EXIT_FAILURE);
}
snprintf(buff, sizeof(buff), "%ld ", (long)pid);
len = strlen(buff);
ptr = buff + len;
fgets(ptr, MAXLINE - len, stdin); // read pathname
len = strlen(buff); // fgets guarantees null byte at end
// open FIFO to server and write PID and pathname to FIFO
if (0 > (writefifo = open(SERV_FIFO, O_WRONLY, 0))) {
printf("open %s error: %s", SERV_FIFO, strerror(errno));
unlink(fifoname);
exit(EXIT_FAILURE);
}
write(writefifo, buff, len);
// now open our FIFO, blocks untill server opens for writing
if (0 > (readfifo = open(fifoname, O_RDONLY, 0))) {
printf("open %s error: %s", fifoname, strerror(errno));
unlink(fifoname);
exit(EXIT_FAILURE);
}
while (0 < (n = read(readfifo, buff, MAXLINE))) {
write(STDOUT_FILENO, buff, n);
}
close(readfifo);
unlink(fifoname);
exit(EXIT_SUCCESS);
}
// mainserver.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define MAXLINE (100) // buffer size
#define SERV_FIFO "/tmp/fifo.serv" // server's well-known FIFO
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) // (FILE_MODE & ~umask)
int main(int argc, char **argv)
{
int readfifo, writefifo, dummyfd, fd;
char *ptr, buff[MAXLINE], fifoname[MAXLINE];
pid_t pid;
ssize_t n;
// create server's well-known FIFO, OK if already exists
if ((0 > mkfifo(SERV_FIFO, FILE_MODE)) && (EEXIST != errno)) {
printf("mkfifo %s error: %s", SERV_FIFO, strerror(errno));
exit(EXIT_FAILURE);
}
// open server's well-known FIFO for reading and writing
if (0 > (readfifo = open(SERV_FIFO, O_RDONLY, 0))) {
printf("open %s error: %s", SERV_FIFO, strerror(errno));
exit(EXIT_FAILURE);
}
if (0 > (dummyfd = open(SERV_FIFO, O_WRONLY, 0))) { // never used, blocks at read insteand of re-open
printf("open %s error: %s", SERV_FIFO, strerror(errno));
exit(EXIT_FAILURE);
}
while (0 < (n = read(readfifo, buff, MAXLINE))) {
if ('\n' == buff[n - 1]) {
--n; // delete newline from read
}
buff[n] = '\0'; // null terminate pathname
if (NULL == (ptr = strchr(buff, ' '))) {
printf("** request: %s, error input from client", buff);
continue;
}
*ptr++ = 0; // null termiate PID, ptr = pathname
pid = atol(buff);
snprintf(fifoname, sizeof(fifoname), "/tmp/fifo.%ld", (long)pid);
if (0 > (writefifo = open(fifoname, O_WRONLY, 0))) {
printf("open %s error: %s", fifoname, strerror(errno));
continue;
}
if (0 > (fd = open(ptr, O_RDONLY, 0))) { // error: must tell client
snprintf(buff + n, sizeof(buff) - n, ": can't open, %s\n", strerror(errno));
n = strlen(ptr);
write(writefifo, ptr, n);
close(writefifo);
}
else { // open succeded: copy file to FIFO
while (0 < (n = read(fd, buff, MAXLINE))) {
write(writefifo, buff, n);
}
close(fd);
close(writefifo);
}
}
// unlink(readfifo); // no need remove server FIFO
exit(EXIT_SUCCESS);
}
字节流与消息——
字节流:不存在记录边界,读写操作不检查数据。带内特殊终止序列,如换行符;显式长度,如记录数据长度的数据结构;每次连接一个记录,如open/close。
// pipemesg.h
#ifndef PIPEMESG
#define PIPEMESG
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <unistd.h>
#ifndef PIPE_BUF
#define PIPE_BUF (4096)
#endif
// Our own "messages" to use with pipes, FIFOS, and message queues.
// want sizeof(struct mymesg) <= PIPE_BUF
#define MAXMESGDATA (PIPE_BUF - 2 * sizeof(long))
// length of mesg_len and mesg_type
#define MESGHDRSIZE (sizeof(struct mymesg) - MAXMESGDATA)
struct mymesg
{
long mesg_len; // bytes in mesg_data, can be 0
long mesg_type; // message type, must be > 0
char mesg_data[MAXMESGDATA];
};
ssize_t mesg_send(int fd, struct mymesg *mptr);
ssize_t mesg_recv(int fd, struct mymesg *mptr);
void client(int readfd, int writefd);
void server(int readfd, int writefd);
#endif // PIPEMESG
// mesg_send.c
#include "pipemesg.h"
ssize_t mesg_send(int fd, struct mymesg *mptr)
{
return write(fd, mptr, MESGHDRSIZE + mptr->mesg_len);
}
// mesg_recv.c
#include "pipemesg.h"
ssize_t mesg_recv(int fd, struct mymesg *mptr)
{
size_t len;
ssize_t n;
// read massage header first, to get len of data that follows
if (0 == (n = read(fd, mptr, MESGHDRSIZE))) {
return 0; // end of file
}
else if (MESGHDRSIZE != n) {
printf("message header expected %d, got %d\n", MESGHDRSIZE, n);
return -1;
}
if (0 < (len = mptr->mesg_len)) {
if (len != (n = read(fd, mptr->mesg_data, len))) {
printf("message header expected %d, got %d\n", len, n);
return -1;
}
}
return len;
}
// client.c
#include "pipemesg.h"
void client(int readfd, int writefd)
{
size_t len;
ssize_t n;
struct mymesg mesg;
// read pathname
fgets(mesg.mesg_data, MAXMESGDATA, stdin);
len = strlen(mesg.mesg_data);
if ('\n' == mesg.mesg_data[len - 1]) {
--len; // delete newline from fgets
}
mesg.mesg_len = len;
mesg.mesg_type = 1;
// write pathname to IPC channel
mesg_send(writefd, &mesg);
// read from IPC, write to standard output
while (0 < (n = mesg_recv(readfd, &mesg))) {
write(STDOUT_FILENO, mesg.mesg_data, n);
}
}
// server.c
#include "pipemesg.h"
void server(int readfd, int writefd)
{
FILE *fp;
ssize_t n;
struct mymesg mesg;
// read pathname from IPC channel
mesg.mesg_type = 1;
if (0 == (n = mesg_recv(readfd, &mesg))) {
printf("pathname missing\n");
return;
}
mesg.mesg_data[n] = '\0'; // null terminate pathname
if (NULL == (fp = fopen(mesg.mesg_data, "r"))) {
// error: must tell client
snprintf(mesg.mesg_data + n, sizeof(mesg.mesg_data) - n, ":can't open, %s\n", strerror(errno));
mesg.mesg_len = strlen(mesg.mesg_data);
mesg_send(writefd, &mesg);
}
else {
// fopen succeeded: copy file to IPC channel
while (NULL != (fgets(mesg.mesg_data, MAXMESGDATA, fp))) {
mesg.mesg_len = strlen(mesg.mesg_data);
mesg_send(writefd, &mesg);
}
fclose(fp);
}
// send a 0-length message to signify the end
mesg.mesg_len = 0;
mesg_send(writefd, &mesg);
}
// mesg_main.c
#include "pipemesg.h"
#define MAXLINE (100) // buffer size
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int main(int argc, char **argv)
{
int pipe1[2];
int pipe2[2];
pid_t childfd;
if (-1 == pipe(pipe1) || -1 == pipe(pipe2)) { // create two pipes
printf("create pipe error: %s", strerror(errno));
exit(EXIT_FAILURE);
}
if (0 == (childfd = fork())) { // child
// using pipe1[0] for read and pipe2[1] for write
// so close unused pipe1[1] and pipe2[0]
if (-1 == close(pipe1[1]) || -1 == close(pipe2[0])) {
printf("close pipe error: %s", strerror(errno));
exit(EXIT_FAILURE);
}
server(pipe1[0], pipe2[1]); // child is a server
exit(EXIT_SUCCESS);
}
// using pipe2[0] for read and pipe1[1] for write
// so close unused pipe1[0] and pipe2[1]
if (-1 == close(pipe1[0]) || -1 == close(pipe2[1])) {
printf("close pipe error: %s", strerror(errno));
exit(EXIT_FAILURE);
}
client(pipe2[0], pipe1[1]); // father is a client
if (-1 == waitpid(childfd, NULL, 0)) { // wait for child to terminate
perror("waitpid error");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}