IPC使用fork()和pipe()

时间:2016-10-24 21:29:16

标签: c unix pipe output fork

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

以下是代码:

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

但是在收到&#34; Bye!&#34;。

之后仍然没有退出
Caller: hi
Received hi
Caller: Hello
Received Hello
Caller: Bye!
Received Bye!
Caller: Bye!
Received Bye!
Caller: ^C

4 个答案:

答案 0 :(得分:2)

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

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

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

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

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

  5. 输出可能会混乱,因为您无法控制进程刷新到stdout的时间(某种竞赛条件,但不会崩溃)。

答案 1 :(得分:1)

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

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

客户端在其读取的缓冲区中有换行符(“换句话!”后面跟换行符)。这不符合退出测试。您可以尝试将其删除。

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

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

     strcpy(buffer, "");

如果您愿意,可以在运行read功能之前加上它。

如其他答案所指出的,您也可能遇到问题缓冲问题(匹配结束条件)和竞争条件(关闭fds)。但是,我想这只是“学习”的例子。祝你好运。

答案 2 :(得分:1)

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

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

答案 3 :(得分:1)

您的计划有几个问题。

首先,当fgets()读取一行时,包含末尾的换行符(如果缓冲区中有足够的空格)。您看到的额外换行是因为您发送的字符串包含一个,然后在接收器中打印时添加另一个。此外,您正在查找字符串"Bye!"以决定何时退出...但您实际获得的字符串为"Bye!\n"。您需要在发件人中从stdin读取的字符串末尾删除换行符,或者在打印时和换算中对换行符中的换行符进行说明。

其次,在发件人中,在检查何时退出时,您的逻辑被颠倒了:当end_call = true; 不包含时,您需要设置buffer "Bye!",而不是它。这导致发送者在发送第一个字符串而不是循环之后退出(同样,需要将比较修复为如上所述的换行的帐户)。它不是早期停止的接收器,它是发送者......由于下一个问题,接收者将永远运行。

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