Do not write archaic C
You should not, in the 21st Century, be writing ante-diluvian C with K&R-style function definitions, nor using 'implicit int' declarations for main()
. You should be compiling in C11 (or, at least C99) mode, where such things are not allowed. (Strictly, C11 still allows K&R-style functions, but you should never write them. Prototypes are vastly superior to K&R-style.)
Therefore, a function definition such as this is seriously awful code — instant failing grade in my book. You might need to recognize the notation, but you should never write it.
void client(readfd, writefd) // Wrong!
int readfd; // Archaic!
int writefd; // Do not use!
{
You should be using:
void client(int readfd, int writefd)
{
Similarly:
main() {
is awful; you should specify the return type explicitly and you should really specify that it takes no arguments with void
, as in:
int main(void) {
(The choice between int main()
and int main(void)
isn't quite as clear-cut as not omitting the int
— if you look in standard C, you will find a couple examples of int main()
, and several of int main(void)
. However, you will find no examples without the int
; that has not been valid standard C for the whole of the current millennium.)
Bidirectional communication needs two pipes
For sanity's sake, bidirectional communication needs two pipes. If, and only if, you can guarantee that the data to be sent by the child is small enough to fit in the pipe buffer (which might be as small as 4 KiB; historically, it was commonly 5 KiB, but on many modern systems it is 64 KiB), then you could use a single pipe, but without that guarantee, you cannot readily coordinate between two processes. As soon as you want to send multiple messages in each direction, two pipes are necessary (and you need a protocol so the writers can let the reader know when the end of the current message has been reached).
Here's some code that works. It is based on the code in your question, not the code in the 'answer' that isn't an answer. It was in a file pipe13.c
and compiled to pipe13
:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#define MAXBUFF 1024
static
void err_sys(const char *msg)
{
perror(msg);
exit(1);
}
static
void client(int readfd, int writefd)
{
char buff[MAXBUFF];
if (fgets(buff, MAXBUFF, stdin) == NULL)
err_sys("client: filename read error");
int n = strlen(buff);
if (buff[n - 1] == '\n')
buff[--n] = '\0';
/* Write the null byte too */
if (write(writefd, buff, n+1) != n+1)
err_sys("client: filename write error");
close(writefd);
while ((n = read(readfd, buff, MAXBUFF)) > 0)
{
static const char marker[] = "\n<client>:\n";
enum { MARKER_SZ = sizeof(marker) - 1 };
if (write(STDOUT_FILENO, marker, MARKER_SZ) != MARKER_SZ)
err_sys("client: marker write error");
if (write(STDOUT_FILENO, buff, n) != n)
err_sys("client: data write error");
}
static const char end[] = "\n</client>\n";
enum { END_SZ = sizeof(end) - 1 };
if (write(STDOUT_FILENO, end, END_SZ) != END_SZ)
err_sys("client: end marker write error");
close(readfd);
if (n < 0)
err_sys("client: data read error");
}
static
void server(int readfd, int writefd)
{
char buff[MAXBUFF];
int n, fd;
if ((n = read(readfd, buff, MAXBUFF)) <= 0)
err_sys("server: filename read error");
printf("server: filename [%s]\n", buff);
close(readfd);
if ((fd = open(buff, O_RDONLY)) < 0)
{
if (write(writefd, "error", 5) != 5)
err_sys("server: errmesg write error");
}
else
{
while ((n = read(fd, buff, MAXBUFF)) > 0)
{
static const char marker[] = "\n<server>:\n";
enum { MARKER_SZ = sizeof(marker) - 1 };
if (write(writefd, marker, MARKER_SZ) != MARKER_SZ)
err_sys("server: marker write error");
if (write(writefd, buff, n) != n)
err_sys("server: data write error");
}
static const char end[] = "\n</server>\n";
enum { END_SZ = sizeof(end) - 1 };
if (write(writefd, end, END_SZ) != END_SZ)
err_sys("server: end marker write error");
close(writefd);
if (n < 0)
err_sys("server: read error");
}
}
int main(void)
{
int childpid, pipe1[2], pipe2[2];
if (pipe(pipe1) < 0 || pipe(pipe2) < 0)
err_sys("can't create pipes");
if ((childpid = fork()) < 0)
err_sys("can't fork");
else if (childpid > 0)
{
close(pipe1[0]);
close(pipe2[1]);
client(pipe2[0], pipe1[1]);
}
else
{
close(pipe1[1]);
close(pipe2[0]);
server(pipe1[0], pipe2[1]);
}
int corpse;
int status;
while ((corpse = wait(&status)) != -1)
{
printf("%d: child PID %d exited with status 0x%.4X\n",
(int)getpid(), corpse, status);
}
return 0;
}
When it was compiled and run on its own source code (using Bash):
$ make pipe13 && ./pipe13 <<< pipe13.c
gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes -Wstrict-prototypes pipe13.c -o pipe13
server: filename [pipe13.c]
<client>:
<server>:
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
…
static const char end[]
<client>:
= "\n</clie
<server>:
nt>\n";
enum { END_SZ = sizeof(end) - 1 };
if (write(STDOUT_FILENO, end, END_SZ) != END_SZ)
err_sys("client: end marker write error");
…
if (write(writefd, buff, n) != n)
err_sys
<client>:
("server: data write e
<server>:
rror");
}
static const char end[] = "\n</server>\n";
…
return 0;
}
</server>
</client>
28996: child PID 28997 exited with status 0x0000
$