QTcpSocket readyRead信号在传输数据时停止发射

问题描述 投票:0回答:2

我在使用QT-Framework为自己编写一些网络应用程序时偶然发现了一个问题。我非常喜欢信号/插槽系统,但我感觉我在这里遇到了竞争状态。

我的服务器正在快速发送小的“电报”,因为它们不值得自己的TCP数据包(很好)。

在客户端,我使用套接字readyRead-slot并处理我的处理程序槽“socket Has Data”中的传入数据。

问题是,当执行插槽内的代码时,似乎不会重新发出信号。

我绕了它一个循环,这有点帮助(我收到更多的电报)。看起来如果退出循环但不退出插槽然后接收数据,则跳过信号。执行从插槽返回后,我的客户端卡住并等待数据堆积在套接字缓冲区中。

我能做什么?是否可以在QT中完成此活动?我明白了什么问题吗?如何以这种方式实现基于电报的数据传输?

这里的问题似乎已经解决了:How to make sure that readyRead() signals from QTcpSocket can't be missed?,但我没有提到上述错误:

  • 我不是在调用waitForReadyRead
  • 我没有进入任何事件循环
  • 除了主线程之外我不使用任何线程

并且仍然遇到问题。

以下是一些具有相关行为的示例代码:

服务器

#include <QCoreApplication>
#include <QTcpServer>
#include <QTcpSocket>

#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QTcpServer *tcpServer = new QTcpServer(NULL);

    tcpServer->listen(QHostAddress("127.0.0.1"), 5573);
    tcpServer->waitForNewConnection(-1);

    QTcpSocket *socket = tcpServer->nextPendingConnection();

    QByteArray telegram;

    for (int i = 0; i < 10000; ++i) {

        telegram.clear();

        QString message = "Test number " + QString::number(i);

        // Each "telegram" is just that string and an incrementing number.

        telegram.append(static_cast<char>(message.length()));
        telegram.append(message.toLocal8Bit());

        socket->write(telegram);
    }

    socket->flush();
    socket->close();

    tcpServer->close();

    return a.exec();
}

客户

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
}

MainWindow::~MainWindow() {
    delete ui;
}

void MainWindow::on_pushButton_clicked() {

    this->socket = new QTcpSocket(this);
    this->socket->connectToHost("127.0.0.1", 5573);

    connect(this->socket, SIGNAL(readyRead()), this, SLOT(socketHasData()));
}

void MainWindow::socketHasData() {

    do {

        QByteArray header = this->socket->read(1);

        int length = header.at(0);

        while (this->socket->bytesAvailable() < length);

        QByteArray payload = this->socket->read(length);

        qDebug() << "[RX]" << QString::fromLocal8Bit(payload);

    } while (this->socket->atEnd() == false); // I added the loop, which helps somewhat
}

客户端头

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTcpSocket>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();

    void socketHasData();
private:
    Ui::MainWindow *ui;

    QTcpSocket *socket;
};

#endif // MAINWINDOW_H
c++ qt sockets networking race-condition
2个回答
0
投票

如果您的数据被“预打包”成块,您可以轻松识别,为什么不简单地使用QTimer并将QTimer::timeout()连接到某些slot_processData(QByteArray incoming)插槽?可以找到合理的超时(使用套接字通常是10-50毫秒安全)。

分析slot_processData插槽中的可用数据,将部分数据块保留在输入数据的“全局”字节数组中。

这对于“已知”数据实际上非常有效。我将它用于QSerialPortQTcpSocketQTimerQSerialPort超时值可能更难以确定。我发现通常需要对该值进行用户配置控制以考虑旧计算机或超载计算机等。

ALTERNATELY如果数据在“块”大小相对较小,请使用readyRead槽每次评估所有数据,并将完整的块发送到处理槽/处理程序。我在串口实现中使用它非常多,因为硬件设备通常具有短指示或回复值。它消除了QTimer问题。


0
投票

以下是如何使用Qt实现基于电报的TCP数据传输。

Sender

使用指定邮件大小的标头将数据作为邮件发送。

QByteArray data;
QDataStream stream(&data, QIODevice::WriteOnly);

stream << (quint32)0;
stream << theData;
stream.device()->seek(0);
stream << (quint32)data.size();

m_tcpSocket->write(data);

Receiver

在接收器侧,将readyRead信号连接到读取消息的方法。在这种阅读消息的方法中,请确保在实际阅读之前有完整的消息。如果您还没有完整的消息,请等待下一个TCP有效负载到达,并再次检查是否有完整的消息,直到您拥有它。然后阅读。

...
connect(m_tcpSocket, SIGNAL(readyRead()), this, SLOT(readMessage()));
...

void readMessage() {

    // wait for the msgSize
    while (m_tcpSocket->bytesAvailable() < qint64(sizeof(quint32))) {
        if (!m_tcpSocket->waitForReadyRead()) {
            return;
        }
    }

    // get the message size
    QDataStream stream(m_tcpSocket);
    quint32 msgSize = 0;
    stream >> msgSize;

    //wait for the whole message
    int length = qint64(msgSize - sizeof(quint32));
    while (m_tcpSocket->bytesAvailable() < length) {
        if (!m_tcpSocket->waitForReadyRead()) {
            return;
        }
    }

    // get the message
    QByteArray buffer;
    buffer.resize(length);
    stream.readRawData(buffer.data(), length);

    // do something with the message
    emit messageRead(buffer);

}
© www.soinside.com 2019 - 2024. All rights reserved.