【问题标题】:how does readyRead() work in Qt?readyRead() 在 Qt 中是如何工作的?
【发布时间】:2014-12-24 02:59:13
【问题描述】:

这是我在这个网站上的第一个问题!

我在从一个 COM 端口读取数据时遇到问题,我从另一个 COM 端口发送了一条完整的消息,当我使用 Qt 接收它时,它总是被分成多个子消息。

void SerialPortReader::init()
{
    connect(m_serialPort, SIGNAL(readyRead()), this, SLOT(readData()));
}   

void SerialPortReader::readData()
{
//    m_serialPort->waitForReadyRead(200);
    QByteArray byteArray = m_serialPort->readAll();
    qDebug() << byteArray;

    if(byteArray.startsWith(SOF) && byteArray.endsWith(EOF_LS)
        && byteArray.size() >= MIN_SIZE_DATA) {
    decodeData(byteArray.constData());
    } else {
        qDebug() << "LIB SWCom : Unvalid trame !";
    }
}

发送的消息是 25 或 27 字节长,如果我使用 Putty 或超级终端来阅读它们,我没有问题。 另外,如果我使用 2 个仿真串口 COM 进行通信,我没有这个问题... 它只发生在 Qt 读取系统和 2 个物理 COM 端口...

我认为当 readyRead 信号准确发出时我没有得到...

我很困惑,提前感谢您的帮助!

【问题讨论】:

  • 您是在问我们如何在 QtSerialPort 中具体实现这一点和/或只是一般的 API 概念?
  • 是的,帮了大忙,谢谢

标签: c++ qt qtcore qtserialport qiodevice


【解决方案1】:

documentation其实已经很清楚了:

void QIODevice::readyRead() [信号]

每次有新数据可用于从设备读取时,都会发出一次此信号。只有在有新数据可用时才会再次发出它,例如当新的网络数据负载到达您的网络套接字时,或者当新的数据块已附加到您的设备时。

readyRead() 不会递归发出;如果您重新进入事件循环或在连接到 readyRead() 信号的插槽内调用 waitForReadyRead(),则不会重新发送该信号(尽管 waitForReadyRead() 仍可能返回 true)。

实现从 QIODevice 派生的类的开发人员注意:当新数据到达时,您应该始终发出 readyRead()(不要仅仅因为缓冲区中还有数据要读取而发出它)。不要在其他情况下发出 readyRead()。

这意味着并不能真正保证有多少数据可供读取,只是有一些数据可用。

如果您希望读取的数据多于一次读取的数据,您可以选择超时值和/或 readyRead。这取决于您要达到的目标。

请参阅我前段时间为此操作编写的command line async reader example

#include "serialportreader.h"

#include <QCoreApplication>

QT_USE_NAMESPACE

SerialPortReader::SerialPortReader(QSerialPort *serialPort, QObject *parent)
    : QObject(parent)
    , m_serialPort(serialPort)
    , m_standardOutput(stdout)
{
    connect(m_serialPort, SIGNAL(readyRead()), SLOT(handleReadyRead()));
    connect(m_serialPort, SIGNAL(error(QSerialPort::SerialPortError)), SLOT(handleError(QSerialPort::SerialPortError)));
    connect(&m_timer, SIGNAL(timeout()), SLOT(handleTimeout()));

    m_timer.start(5000);
}

SerialPortReader::~SerialPortReader()
{
}

void SerialPortReader::handleReadyRead()
{
    m_readData.append(m_serialPort->readAll());

    if (!m_timer.isActive())
        m_timer.start(5000);
}

void SerialPortReader::handleTimeout()
{
    if (m_readData.isEmpty()) {
        m_standardOutput << QObject::tr("No data was currently available for reading from port %1").arg(m_serialPort->portName()) << endl;
    } else {
        m_standardOutput << QObject::tr("Data successfully received from port %1").arg(m_serialPort->portName()) << endl;
        m_standardOutput << m_readData << endl;
    }

    QCoreApplication::quit();
}

