Trouble with pipes in chat client on linux

I'm writing a simple chat client in C++ on linux to connect to a win32 chat server on my computer also written in C++. I'm confident that the server works but the chat client is giving me some trouble. I'm forking the chat client and have one process dealing with incoming messages and another dealing with sending messages. I'm using two pipes in the chat client so each process can tell the other when it has terminated so the other process can go ahead and terminate. The pipes don't seem to be working though and I'm not sure where I'm going wrong. If you guys could take a look at my code I'd appreciate it. Maybe one of you will notice something I'm not doing right. Here's my code:


#define ChildRead pipefd1[0]
#define ChildWrite pipefd2[1]
#define ParentRead pipefd2[0]
#define ParentWrite pipefd1[1]

int main()
{
  int nret, theSocket, theMode = 1, pipefd1[2], pipefd2[2];
  struct sockaddr_in server;
  char buf[2] = {0}, message[999] = {0}, ipAddy[20]= {0}, scrName[20] = {0};
  pid_t childpid;

  printf("Enter the IP address to connect to.\n\n");
  scanf("%s", ipAddy);

  printf("\nEnter your screen name.\n\n");
  scanf("%s", scrName);

  theSocket = socket(AF_INET, SOCK_STREAM, 0);

  if(theSocket < 0)
   printf("\nCouldn't create socket\n");

  memset(&server, 0, sizeof(server));
  server.sin_family = AF_INET;
  server.sin_addr.s_addr = inet_addr(ipAddy);
  server.sin_port = htons(8888);

  printf("\nAttempting to connect to %s\n", ipAddy);

  nret = connect(theSocket, (struct sockaddr *)&server, sizeof(server));

  if(nret < 0)
   printf("\nCouldn't connect\n");

  ioctl(theSocket, FIONBIO, &theMode);

  printf("\nConnected to server!\n\n");

  memset(message, 0, strlen(message));

  pipe(pipefd1);
  pipe(pipefd2);

  fcntl(ParentRead, F_SETFL, O_NONBLOCK);
  fcntl(ParentWrite, F_SETFL, O_NONBLOCK);
  fcntl(ChildRead, F_SETFL, O_NONBLOCK);
  fcntl(ChildWrite, F_SETFL, O_NONBLOCK);

  childpid = fork();

  if(childpid == 0)
  {
    close(ParentRead);
    close(ParentWrite);

    while(1)
    {
      read(ChildRead, &buf, 1);

      if(buf[0] == 'q')
      {
       close(ChildRead);
       close(ChildWrite);
       close(theSocket);
       exit(0);
      }

      if((nret = recv(theSocket, message, 999, 0)) < 1)
       continue; 

      if(message[7] == '~' && message[8] == 'q')
      {
        printf("\nChat server disconnected..\n");  
        write(ChildWrite, "q\0", 1);
        close(ChildRead);
        close(ChildWrite); 
        close(theSocket); 
        exit(0);
      }

      printf("%s\n", message);

      memset(message, 0, strlen(message));
    }
  }
  else
  {  
     strcpy(message, scrName);
     strcpy(&message[strlen(message)], ": ");

     close(ChildWrite);
     close(ChildRead);

     while(1)
     {
       read(ParentRead, &buf, 1);

       if(buf[0] == 'q')
       {
         close(ParentRead);
         close(ParentWrite);   
         close(theSocket);
         exit(0); 
       }

       scanf("%s", &message[strlen(message)]);

       if(strcmp(&message[strlen(scrName) + 2], "~q\0") == 0)
       {
         write(ParentWrite, "q\0", 1);
         close(ParentRead);
         close(ParentWrite);
         close(theSocket);
         exit(0);          
       }

       send(theSocket, message, strlen(message), 0);

       memset(&message[strlen(scrName) + 2], 0, strlen(&message[strlen(scrName) + 2]));     
     }  
  } 

  return 0;
}

If you are going with a terminal interface you might want to consider redesigning the whole client to be single threaded. You do not really need to have separate process for handling I/O on a socket, you should read some tutorials on asynchronous I/O programming. The trick is to use select() function on your socket and on stdin descriptor to know when there is data from server or from keyboard to read and handle them one at a time. Having multiple processes or threads makes it very messy design when it can be much more clean and simple with a single thread.

Thank you expl for replying to my post. I'll look into the select function and doing what you suggest. I still would like to know why the pipes aren't working though. I think it would be good to know how to use pipes successfully.

In what way are the pipes "not working"? Always be specific.

Pipes buffer. You may not get a line out of the pipe the instant you write it in.

Well your code is pretty messy, Id sugest you start from 0 with a better design.
There are plenty of uncontrolled stuff happening specially with string buffer handling like so:

