用QProcess实现父子进程的双向交互

Qt环境:4.8

原代码:

转载自:http://blog.chinaunix.net/uid-13830775-id-97752.html

在Unix环境下实现了,与大家共同学习。

记得以前写过Linux的C程序, 里面用popen打开一个子进程, 这样可以用read/write和子进程通讯, 而在子进程里则是通过从stdin读和向stdout写实现对父进程的通讯。 QProcess的底层实现用的是类似的理念。 QProcess类提供的API让父进程可以轻松地读取子进程stdout的数据, 也可以轻松地向子进程的stdin写数据。 不过这其中还是会有各种各样颇让人费解的谜团, 需要memo一下。

Test Case

两个小程序, 父进程程序名为server。 其中定义了一个QProcess, 将QProcess的readyRead信号连接到一个槽函数上, 用来接收client端stdout上的信息。 在按下“Send to Client”按钮时调用QProcess::write发送编辑框里的文字。


子进程名为client, 定义一个QFile打开stdin, 连接readyRead信号等待server端写入的数据。 按下“Send message”时向stdout写入数据。


大家觉得这个test case能如我们预料那样正常运行吗?

问题分析

这个程序从结构上来看非常简单, 如果代码不乱写不像是会出问题的样子。 实际运行的结果还是颇让人意外的, 从client端向server端发送数据很正常, 但在client里始终收不到readyRead信号!  双向交流变成了单向, 这可真让人郁闷。 仔细看QFile的文档包括QIODevice有关的描述, 看不出问题所在。 不得已只好求助专家拉, 按照专家的意见, QFile是不支持readyRead信号的。 这一点比较好理解。 但是似乎没有什么好的Qt API可以解决此问题。

Qt的API设计里没有包括最底层设备处理的类, 这个问题经常看到有人埋怨, 比如串口通讯等功能就没有直接的Qt类可用。 不过嘛笔者觉得这个不算什么问题, 像这种特别底层的东西API超简单, open->ioctl->read/write->close就完事,就算拿Qt包一层也不会更简单 , 何必多此一举呢, 如果什么功能都依赖Qt实现, 那要我们这些程序员干啥用?!

这里就需要把原来用QFile来操作stdin的代码修改成直接open/read/write/close的方式。 如果要保持原来的“有事件才动作”也不是难事, 这部分可以用Qt里的QSocketNotifier类监控fd的读写事件, 在收到信号时才去调用read/write。

主要代码

server端开进程
if(! pro )
{
pro = new QProcess(this);
connect(pro, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(processFinished(int, QProcess::ExitStatus)));
connect(pro, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError)));
connect(pro, SIGNAL(readyRead()), this, SLOT(readFromClient()));
pro->start(“./client”);
}

server端接收数据
void MainWin::readFromClient()
{
if( !pro) return;
QByteArray output = pro->readAllStandardOutput();
qWarning() }

server端发送数据
void MainWin::writeToClient()
{
if( !pro) return;
pro->write(le->text().toLatin1().constData(), le->text().length());
qWarning() text();
}

client端监控stdin的读写消息
filein.open(stdin, QIODevice::ReadOnly);
QSocketNotifier* sn = new QSocketNotifier(filein.handle(), QSocketNotifier::Read, this);
connect(sn, SIGNAL(activated(int)), this, SLOT(readFromServer(int)));

client端接收数据
void MainWin::readFromServer(int fd)
{
if(fd != filein.handle() )
return;

char buffer[256];
int count = read(filein.handle(), buffer, 256);
le->setText(“FROM SERVER:” + QString(buffer).left(count));
}

client端发送数据
void MainWin::writeToServer()
{
QFile fileout;
fileout.open(stdout, QIODevice::WriteOnly);
fileout.write(le->text().toLatin1().constData(), le->text().length()); // write to stderr
qWarning() text();
fileout.close();

}


问题:

但是问题是我的server端怎么都接不到client端的消息问题是什么?

答案:其实上面注释已经写出来了,是的,stdout write to stderr  Qt把stdout重定向了?好吧,这是事实。具体原因,有待进一步学习考究。

解决:设置process的读取通道,读取标准错误输出,因为默认情况是读取标准输出。

m_pro->setReadChannel(QProcess::StandardError);

void QProcess::setReadChannel ( ProcessChannel channel )
Sets the current read channel of the QProcess to the given channel. The current input channel is used by the functions read(), readAll(), readLine(), and getChar(). It also determines which channel triggers QProcess to emit readyRead().

Qt手册没有写默认的读取通道,但注释掉setReadChannel()可知默认的读取通道是标准输出通道。

上一个完整的开process的代码:

void MainWindow::on_pushButtonNewPro_clicked()
{
    if(!m_pro)
    {
        m_pro = new QProcess(this);
        m_pro->setReadChannelMode(QProcess::SeparateChannels);
        m_pro->setReadChannel(QProcess::StandardError);
        connect(m_pro,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(OnHandleProcessFinish(int,QProcess::ExitStatus)));
        connect(m_pro,SIGNAL(error(QProcess::ProcessError)),this,SLOT(OnHanleProcessError(QProcess::ProcessError)));
        connect(m_pro,SIGNAL(readyReadStandardOutput()),this,SLOT(OnReadyReadFromClient()));
        connect(m_pro,SIGNAL(readyRead()),this,SLOT(OnReadyReadFromClient()));
        m_pro->start("/opt/qtcreator-2.6.1/ChildOnlyWrite-build-DESKTOP-Debug/ChildOnlyWrite");
    }
}
其他的照着上面的代码是没错的。进行了一点改进,搞了1天....


猜你喜欢

转载自blog.csdn.net/daizhiyan1/article/details/80596583