【问题标题】:Trying to pipe data from a child-process server to its parent process试图将数据从子进程服务器传送到其父进程
【发布时间】:2011-11-22 03:32:08
【问题描述】:

我正在为我的分布式系统课程做作业。我是 C.S. 的硕士生,但我的编程专长是 .NET,我正在从事一个需要一些相当多的 Unix 知识的项目,这让我很受挫。

任务正在实现一个刷新通道协议 API。所以我正在编写一个小型函数库,其他应用程序可以实现该函数库以使用刷新通道通信。我已经对其进行了设置,以便在调用 init 函数时,它会派生一个子进程来充当传入消息的服务器。子进程通过管道将传入数据发送给父进程,从而与父进程通信。

如果一次发送和接收一条消息,这可以正常工作;例如,

发送 -> 接收 -> 发送 -> 接收 -> 等等。

但是,如果在接收之前发送了多条消息;例如,

发送->发送->发送->接收

然后就搞砸了。具体来说,第一条消息接收正确,但是当我去接收第二条消息时,程序挂了,需要被杀死。我已经在网上进行了大量搜索,并为此花费了几个小时,但没有取得太大进展。

整个程序太大,无法在此处显示,但这里是最相关的部分。这是我让服务器运行并接收消息的部分。注意这一行

写(fd[1], 缓冲区, (strlen(buffer)+1));

-- 我认为这是成为问题根源的好人选,但不知道该怎么做。 (尝试了 fwrite() 并且根本不起作用。)

    fd = malloc(2 * sizeof(int));
    int nbytes;
    if (pipe(fd) < 0) {
        perror("Could not create pipe");
        return -1;
    }

    pID = fork();

    if (pID < 0) {
        perror("Failed to fork");
        return -1;
    } else if (pID == 0) { // child
        close(fd[0]);  // close input side of pipe
        int cc;
        int fsize;
        struct sockaddr_in from;
        int serials[500];
        int i;
        for (i = 0; i < 500; i++) serials[i] = 0;

        char buffer[2048];

        while (1) {
            fsize = sizeof(from);
            cc = recvfrom(socketNo, buffer, 2048, 0, (struct sockaddr*)&from, &fsize);
            if (cc < 0) perror("Receive error");
            datagram data = decodeDatagram(buffer);
            if (serials[data.serial] == 0) {
                write(fd[1], buffer, (strlen(buffer)+1));
                serials[data.serial] = 1;
            }
        }
    } else { // parent
        close(fd[1]);  // close output side of pipe
        return 0;
    }

(“序列”数组用于不转发重复的消息,因为消息被多次发送以提高可靠性。我知道这个数组的固定大小不是好的做法,但是这个分配的测试不会发送那个很多消息,所以在这种情况下没关系。)

接收函数的开头是这样的:

int fRecv(int* id, char* buf, int nbytes) {

    checkDatagramTable(*id);

    char* tbuf = malloc((nbytes + 9) * sizeof(char));
    int rbytes = read(fd[0], tbuf, nbytes + 9);

“+9”用于容纳与要发送的消息一起打包的附加信息,用于刷新通道排序。这也是一个相当粗略的区域,但分配更多空间以更加确定并没有解决问题。

我知道这里有很多无关的东西,对其他函数的引用等。但问题肯定在于我如何通过管道传输数据,所以我的问题的根源应该在某个地方。

提前感谢您的帮助;真的很感激。

【问题讨论】:

  • 你每次通过while(1)循环分配一个新的buffer——为什么?我不明白为什么它是动态分配的而不是堆栈分配的,我不明白为什么它会在这个函数之外持续存在。
  • 顺便说一下,strace(1) 在尝试追踪问题时可能真的很有帮助。这就像每个系统调用上的免费printf(3) 行。
  • 我不记得我为什么这样做,但显然有一个很好的理由,因为将其更改为堆栈分配会导致编译器发出一些看起来非常不祥的警告。为什么?您认为这与我遇到的问题有关吗?
  • 我很好奇你收到的警告——但这不应该是你遇到的错误,它只是一个简单的内存泄漏。您可能甚至不会注意到几十万条消息。
  • 原来警告是由于我搞砸了更改。在凌晨 2 点 30 分编辑代码并不是一个好主意——我想我会在早上睡觉并解决这个问题。

标签: c unix pipe


【解决方案1】:

这看起来很可疑。 (数据包中有什么?它们可能是二进制的)数据报的类型定义在哪里?

fsize = sizeof(from);
        cc = recvfrom(socketNo, buffer, 2048, 0, (struct sockaddr*)&from, &fsize);
        if (cc < 0) perror("Receive error");
        datagram data = decodeDatagram(buffer);
        if (serials[data.serial] == 0) {
            write(fd[1], buffer, (strlen(buffer)+1)); // <-- ????
            serials[data.serial] = 1;
        }

我会尝试:

            write(fd[1], buffer, cc);

更新:

如果消息不是空终止的,您必须明确终止它:

    (if cc == 2048) cc -= 1; 
    buffer [cc] = '\0'; // <<--
    datagram data = decodedatagram(buffer);
    ...

另外,建议使用“sizeof buffer”而不是“2048”。

更新2: 您可以通过以下方式测试数据包中的字符串是否真的以空值结尾:

        unsigned pos;
        cc = recvfrom(socketNo, buffer, 2048, 0, (struct sockaddr*)&from, &fsize);
        if (cc < 0) perror("Receive error");
        for pos=0; pos < cc; pos++) {
             if (buff[pos] == 0) break;
             }
        switch (cc-pos) {
        case 0: fprintf (stderr, "No nul byte found in packet: I lose!\n" ); break;
        default: fprintf (stderr, "Spurious nul byte found in the middle of packet\n" );
        case 1: break;
             }

        datagram data = decodeDatagram(buffer);
        if (serials[data.serial] == 0) {
            write(fd[1], buffer, cc);
            serials[data.serial] = 1;
        }

【讨论】:

  • 包保证是字符数据;这是作业描述的一部分——抱歉,我应该在我的问题中说明这一点。无论如何,在更改它之后,它的行为是相同的——当数据按顺序发送和接收时工作正常,但是当在用户应用程序开始接收它们之前一次发送一堆数据包时,它在尝试检索时挂起第二个数据包。
  • 它们可能是字符,但它们是否以空值结尾?发件人是否在每个数据包的末尾明确放置了一个“\0”?此外:如果您知道数据包是空终止的,为什么调用 strlen()?
  • 这些都是好点,但正如我所说,我将线路更改为您的建议,但并没有改变行为。
  • 您还没有回答“收到的ascii数据是否为空终止”的问题?无论如何:我更新了我的答案。
  • 是的。这就是为什么它是 strlen + 1 而不是 strlen。
猜你喜欢
  • 2016-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多