首页 > 解决方案 > 如何在 C++ 中使用管道构建聊天程序

问题描述

我想实现一个运行 chatter 1 或 chatter 2 的小程序,这取决于调用 main 时的参数输入。打开 2 个终端后,chatter 1,chatter 2 向上,我希望在 2 个终端之间发送小字符串消息。

我尝试实现一个简单的管道程序,其中一个进程将以读取或写入的方式打开管道。然后,如果您不是出于其他目的的管道,则该过程将关闭相应的端、读端或写端。当我打开 2 个终端并使用参数 0 或 1 调用 main 时,我调用 2 个进程来打开管道。

但是,我无法运行程序以在 2 个进程之间连续发送消息。每当检测到键盘输入时,进程就会退出。

我怀疑我没有正确打开管道。(我的代码中没有打开函数)。open 函数还需要我输入管道的路径名,它位于 /proc/[PID]/fd 中。我看到每次运行程序时 PID 都会有所不同。那么,我怎样才能将它放入 open 函数的参数中。

请帮我修改我的代码。

#include <unistd.h>   /* to use pipe, getpid*/
#include <stdio.h>    /*to use prinf*/
#include <iostream>   /*to use cin, cout*/
#include <string.h>   /*to use str function*/
#include <errno.h>    /* to use with errno*/

int onServiceStart(int chatter, int readPipe[], int writePipe[]){
    if( close(readPipe[1]) == -1 ){   //if use the pipe to read, then close the write end of the pipe
        std::cout<<"Error closing the write end of read pipe"<<errno<<std::endl;
    };
    if( close(writePipe[0]) == -1 ){   //if use the pipe to write, then close the read end of the pipe
        std::cout<<"Error closing the read end of write pipe"<<errno<<std::endl;
    };
    while(true){
        //preparing to get the string from keyboard
        std::string chatInput;
        getline(std::cin, chatInput);
        //send the string to pipe
        if ( write( writePipe[1],chatInput.c_str(),strlen( chatInput.c_str() )) == -1) {
            std::cout<<"Error writing to write end of write pipe"<<errno<<std::endl;
        };
        //preparing the buffer to put the string to after reading from pipe
        char buffer;
        if ( read(readPipe[0],&buffer,1)== -1) {
            std::cout<<"Error reading from read end of read pipe"<<errno<<std::endl;
        };
        std::cout<<buffer;
    }
};

int main(int argc, char *argv[]){
    int chatter = *(argv[1]) - '0';
    int fileDescription1[2], fileDescription2[2] ;
    if ( pipe(fileDescription1)==-1 ) {
        printf("cannot create pipe 1");
    };
    if ( pipe(fileDescription2)==-1 ) {
        printf("cannot create pipe 2");
    };

    switch (chatter) {
        case 0:
            printf("PID %d, chatter 1: \n", getpid());
            onServiceStart(chatter,
                            fileDescription1,       //chatter1 will use fileDescription1 to read,
                            fileDescription2);     //chatter1 will use fileDescription2 to write,

        case 1:
            printf("PID %d, chatter 2: \n", getpid());
            onServiceStart(chatter,
                            fileDescription2,       //chatter2 will use fileDescription2 to read,
                            fileDescription1);    //chatter2 will use fileDescription1 to write,
    }
}

标签: c++pipeipc

解决方案


有几个问题。进程立即退出的原因是因为您的程序只运行管道的一侧,而不是另一侧。chatter = 0例如,如果您使用fileDescription2for运行它writePipe,但您要做的第一件事onServiceStart()close()读取writePipe. 这意味着无论何时您写入writePipe[1],您都会收到 EPIPE 错误,因为writePipe[0]它已关闭。

如果打算启动程序的两个实例,一个以 with作为参数调用0,另一个以更好,因为您只需要一个。1

请注意,创建的一对匿名管道pipe()仅在您的程序分叉或创建多个线程时才有用。

另一个问题是您没有从管道另一端读取整个响应:

//preparing the buffer to put the string to after reading from pipe
char buffer;
if ( read(readPipe[0],&buffer,1)== -1) {
    ...

这只读取一个字符。之后,您再次开始从标准输入读取,因此您发送的每一行只会收到一个字符,这显然不是您想要的。

处理这个问题的正确方法是通过传递标志使管道非阻塞O_NONBLOCK,并使用select()orpoll()循环同时等待来自标准输入和管道的数据。


推荐阅读