通过stdin/stdout和qdatastream与Qprocess进行通信

Communicating with QProcess via stdin/stdout and QDataStream

本文关键字:Qprocess 通信 qdatastream stdin stdout 通过      更新时间:2023-10-16

我正在尝试编写一个可以产生子过程并通过标准输出/输入与之通信的应用程序。只是为了掌握它,我尝试编写一个简单的应用程序,该应用程序可以向子过程发送消息,而子过程会接收它并将其发送回。坦率地说,经过荒谬的反复试验,我设法向子过程收到了一条消息,但我不知道如何将其发送回去。

这是我的尝试:

#include <QApplication>
#include <QDataStream>
#include <QFile>
#include <QDebug>
#include <QProcess>
#include <QThread>
#define dumpval(x) qDebug()<<#x<<'='<<x
void slave()
{
    QApplication::setApplicationName("slave");
    qSetMessagePattern("%{appname}: %{message}");
    qDebug()<<"started";
    QFile input;
    QFile output;
    dumpval(input.open(stdin, QFile::ReadOnly));
    dumpval(output.open(stdout, QFile::WriteOnly));
    QObject::connect(&output, &QIODevice::bytesWritten, [](int bytesWritten){dumpval(bytesWritten);});
    QDataStream inputStream(&input);
    QDataStream outputStream(&output);
    QByteArray data;
    while (true){
        inputStream>>data;
        dumpval(data);
        if (!data.isEmpty()) break;
        inputStream.resetStatus();
        QThread::sleep(1);
    }
    dumpval(output.isWritable());
    outputStream<<data;
    dumpval(output.waitForBytesWritten(-1));
    qDebug()<<"data written";
    qDebug()<<"stopped";
}
void master(QString path)
{
    QApplication::setApplicationName("master");
    qSetMessagePattern("%{appname}: %{message}");
    qDebug()<<"started";
    QProcess p;
    QObject::connect(&p, &QIODevice::bytesWritten, [](int bytesWritten){dumpval(bytesWritten);});
    p.setProgram(path);
    p.setArguments({"slave"});
    p.setProcessChannelMode(QProcess::ForwardedErrorChannel);
    p.start();
    p.waitForStarted();
    QDataStream stream(&p);
    QByteArray data = "this is a test";
    stream<<data;
    dumpval(p.waitForBytesWritten(-1));
    data.clear();
    while (true){
        stream>>data;
        dumpval(data);
        if (!data.isEmpty()) break;
        stream.resetStatus();
        QThread::sleep(1);
    }
    qDebug()<<"stopped";
}
int main(int argc, char** argv)
{
    if (argc == 1) master(argv[0]);
    else slave();
}

这是此代码的输出:

master: started
master: bytesWritten = 18
master: p.waitForBytesWritten(-1) = true
master: data = ""
slave: started
slave: input.open(stdin, QFile::ReadOnly) = true
slave: output.open(stdout, QFile::WriteOnly) = true
slave: data = "this is a test"
slave: output.isWritable() = true
slave: output.waitForBytesWritten(-1) = false
slave: data written
slave: stopped
master: data = ""
master: data = ""
master: data = ""
master: data = ""
master: data = ""
master: data = ""
^C

我在做什么错?

QFile不能实现异步接口。读取和写入正在阻止,waitForXxx方法是无op的。

请参阅此问题,以了解如何实现非阻滞控制台I/O,您是否希望这样做。

由于QFile正在阻止,因此slave()不需要检查状态的循环。

您使用QProcess使用其阻止API,因此不需要使用其信号。您还假设读取将返回完整的数据。控制台I/O是面向流的,而不是面向消息的,因此您必须使用QDataStream交易来确保读取在原子上成功。readyRead指示仅表示有些数据可用。它可能只是一个字节。

如果您想使用一种非阻止状态的方法来处理QProcess和类似的通信,请参见此答案。

请注意,使用argc[0]作为从属开始自我启动是不可靠的。改用QCoreApplication::applicationFilePath()

下面的示例有效,并产生以下输出:

master: started
slave: started
slave: input.open(stdin, QFile::ReadOnly) = true
slave: output.open(stdout, QFile::WriteOnly) = true
slave: data = "this is a testx00"
slave: data = ""
slave: inputStream.status() = 0
slave: stopped
master: data = "this is a testx00"
master: data = ""
master: stopped
// https://github.com/KubaO/stackoverflown/tree/master/questions/process-echo-43523282
#include <QtCore>
#define dumpval(x) qDebug()<<#x<<'='<<x
void slave()
{
   QCoreApplication::setApplicationName("slave");
   qDebug()<<"started";
   QFile input, output;
   QDataStream inputStream{&input}, outputStream{&output};
   dumpval(input.open(stdin, QFile::ReadOnly));
   dumpval(output.open(stdout, QFile::WriteOnly));
   QByteArray data;
   do {
      inputStream >> data;
      outputStream << data;
      dumpval(data);
   } while (inputStream.status() == QDataStream::Ok && !data.isEmpty());
   dumpval(inputStream.status());
}
void master()
{
   QCoreApplication::setApplicationName("master");
   qDebug()<<"started";
   QProcess p;
   p.setProgram(QCoreApplication::applicationFilePath());
   p.setArguments({"slave"});
   p.setProcessChannelMode(QProcess::ForwardedErrorChannel);
   p.start();
   p.waitForStarted();
   QDataStream stream(&p);
   QByteArray data;
   stream << "this is a test" << QByteArray{};
   while (true) {
      stream.startTransaction();
      stream >> data;
      if (stream.commitTransaction()) {
         dumpval(data);
         if (data.isEmpty())
            break;
      } else
         p.waitForReadyRead();
   }
   p.waitForFinished();
}
int main(int argc, char** argv)
{
   QCoreApplication app(argc, argv);
   qSetMessagePattern("%{appname}: %{message}");
   if (app.arguments().size() < 2) master(); else slave();
   qDebug() << "stopped";
}