views:

2951

answers:

1

I am writing a server which fork()'s off a child process when it accepts a socket connection.

As the child communicates with a client, it must send some of that information back to the parent. I am using a pipe to accomplish this.

The problem is that when I try to do the parent-child IPC, the parent blocks when reading input from the child. Which means that, even though the children are running concurrently, they can only be processed one-at-a-time because they are all waiting for the parent.

My code looks something like this (error-checking removed for brevity):

/* loop: wait for clients to connect */
for(;;) {

  pipe(write_to_parent); /* create pipe between parent and child */
  pipe(write_to_child);  /* create pipe between parent and child */

  newsockfd = accept(...);

  pid = fork();

  if (pid == 0) { /* child process */
      close(write_to_parent[0]);
      close(write_to_child[1]);

     printf("Connected!\n");

     while ((rc = recv(newsockfd, ...)) > 0) {
         /* process socket request */

         /* write stuff to parent that is related to what we recv'd from client */
         rc = write(write_to_parent[1], &buf, 1024);
     }

     printf("Disconnected!\n");

     exit(0);
  } else { /* parent */

      close(write_to_parent[1]);
      close(write_to_child[0]);

       while (read(write_to_parent[0], &buf, 1024) > 0) {
           /* accept data from child process, and do some processing */
       }
  }
}

So my question is, how do I remedy this? How can I have the parent communicating with a children using pipes in such a way that it is not blocking?

Is this even possible with pipes, or should I used shared memory (with a semaphore, i guess) or message queue instead? (I read this post: http://stackoverflow.com/questions/404604/comparing-unix-linux-ipc but it is really difficult to find examples how these tasks are actually accomplished.)

More details:

I have a test program that does the following: 1. Connect to the server 2. Sleep(5) 3. Disconnect from the server

When I run 2 instances of this program, the server outputs this:

connected!
  // pause for 5 seconds
disconnected!
connected!
  // pause for 5 seconds
disconnected!

Obviously processing each client one at a time.

When I remove the IPC - when I get rid of the pipe read() and write()'s from both the parent and child, I get this:

connected!
connected!
  // pause for 5ish seconds
disconnected!
disconnected!

Which is what I want!

Any thoughts on how I can accomplish this? (or changes I should make in the way I am going about solving this problem?)

(edit: This is part of an assignment for a networking class. We are implementing a P2P protocol that uses a centralized server to manage the peers. We could use any language, and I figured I'd give it a whirl in C .)

+2  A: 

I wrote the answer below and upon rereading your question, noticed that I had missed your actual question completely. However, it may be useful info in any case.

To answer your concurrent parent question, you will need to have the parent set up a select() loop to handle possible responses from any of its children at any time. You will need to:

  • keep track of how many children are open
  • keep a set of pipes for each child
  • use a loop with select() to both accept incoming connections (since they may happen at any time) and also incoming data from any child
  • use waitpid() to reap children who have terminated

This technique is powerful but does require a lot of bookkeeping to set up properly.


File handles opened in the parent are by default inherited in child processes. The problem may be that both the parent and the child have the write end of the pipe still open, so the when the parent reads from the pipe, it will never see the end of it. Try using a close(write_to_parent[1]) in the parent immediately before you start reading from write_to_parent[0]. So:

} else { /* parent */
     close(write_to_parent[1]);
     while (read(write_to_parent[0], &buf, 1024) > 0) {
         /* accept data from child process, and do some processing */
     }
}
Greg Hewgill
You're right about using close() - I had that in my code, but I had omitted it from this snippet quite unintentionally. I've added it back, however. Thank you!
rascher