void SerialPortReader::handleError(QSerialPort::SerialPortError serialPortError)
{
    if (serialPortError == QSerialPort::ReadError) {
        m_standardOutput << QObject::tr("An I/O error occurred while reading the data from port %1, error: %2").arg(m_serialPort->portName()).arg(m_serialPort->errorString()) << endl;
        QCoreApplication::exit(1);
    }
}

在这种情况下,命令行阅读器示例将获取一次性传递的任何数据,但它不保证长度或任何东西。

另外,请注意,您的 cmets 背后的同步 API 与您询问的异步 API 没有多大意义。我在这里指的是m_serialPort-&gt;waitForReadyRead(200);

【讨论】:

  • 感谢您的回答,我忘了说数据是连续发出的(一次),在最后的情况下每 20 毫秒。我知道 waitForReadyRead() 会阻止信号 readyRead 被重新发出,所以我在读取数据之前尝试了 waitForReadyRead(20) 并且效果很好。但在这种情况下,我需要保证发件人每 20 毫秒发出一次,而不是更多。
  • @palador:根据我的回答,您可以为此使用计时器。我的示例还展示了计时器的使用。
  • @palador:请在问题解决后提供一些反馈。
  • @palador:请不要提交澄清作为答案。只需更新问题。尽管您应该事先清楚。
  • 哦,好吧,对不起,我不会忘记在我的新帖子中更清楚。关于您的技术,我不知道如何将计时器时间设置得更准确以避免帧丢失或避免连接帧......
【解决方案2】:

只要有待处理的数据并且之前的 readyRead 执行完成,就会发出 readyRead 信号。

文档说:

每次有新数据可用时,都会发出一次此信号 从设备读取。它只会在有新数据时再次发出 可用,例如当网络数据的新负载到达时 在你的网络套接字上,或者当一个新的数据块被附加时 到您的设备。

readyRead() 不会递归发出;如果你重新进入事件循环 或在连接到 readyRead() 的插槽内调用 waitForReadyRead() 信号,该信号将不会被重新发送(虽然 waitForReadyRead() 可能仍然返回 true)。

如果这会导致您遇到任何问题,您可以使用 while 循环来检查 bytesAvailable()。

【讨论】:

    【解决方案3】:

    在这里使用线程概念,使用线程您可以实现这一点...在单独的线程中运行您的端口读取,然后它会正常工作,否则消息将被剪切。 其他方法您可以使用 Qpr​​ocess 并使用 QProcess 打开一个新终端,并将端口号和连接详细信息发送到该终端并从 Qt 运行它,然后您也可以将其存档

    我的意思是我们可以在这里使用therad和socket通信看看这段代码

        QTcpSocket *SocketTest::getSocket() {
            return socket;
        }
    
        void SocketTest::Connect()
    
        {
    
            socket = new QTcpSocket(this);
            socket->connectToHost("127.0.0.1",22);
            if(socket->waitForConnected(3000))
            {
    
                qDebug()<<"connect";
                socket->waitForBytesWritten(3000);
                socket->waitForReadyRead(1000);
             Settings::sockdata=socket->readAll();
               qDebug()<<Settingssock::sockets;
               socket->close();
    
            }
            else
            {
                 qDebug()<<" not connect";
    
            }
        }
    
    
    ............ call socket 
    
     SocketTest sock;
        sock.Connect();
        QTcpSocket * socket = sock.getSocket();
           QObject *ob= new QThread;
           mythread thread1(socket);
        thread = new mythread(socket);
        thread.run();
        thread->start();
       thread->wait();
    

    这是一个简单的telnet连接示例,同样你可以指定你想连接哪个端口和连接到哪个ip

    【讨论】:

    • 我在解析这个答案时遇到问题,恐怕。 OP 没有提到协议,所以我不确定你在写什么端口。另外,我不明白 QProcess/QThread 在这种情况下如何相关?另外,“从Qt运行它”是什么意思? “你能做到这一点” -> 实现什么?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-28
    相关资源
    最近更新 更多