-2

I need to write two programs in C. The first one is the client-server program with two one-way links and the second one it's the same but using two-way communication (socet), so it has to be bidirectional. User type the name of file or file path in stdin and the client function has to read that, send to server function. Then server has to read the contents of the file, send it back to the client and the client function display that content on screen (stdout). The first program I wrote:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#define MAXBUFF 1024

void err_sys(const char* x){
  perror(x);
  exit(1);
}

void client(readfd, writefd)
int readfd;
int writefd;
{
  char buff[MAXBUFF];
  int n;
  if(fgets(buff, MAXBUFF, stdin)==NULL)
    err_sys("client: filename read error");
  n=strlen(buff);
  if(buff[n-1]=='\n')
    n--;
  if(write(writefd, buff, n)!=n)
    err_sys("client: filename write error");
  while((n=read(readfd, buff, MAXBUFF))>0)
    if(write(1, buff, n)!=n)
        err_sys("client: data write error");
  if(n<0)
    err_sys("client: data read error");
}

void server(readfd, writefd)
int readfd;
int writefd;
{
  char buff[MAXBUFF];
  char errmesg[256];
  int n, fd;
  if((n=read(readfd, buff, MAXBUFF))<=0)
    err_sys("server: filename read error");
  buff[n]='\0';
  if((fd=open(buff, 0))<0){
    if(write(writefd, "error", 5)!=5)
        err_sys("server: errmesg write error");
  }
  else {
    while((n=read(fd, buff, MAXBUFF))>0)
        if(write(writefd, buff, n)!=n)
            err_sys("server: data write error");
    if(n<0)
    err_sys("server: read error");
 }
}
main(){
    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]);
      while(wait((int *) 0) != childpid);
      close(pipe1[1]);
      close(pipe2[0]);
      exit(0);
   }
   else{
     close(pipe1[1]);
     close(pipe2[0]);
     server(pipe1[0], pipe2[1]);

     close(pipe1[0]);
     close(pipe2[1]);
     exit(0);
   }
}

but now I have to modify that to using only one socet (bidirectional). I guess I have to use the pipe() function once, but anyone knows how to do that exaclty? I read somewhere I have to use the wait() function to be able to use file descriptors in a proper way ... So any kind of help, some tips or examples would be appreciated. Thank you in advance.

Kamil W.
  • 13
  • 6
  • Pipes are unidirectional. So obviously that means you need two. – Jonathon Reinhart Nov 26 '17 at 13:13
  • Ok, but I want to have an only one communication link in two directions – Kamil W. Nov 26 '17 at 13:16
  • Maybe this can help: https://stackoverflow.com/questions/6171552/popen-simultaneous-read-and-write or https://stackoverflow.com/questions/8390799/can-pipes-in-unix-work-bi-directionally – nempat Nov 26 '17 at 13:16
  • So I shouldn't use pipes? I read that one pipe with *wait()* function could give a desired effect ... – Kamil W. Nov 26 '17 at 13:33
  • 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.) – Jonathan Leffler Dec 21 '17 at 05:59

2 Answers2

0

Ok, I changed the body of main function like this:

main(){
int childpid, pipedes[2];
if(pipe(pipedes)<0)
    err_sys("can't create  pipes");
if((childpid=fork())<0){
    err_sys("can't fork");
}
else if (childpid>0){
    client(pipedes[0], pipedes[1]);
    close(pipedes[0]);
    close(pipedes[1]);
    exit(0);
}
else{
    wait(childpid);
    server(pipedes[0], pipedes[1]);
    close(pipedes[1]);
    close(pipedes[0]);
    exit(0);
}
}

The program read the name of file or file path, read the content of file and write it on stdout, but after that I can still write something (in stdin), the program is not stopped. It looks like the pipe is still open. What is wrong in my code?

Kamil W.
  • 13
  • 6
0

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
$
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278