【问题标题】:IPC using fork() and pipe()使用 fork() 和 pipe() 的 IPC
【发布时间】:2016-10-24 21:29:16
【问题描述】:

我正在尝试使用管道模拟呼叫者和接收者之间的对话。我正在分叉一个进程并使父进程成为接收者,子进程成为调用者。

代码如下:

#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;
}

但是当我运行它时,我得到了这个意外的输出:

Caller: Hi
Received Hi

HI
Hello
Bye!
^C

接收器停止工作,它没有接收到我提供的输入。输出中还会出现额外的换行符。为什么会出现这种情况?

编辑: 正如 Dmitri 所指出的,我已经更改了调用者中的 strcmp 测试和接收者中的 printf 语句。

#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;
}

但收到“Bye!”后仍然没有退出。

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

【问题讨论】:

  • fgets() 在行尾包含换行符,如果缓冲区足够大的话。
  • 另外,如果buffer 包含"Bye!",您将end_call 设置为true
  • @Dmitri:好的,但是为什么它在第一条消息之后没有退出呢?

标签: c unix pipe output fork


【解决方案1】:

strcmp() 成功时返回 0。但是您的代码还有其他几个问题:

  1. 字符串永远不会等于“Bye!”,将附加一个新行以及指示字符串结尾的空字符(总共 6 个字符)。

  2. 管道使用流而不是“数据包”,您永远不知道从一次调用 read() 中会收到多少字节。它可能是不完整的字符串,或者如果数据发送速度非常快,您可能会得到 2 个相互粘合的字符串。您需要实现自己的“协议”来解析流中的数据。

  3. 您没有检查管道是否在另一侧关闭(读取将返回 0)

  4. 你会在输出中得到额外的新行,因为它附加到 fgets() 读取的字符串上

  5. 输出可能会混乱,因为您无法控制进程何时刷新到标准输出(有点竞争条件,但不会崩溃)。

【讨论】:

    【解决方案2】:

    由于fgets 捕获了换行符,您需要测试包含该换行符的Bye!\n 的退出条件。 strcmp(buffer, "Bye!\n")

    这是对@blackpen 指出的否定问题的补充。

    【讨论】:

      【解决方案3】:

      您的程序有几个问题。

      首先,当fgets() 读取一行时,会包含末尾的换行符(如果缓冲区中有足够的空间)。您看到的额外换行符是因为您发送的字符串包含一个,然后在接收器中打印时添加另一个。此外,您正在寻找字符串"Bye!" 来决定何时退出......但您实际得到的字符串是"Bye!\n"。您需要从发送者的stdin 读取的字符串末尾去除换行符,或者在打印和比较时考虑字符串中已经存在的换行符。

      其次,在发件人中,您在检查何时退出时逻辑颠倒了:当buffer 包含"Bye!" 时,您设置end_call = true;,而不是它时做。这会导致发送者在发送第一个字符串后退出而不是循环(此外,需要修复比较以考虑换行符,如上所述)。提前停止的不是 receiver,而是发送者...由于下一个问题,接收者永远运行。

      在接收器中,您在循环结束时清除缓冲区,就在您在循环测试中检查"Bye!" 之前。这可以防止循环测试中的比较找到匹配项,因此循环是无限的。在循环开始时清除 fgets() 之前的缓冲区。 (再一次,修复比较以考虑最后的换行符)。

      【讨论】:

        【解决方案4】:

        将客户端中的测试更改为以下内容。这将让孩子/父母交流。

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

        客户端在它读取的缓冲区中有换行符(“再见!”后跟换行符)。这与退出测试不匹配。您可以尝试删除它。

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

        在服务器端,不要初始化缓冲区(就在 while-check 之前)。因为这会重新初始化您要检查的字符串。

             strcpy(buffer, "");
        

        如果需要,您可以在运行 read 函数之前将其放入。

        正如其他答案所指出的,您还可能遇到缓冲问题(匹配结束条件)和竞争条件(关闭 fds)。但是,我想这对于“学习”来说已经足够了。祝你好运。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-28
          • 1970-01-01
          • 1970-01-01
          • 2014-05-31
          • 1970-01-01
          • 2014-06-09
          相关资源
          最近更新 更多