通过stdin / stdout和QDataStream

时间:2017-04-20 15:05:09

标签: 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

我做错了什么?

1 个答案:

答案 0 :(得分:3)

waitForXxx未实现异步接口。读取和写入都是阻塞的,QFile方法是无操作。

如果您希望这样做,请参阅this question了解如何实现非阻塞控制台I / O.

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

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

如果您希望使用非阻止状态方法来处理argc[0]和类似通信,请参阅this answer for one approach

请注意,使用QCoreApplication::applicationFilePath()启动self作为slave是不可靠的。请改用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";
}
boolean goLeft = false;
while (!s.isEmpty()) {
    ...
    if (order >= 0) {
        if(goLeft) {
        s.push(new brach(endPoints.x, endPoints.y, b.length / 2, b.angle + 35)); // to draw left branch    
        s.push(new brach(endPoints.x, endPoints.y, b.length / 2, b.angle - 35)); // to draw right branch
         goLeft = false;
       } else {
        s.push(new brach(endPoints.x, endPoints.y, b.length / 2, b.angle - 35)); // to draw right branch
        s.push(new brach(endPoints.x, endPoints.y, b.length / 2, b.angle + 35)); // to draw left branch   
         goLeft = true;
       }
       order--; 
    }
}