/* Ajith - Syntax Higlighter - End ----------------------------------------------- */

9.16.2008

Client Server example with PIPES

This example shows a combined use of unnamed and named pipes to produce a client–server relationship. Let us discuss first about the PROBLEM we are going to try.

There is a single Server process which runs continuously in background eventhough if there is no client to interact with it. Client processes runs in foreground and interacts with the server process. Both the client and server processes will run on the same machine (So server process will run background forever until it is manually killed).

The Client process accepts a command (probably a shell command) from the user and send's it to the Server via a FIFO which is a public channel between Client and Server for processing. Let us name this FIFO as PUBLIC fifo since its existence is known to all clients and the server. Once the command is received, the Server executes it using the popen–pclose sequence (which generates an unnamed pipe in the Server process). After execution Server process should return the output of the command executed to the client over a FIFO which is a private channel between the client and server. Let us name this FIFO as PRIVATE fifo. The Client, upon receipt, displays the output on the screen.

NOTE: Each and every Client process should have its own unique PRIVATE fifo to receive information from Server.

Check out the diagrammatic representation of the PROBLEM.


To ensure that both Server and Client processes use the same PUBLIC fifo name and have the same message format for the data sent through the FIFO, a local header file named local.h is created.
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/limits.h>
#include <string.h>
#include <stdio.h>

#define B_SIZE (PIPE_BUF/2)

const char *PUBLIC = "/tmp/PUBLIC";

struct message {
char fifo_name[B_SIZE];
char cmd_line[B_SIZE];
};

The format of the message sent over the PUBLIC fifo is declared within the struct statement. The message structure consists of two character array's namely fifo_name, stores the name of the PRIVATE fifo of the Client process and cmd_line, stores the command to be executed by the Server.

Let us checkout the functionality steps of a newly created Client process.
  [1].  Create a unique name for the PRIVATE fifo and invoke it.
  [2].  Open the PUBLIC fifo in write mode.
  [3].  Prompt for command from user.
  [4].  Write command to PUBLIC fifo for Server to process.
  [5].  Open PRIVATE fifo in read mode to read the contents from Server..
#include "local.h"
int main()
{
int publicfifo, privatefifo, n;
static char buffer[PIPE_BUF];
struct message msg;

/*Using sprintf to create a unique fifo name
and save into message structure*/
sprintf(msg.fifo_name, "/tmp/fifo%d", getpid());

/*Creating the PRIVATE fifo*/
if(mknod(msg.fifo_name, S_IFIFO | 0666, 0) < 0) {
  perror(msg.fifo_name);
  exit(1);
}

/*Opening PUBLIC fifo in WRITE ONLY mode*/
if((publicfifo = open(PUBLIC,O_WRONLY)) < 0) {
  unlink(msg.fifo_name);
  perror(PUBLIC);
exit(1);
}

while(1) {

  write(fileno(stdout), "\n cmd>", 6);
  memset(msg.cmd_line, 0x0, B_SIZE);
  n = read(fileno(stdin), msg.cmd_line, B_SIZE);

if(strncmp("quit", msg.cmd_line, n-1) == 0) {
  break;
}

write(publicfifo, &msg, sizeof(msg));

if((privatefifo = open(msg.fifo_name, O_RDONLY)) < 0) {
  printf("1\n");
  perror(msg.fifo_name);
  goto CLEANUP;
}

while((n = read(privatefifo, buffer, PIPE_BUF)) > 0) {
  write(fileno(stderr), buffer, n);
}

close(privatefifo);
}

CLEANUP:
close(publicfifo);
unlink(msg.fifo_name);

return 0;
}

Now we will check the Server process.
  [1].  Generate a PUBLIC fifo and open it in both read and write mode. Wait for the message from a Client process.
  [2].  Read the message from PUBLIC fifo.
  [3].  Open the Client's PRIVATE fifo in write mode.
  [4].  Execute the command from Client process using popen.
  [5].  Write the output into Client's PRIVATE fifo.
#include "local.h"

int main() {

int privatefifo, dummyfifo, publicfifo, n, done;
struct message msg;
FILE *fin;
static char buffer[PIPE_BUF];

/*creating the PUBLIC fifo*/
mknod(PUBLIC, S_IFIFO | 0666, 0);

/*
Server process opens the PUBLIC fifo in write mode to make sure that
the PUBLIC fifo is associated with atleast one WRITER process. As a
result it never receives EOF on the PUBLIC fifo. The server process
will block any empty PUBLIC fifo waiting for additional messages to
be written. This technique saves us from having to close and reopen
the public FIFO every time a client process finishes its activities.
*/

if( (publicfifo = open(PUBLIC, O_RDONLY)) < 0 ||
(dummyfifo = open(PUBLIC, O_WRONLY | O_NDELAY)) < 0) {
   perror(PUBLIC);
   exit(1);
}

/*Read the message from PUBLIC fifo*/
while(read(publicfifo, &msg, sizeof(msg)) > 0) {

n=0;
done=0;

do {
  if((privatefifo = open(msg.fifo_name, O_WRONLY|O_NDELAY)) == -1) {
    sleep(5);
  }
  else {
    fin = popen(msg.cmd_line, "r");
    write(privatefifo,"\n",1);

    while((n= read(fileno(fin), buffer, PIPE_BUF)) > 0) {
      write(privatefifo, buffer, n);
      memset(buffer, 0x0, PIPE_BUF);
    }

    pclose(fin);
    close(privatefifo);
    done = 1;
  }
}while(n++ < 5 && !done);

if(!done) {
  perror("Not accessed the private fifo\n");
  exit(1);
}

}
return 0;
}

Compile server.c and client.c and generate server, client executable's. A sample execution of the client-server programs is shown below

[bash]$./server &                                  
[1] 27107

[bash]$./client
cmd>ps
PID TTY TIME CMD
14736 pts/3 00:00:00 csh
27107 pts/3 00:00:00 server
27108 pts/3 00:00:00 client
27109 pts/3 00:00:00 6
cmd>who
gray pts/3 Feb 27 11:28
cmd>quit

$./kill -9 27107
[1] Killed server
$

10 comments :

  1. Great tutorial!
    After some desperation of ambiguity in describing pipe's notion in
    Advanced Programming in the UNIX® Environment: Second Edition
    I came to this wonderful page! Great stuff!

    ReplyDelete
  2. thank you....i really needed this one.....

    ReplyDelete
  3. Thankyou ... Finished my assignment because of this. You are a real life saver.

    ReplyDelete
  4. Yes this example really covers good number of points i really appreciate for your wonderful work.

    ReplyDelete
  5. Very good, I've solved a half year I've been on this task. thank you very much

    ReplyDelete
  6. A huge thinks :)
    it helps to my project in LINUX

    ReplyDelete
  7. very helpful to complete my assignment . . . :)

    ReplyDelete
  8. Thanks, very helpful. If it still remains, would be nice if you reload the diagrammatic representation of the communication. Thanks.

    ReplyDelete
  9. Thanks but there is one problem with the program.......the client runs the commands and gives output even though there is no server!!!

    ReplyDelete

Your comments are moderated