c++ - 通过 stdin/stdout 和 QDataStream 与 QProcess 通信

标签 c++ qt

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

这是我的尝试:

#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不实现异步接口(interface)。读取和写入是阻塞的,waitForXxx方法是无操作的。

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

QFile正在阻塞,slave()不需要检查状态的循环。

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

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

请注意,使用 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 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";
}

关于c++ - 通过 stdin/stdout 和 QDataStream 与 QProcess 通信,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43523282/

相关文章:

c++ - 确定在 Qt 中按下了哪个快捷方式

c++ - 长12位数字?

java - 来自 Java 的 Qt Android : How to call Toast. makeText?

c++ - 在 Qt 应用程序中保护私钥

c++ - 是否可以根据其位置更改 QSlider handle 的颜色?

qt - 如何省略 "Binding loop detected for property"警告?

c++ - 套接字编程中非法查找的可能原因

c++ - 自定义内存管理器

c++ - 自动定义来自 CRTP 的访客(CRTP 使用 boost foreach with boost variant)

c++ - Boost.Asio TCP move 到套接字析构函数不足以干净地关闭?