char buf[2] = {0}, message[999] = {0}

...

if((nret = recv(theSocket, message, 999, 0)) < 1)

...

memset(message, 0, strlen(message));

After the recv() call there is no way to make sure that 'message' is a proper 0 terminated string, so strlen() can go outside the buffer size and cause some weird stuff to happen after memset() runs. But this is probably not the major cause of problem for you.

Your client probably is not working because the parent process (childpid != 0) calls exit() before waiting for the child to clean up. If parent exits while child is still running kernel will send SIGINT to your child that will cause premature termination of it. You either need to call wait() function on the child or implement a better design.

I thought if childpid was zero you're in the child process. The problem I'm dealing with right now is that the child process is terminating but the parent is not. I write a q character to the pipe to inform the parent to terminate, but the parent apparently never gets the q on the read end of the pipe. I keeping polling the pipe but it never gets the q character.

As per my last post:

You don't need a pipe to know when the child dies anyhow; you get sent a SIGCHLD signal when it does. Catch that to handle when the child quits.

Pipe should flush if you close it after writing to it.

That is correct, it was a typo from me.

Ok looking into this deeper problem seems to be here:

read(ParentRead, &buf, 1);

Do you see whats wrong with this line?

Which system behaves in this way? To my knowledge, no UNIX-like system sends children a signal when their parent dies. The only thing that happens is that the child is adopted by another process (usually PID 1, usually init).

The only behavior similar to that which you've described is when a child uses Linux's prctl(2) with the PR_SET_PDEATHSIG option. This instructs the kernel to send the chosen signal to that child when its parent dies. However, this is not default behavior; it is non-standard; the signal chosen need not be SIGINT; and the signal value is cleared at every fork(2), so even if a child is set to be notified, its children won't be unless they also call prctl(2).

In this thread's particular case, if the child receives a signal because of its parent's death, it'll likely be a SIGPIPE from the child writing to a broken pipe.

Regards,
Alister

This points to an interesting planning issue: The parent should supervise the children, not the other way around (duh), so the parent can get SIG child if the child goes wrong. I suppose in shell you might even be able to trap that signal.

It is one of the limittions of UNIX piping that something upstream like a sort or find will run on mindless of the loss of processes downstream, until it tries to write something. I even tried writing 0 length messages to the pipe to see if the downstream was still there, but to no avail, or worse, false EOF detection from read() returning zero. So, if it is critical, you need a watchdog or cleanup process. Children can have children, ad infinitum, complicating the process. One way to identify the whole tree is to redirect stderr to a log file, so fuser can tell you who is attached.

You can, but then you get sigchild for every child that quits, including things like mv and cat, not just subshells.

Forgive my stupidity but I don't. I've never used a pipe before so I'm rather ignorant of exactly how interprocess communication works.

---------- Post updated at 11:41 PM ---------- Previous update was at 11:33 PM ----------

I think I see now what you were getting at expl. I was giving the read function the address of an address. Sorry about that. I went into the code, got rid of the ampersand, and rebuilt the program but to no avail. Something is still wrong.

---------- Post updated 10-20-12 at 06:47 PM ---------- Previous update was 10-19-12 at 11:41 PM ----------

Ok, I feel dumb. As it turns out the calls to write to and read from the pipes are working. I forgot that scanf was a blocking function call. Sorry for wasting your time guys, and thank you for all your help.

Yes, but you can keep track of which PIDs you are tracking and just using wait() (not writing signal catchers), can filter child responses for what pids and what return codes you care about. In bash, you can devote a watcher wait subshell for each background process pid $! saved as you launch them, each retrieving the exit code from that child.

It's nothing to do with pipes, it's to do with how arrays work. &buf is overdoing it, it's a pointer already.

BTW, pipes do not buffer/delay write() data, but FILE* do delay output until you flush or overflow the buffer, unless you turn on unbuffered or line buffered output (setvbuf()). Graceful exit() flushes all pipes on all but the most crude C compilers.

Pipes are part of IPC, along with signals, shared memory, semaphores, message queues, mmap() of files, sockets. You can inherit a fd such as a pipe or socket from a parent. You can open a named pipe and if someone opens the opposite way (r/w), you are connected 1-1. BTW, you can pass fd down a pipe so another process can attach it. I prefer mmap(): no root requirement, all RAM as a buffer, hard copy after a crash and for a restart, no swap used. Some UNIX/LINUX flavors map socket resources into the file tree. UDP Sockets are neat, as they can broadcast and multicast. A really slick messaging app would multicast, so it scaled really well. When studying IPC, make sure your opinions do not come from way back. Systems have become quite different, and culturally, apps less root entangled.