简体   繁体   中英

IPC using fork() and pipe()

I am trying to simulate conversation between a caller and a receiver using pipes. I am forking a process and making the parent process the receiver and the child process the caller.

Here is the code:

#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>

#define BUF_LEN 25
#define READ_END 0
#define WRITE_END 1

int main()
{
    int fd[2];
    if (pipe(fd) == -1) {
        fprintf(stderr, "Pipe failed");
        return 1;
    }

    pid_t pid = fork();

    if (pid < 0) {
        fprintf(stderr, "Fork failed");
        return 1;
    } 

    // the parent process is the receiver
    if (pid > 0) {
        close(fd[WRITE_END]);
        char buffer[BUF_LEN + 1] = "";
        do {
            read(fd[READ_END], buffer, sizeof buffer);
            if (strcmp(buffer, "")) {
                printf("Received %s\n", buffer);
            }
            strcpy(buffer, "");
        } while (strcmp(buffer, "Bye!"));
        close(fd[READ_END]);
    } else {
        close(fd[READ_END]);
        // const char *msg = "Hello";
        char buffer[BUF_LEN + 1] = "";
        bool end_call = false;
        do {
            printf("Caller: ");
            fgets(buffer, sizeof buffer, stdin);
            if (strcmp(buffer, "Bye!")) {
                end_call = true;
            }
            // printf("Sent %s\n", buffer);
            write(fd[WRITE_END], buffer, strlen(buffer) + 1);
        } while (!end_call);
        close(fd[WRITE_END]);
    }
    return 0;
}

But when I run this, I get this unexpected output:

Caller: Hi
Received Hi

HI
Hello
Bye!
^C

The receiver stops working, it is not receiving the inputs I give. Also there are extra newlines appearing in the output. Why is this occuring?

Edit: As pointed by out Dmitri, I have changed the strcmp test in the caller and the printf statement in the receiver.

#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>

#define BUF_LEN 25
#define READ_END 0
#define WRITE_END 1

int main()
{
    int fd[2];
    if (pipe(fd) == -1) {
        fprintf(stderr, "Pipe failed"); return 1; }

    pid_t pid = fork();

    if (pid < 0) {
        fprintf(stderr, "Fork failed");
        return 1;
    } 

    // the parent process is the receiver
    if (pid > 0) {
        close(fd[WRITE_END]);
        char buffer[BUF_LEN + 1] = "";
        do {
            read(fd[READ_END], buffer, sizeof buffer);
            if (strcmp(buffer, "")) {
                printf("Received %s", buffer);
            }
            strcpy(buffer, "");
        } while (strcmp(buffer, "Bye!"));
        close(fd[READ_END]);
    } else {
        close(fd[READ_END]);
        // const char *msg = "Hello";
        char buffer[BUF_LEN + 1] = "";
        bool end_call = false;
        do {
            printf("Caller: ");
            fgets(buffer, sizeof buffer, stdin);
            if (!strcmp(buffer, "Bye!")) {
                end_call = true;
            }
            // printf("Sent %s\n", buffer);
            write(fd[WRITE_END], buffer, strlen(buffer) + 1);
        } while (!end_call);
        close(fd[WRITE_END]);
    }
    return 0;
}

But it is still not exiting after receiving "Bye!".

Caller: hi
Received hi
Caller: Hello
Received Hello
Caller: Bye!
Received Bye!
Caller: Bye!
Received Bye!
Caller: ^C

The strcmp() returns 0 on success. But there are also several other problems with your code:

  1. The string will never be equal to "Bye!", there is going to be a new line attached as well as the null character indicating the end of string (total of 6 chars).

  2. The pipes use streams not "packets" you never know how many bytes you will receive from one call to read(). It might be incomplete string or if the data is sent very fast you might get 2 strings glued to each other. You need to implement your own "protocol" to parse the data out of the stream.

  3. You are not checking if the pipe was closed on the other side (read would return 0)

  4. You get extra new line in the output because it is attached to the string read by fgets()

  5. Output may be messed up because you have no control on when processes flush to stdout (sort of a racing condition but it will not crash).

Change the test in client to the following. That will let the child/parent communicate.

    if (!strcmp(buffer, "Bye!")) {

The client has newline in the buffer it reads ("Bye!" following by a newline character). That fails to match exit test. You could try to remove it.

    fgets(buffer, sizeof buffer, stdin);
    buffer[strlen(buffer)-1]='\0';

In the server side, don't initialze the buffer (just before while-check). Because that would reinitialize the very string that you are about to check.

     strcpy(buffer, "");

If you want, you could put that just before you run the read function.

You may also get into problem buffering problem (in matching for end condition) and race conditions (while closing fds) as pointed out by other answers. But, I guess it is good enough example for just "learning". Good luck.

Because of the newline captured by fgets you need to test the exit condition for Bye!\\n which includes that newline. strcmp(buffer, "Bye!\\n")

This is in addition to fixing the negation issue pointed out by @blackpen.

Your program has several issues.

First, when fgets() reads a line, the newline at the end is included (if there was sufficient space in the buffer). The extra newlines you're seeing are because the string you send contains one, then you add another when you print it in the receiver. Also, you're looking for the string, "Bye!" to decide when to quit... but the string you actually get is "Bye!\\n" . You need to either strip the newline off the end of the string you read from stdin in the sender, or account for the newline already in the string when you print it and in your comparisons.

Second, in the sender, you have your logic inverted when checking when to quit: You're setting end_call = true; when buffer doesn't contain "Bye!" , instead of when it does. This causes the sender to exit after sending the first string rather than looping (also, the comparison needs to be fixed to account for the newline as mentioned above). It's not the receiver that stops early, it's the sender... the receiver runs forever due to the next issue.

In the receiver, you're clearing the buffer at the end of your loop, right before you check for "Bye!" in the loop test. That prevents the comparison in the loop test from ever finding a match, so the loop is infinite. Clear the buffer at the start of the loop before fgets() instead. (And once again, fix the comparison to account for the newline at the end).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM