如何在 C++ 中使用管道构建聊天程序
Posted
技术标签:
【中文标题】如何在 C++ 中使用管道构建聊天程序【英文标题】:How to build a chat program with pipe in C++ 【发布时间】:2020-10-10 06:55:11 【问题描述】:我想实现一个运行 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++,而大部分代码是 C。你甚至设法混合 cout 和 printf。选择一种语言。 @JHBonarius 无需为此挑剔。可改进的、类似 c 的 C++ 仍然是 C++。 【参考方案1】:有几个问题。进程立即退出的原因是因为您的程序只运行管道的一侧,而不是另一侧。例如,如果您使用chatter = 0
运行它,您使用fileDescription2
作为writePipe
,但您在onServiceStart()
中做的第一件事是close()
writePipe
的读取端。这意味着无论何时你写信给writePipe[1]
,你都会收到一个EPIPE 错误,因为writePipe[0]
已关闭。
如果打算启动程序的两个实例,一个以0
作为参数调用,另一个以1
调用,那么您需要使用一对named pipes 在它们之间进行通信,尽管如果如果您想要双向通信,命名为 UNIX socket 会更好,因为您只需要一个。
请注意,使用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()
或poll()
循环同时等待来自标准输入和管道的数据。
【讨论】:
谢谢!!!我现在改用命名管道,并将合并您提到的问题的修复程序。谢谢【参考方案2】:我改用命名管道。未命名管道不能用于2个独立进程之间的通信,只能用于父子进程。
由于管道是单向的,我必须使用 2 个管道。对于每个进程,管道将用作只读或只写。例如进程0只以只读方式打开管道1,只会调用read()函数读取管道。
由于它被命名为管道,我必须手动关闭并删除管道。在这段代码中,我还没有实现它。它需要一个信号捕获机制来在关闭或“Crtl+C”程序时删除管道。如果您在 main 0 之前运行 main 1 也会导致一个小错误。不过,下面的这段小代码演示了管道的基础。
这是我的代码。
#include <unistd.h> /* to use pipe, getpid */
#include <sys/stat.h> /* to use named pipe mkfifo */
#include <sys/types.h> /* to use open() */
#include <fcntl.h> /* to use open() */
#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 */
#include <thread> /* to use thread */
#define PIPE1_PATH "/home/phongdang/pipe1"
#define PIPE2_PATH "/home/phongdang/pipe2"
int openPipe(const char* pipePathName, int flag)
int fileDescriptor;
fileDescriptor = open(pipePathName, flag) ;
if ( fileDescriptor == -1)
std::cout<<"Error open pipe at "<<pipePathName<<" .Error code: "<<errno<<std::endl;
;
return fileDescriptor;
void writePipe(int writePipeDescriptor)
while (true)
//preparing to get the string from keyboard
std::string chatInput;
getline(std::cin, chatInput);
int writeBytes = write( writePipeDescriptor,chatInput.c_str(),strlen( chatInput.c_str() ));
//send the string to pipe
if ( writeBytes == -1)
std::cout<<"Error writing to write end of write pipe"<<errno<<std::endl;
else
printf("Writing to pipe %d bytes \n", writeBytes);
sleep(1);
void readPipe(int readPipeDescriptor)
char buffer[100];
while (true)
//preparing the buffer to put the string to after reading from pipe
memset(buffer, '\0', 100);
// memset(buffer, 0, 10);
int readByte = read(readPipeDescriptor,&buffer,10);
if ( readByte== -1)
std::cout<<"Error reading from read end of read pipe"<<errno<<std::endl;
else std::cout<<"Read "<<readByte<<" bytes from pipe :"<<buffer<<std::endl;
sleep(1);
int main(int argc, char *argv[])
int chatter = *(argv[1]) - '0';
int writePipeDescriptor, readFileDescriptor;
switch (chatter)
case 0:
printf("PID %d, chatter 1: \n", getpid());
//create pipe is done by chatter 0 only (this is just a hot fix to prevent error 17 EEXIST)
if ( mkfifo(PIPE1_PATH, S_IRUSR | S_IWUSR | S_IWGRP ) ==-1 ) //create pipe for read/write/execute by owner, and others
std::cout<<("cannot create pipe 1 \n")<<errno<<std::endl;
;
writePipeDescriptor = openPipe(PIPE1_PATH, O_WRONLY);
readFileDescriptor = openPipe(PIPE2_PATH, O_RDONLY);
std::thread readThread(readPipe,readFileDescriptor); //has to create thread and execute thread first.
writePipe(writePipeDescriptor);
readThread.join();
break;
case 1:
printf("PID %d, chatter 2: \n", getpid());
if ( mkfifo(PIPE2_PATH, S_IRUSR | S_IWUSR | S_IWGRP ) ==-1 ) //create pipe for read/write/execute by owner, and others
std::cout<<("cannot create pipe 2 \n")<<errno<<std::endl;
;
readFileDescriptor = openPipe(PIPE1_PATH, O_RDONLY);
writePipeDescriptor = openPipe(PIPE2_PATH, O_WRONLY);
std::thread writeThread(writePipe,writePipeDescriptor);
readPipe(readFileDescriptor);
writeThread.join();
break;
return 0;
【讨论】:
以上是关于如何在 C++ 中使用管道构建聊天程序的主要内容,如果未能解决你的问题,请参考以下文章
在IOS中构建一个使用node.js服务器的聊天应用程序[关闭]
使用 Angularjs 和 Firebase 构建聊天应用程序
使用 androidNodeJs 和 Socket.io 创建一个实时聊天应用程序