【问题标题】:Communicating with QProcess via stdin/stdout and QDataStream通过 stdin/stdout 和 QDataStream 与 QProcess 通信
【发布时间】:2017-04-20 15:05:08
【问题描述】:

我正在尝试编写一个应用程序,它会产生一个子进程并通过标准输出/输入与它进行通信。为了掌握它的窍门,我尝试编写一个简单的应用程序,该应用程序将向子进程发送消息,子进程将接收它并将其发回。在经历了荒谬的反复试验后,我设法向子流程发送了一条消息,但我不知道如何将其发回。

这是我的尝试:

#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

我做错了什么?

【问题讨论】:

  • 转储 while 循环以供读取并将其替换为 p.waitForReadyRead()

标签: c++ qt


【解决方案1】:

QFile 没有实现异步接口。读写是阻塞的,waitForXxx 方法是无操作的。

如果您愿意,请参阅this question 了解如何实现非阻塞控制台 I/O。

由于QFile 处于阻塞状态,slave() 不需要检查状态的循环。

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

如果您希望使用非阻塞的有状态方法来处理QProcess 和类似的通信,请参阅this answer for one approach

请注意,使用argc[0] 启动 self 作为从站是不可靠的。请改用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 test\x00"
slave: data = ""
slave: inputStream.status() = 0
slave: stopped
master: data = "this is a test\x00"
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";
}

【讨论】:

  • 如何判断给定的 QOIDevice 是否实现了异步接口?
  • 你一般不能。在 Qt 中,QFileQBuffer 是同步的,其他一切都是异步的(几乎是套接字和端口)。对于您自己编写或从其他库中使用的类,您必须检查代码才能弄清楚。
猜你喜欢
  • 2014-02-14
  • 1970-01-01
  • 2017-02-01
  • 2011-06-22
  • 1970-01-01
  • 2018-09-24
  • 2011-08-01
  • 2012-05-20
  • 1970-01-01
相关资源
最近更新 更多