如何解决如何在C ++中使用管道构建聊天程序
我想实现一个运行小程序1或小程序2的小程序,具体取决于调用main时输入的参数。打开2个终端和第1个聊天室后,我希望在2个终端之间发送小的字符串消息。
我尝试实现一个简单的管道程序,在该程序中,进程将以读取或写入的方式打开管道。然后,如果进程不是您用于其他目的的管道,则该进程将关闭相应的一端,读取端或写入端。打开2个终端,并使用参数0或1调用main时,我调用2个进程打开管道。
但是,我在运行程序以在2个进程之间连续发送消息时遇到麻烦。每当检测到键盘输入时,该过程就会退出。
我怀疑我没有正确打开管道。 (我的代码中没有打开函数)。打开函数还要求我输入管道的路径名,该路径名在/ proc / [PID] / fd中。我看到每次运行程序时PID都会不同。因此,如何将其放入开放函数的参数中。
请帮助我修改代码。
#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",fileDescription2,//chatter2 will use fileDescription2 to read,fileDescription1); //chatter2 will use fileDescription1 to write,}
}
解决方法
有几个问题。进程立即退出的原因是,您的程序仅在管道的一侧运行,而不在另一侧运行。例如,如果您使用chatter = 0
运行它,则将fileDescription2
用作writePipe
,但是在onServiceStart()
中要做的第一件事是close()
的阅读面writePipe
中的。这意味着每当您写入writePipe[1]
时,由于writePipe[0]
已关闭,您将收到EPIPE错误。
如果要启动程序的两个实例,一个以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个管道。对于每个进程,管道将用作只读或仅写。例如,进程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,10);
int readByte = read(readPipeDescriptor,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;
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。