/** * Test for QUdpSocket::readyRead() signal. * * - Works with Qt5.4.1 (msvc2013_64_opengl). * * - Failes with Qt5.6.2 (msvc2015_64): * * ERROR: receiveTimeout > 1s detected * ERROR: m_udpSocket->hasPendingDatagrams() returned true (lost readyRead signal detected) * or * ERROR: clientDatagramReceiver1.m_receivedFromSlotImplCalled = false * * - Failes with Qt5.7.1(msvc2015_64): * * ERROR: Client::readyReadClient() - spurious readyReadClient (datagramSize == -1) * ERROR: receiveTimeout > 1s detected * ERROR: m_udpSocket->hasPendingDatagrams() returned true (lost readyRead signal detected) * or * ERROR: Client::readyReadClient() - spurious readyReadClient (datagramSize == -1) * ERROR: clientDatagramReceiver1.m_receivedFromSlotImplCalled = false * * Related Bugs: * * - QTBUG-46552: QUdpSocket: readyRead() stops firing * https://bugreports.qt.io/browse/QTBUG-46552 * * - QTBUG-58214: QUdpSocket still broken - readyRead() stops firing * https://bugreports.qt.io/browse/QTBUG-58214 */ #include #include #include #include class Server : public QObject { Q_OBJECT public: Server() : m_udpSocket(nullptr) { } ~Server() { } void startServer() { qDebug() << "Server::startServer()"; m_udpSocket = new QUdpSocket(); connect(m_udpSocket, &QUdpSocket::readyRead, this, &Server::readyReadServer); connect(m_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(errorUdpSocket(const QAbstractSocket::SocketError&))); if (!m_udpSocket->bind(QHostAddress("127.0.0.1"), 8123, QAbstractSocket::DontShareAddress)) { qDebug() << "Server::startServer() - bind failed"; exit(-1); } } void sendResponse(const QByteArray& datagram, const QHostAddress& host, const quint16& port) { qDebug() << "Server::sendResponse()"; m_udpSocket->writeDatagram(datagram, host, port); } void stopServer() { delete m_udpSocket; } signals: void datagramReceived(const QHostAddress& host, const quint16& port, const QByteArray& datagram); private slots: void readyReadServer() { qDebug() << "Server::readyReadServer()"; qint64 datagramSize = m_udpSocket->pendingDatagramSize(); if (datagramSize == -1) { qDebug() << "ERROR: Server::readyReadServer() - spurious readyReadServer (datagramSize == -1)"; return; } QByteArray datagram(datagramSize, Qt::Uninitialized); QHostAddress host; quint16 port; qint64 readSize = m_udpSocket->readDatagram(datagram.data(), datagramSize, &host, &port); if (readSize != datagramSize) { qDebug() << "ERROR: Server::readyReadServer() - readDatagram() failed (readSize != datagramSize)"; return; } qDebug() << "Server::readyReadServer() - received: " << datagram.toStdString().c_str(); sendResponse(datagram, host, port); emit datagramReceived(host, port, datagram); } void errorUdpSocket(const QAbstractSocket::SocketError& socketError) { qDebug() << "ERROR: Server::errorUdpSocket() - socketError: " << socketError; } private: QUdpSocket* m_udpSocket; }; class Client : public QObject { Q_OBJECT public: Client() : m_requestCount(0), m_udpSocket(nullptr), m_resendTimer(nullptr) { } ~Client() { } void startClient() { qDebug() << "Client::startClient()"; m_udpSocket = new QUdpSocket(); connect(m_udpSocket, &QUdpSocket::readyRead, this, &Client::readyReadClient); connect(m_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(errorUdpSocket(const QAbstractSocket::SocketError&))); if (!m_udpSocket->bind(QHostAddress(QHostAddress::Any), 0, QAbstractSocket::DontShareAddress)) { qDebug() << "Client::startClient() - bind failed"; exit(-1); } quint16 localPort = m_udpSocket->localPort(); qDebug() << "Client::startClient() - localPort: " << localPort; // the offending call triggering the bug m_udpSocket->connectToHost(QHostAddress("127.0.0.1"), 8123); m_resendTimer = new QTimer(); m_resendTimer->setInterval(100); connect(m_resendTimer, &QTimer::timeout, this, &Client::timerTimeout); m_resendTimer->start(); m_receiveTimeout.start(); sendRequest(); } void stopClient() { delete m_resendTimer; delete m_udpSocket; } void sendRequest() { #if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) QString request = QString().sprintf("Request - %llu", m_requestCount); #else QString request = QString::asprintf("Request - %llu", m_requestCount); #endif m_udpSocket->writeDatagram(request.toStdString().data(), request.toStdString().size(), QHostAddress("127.0.0.1"), 8123); } signals: void datagramReceived(const QHostAddress& host, const quint16& port, const QByteArray& datagram); private slots: void readyReadClient() { qDebug() << "Client::readyReadClient()"; qint64 datagramSize = m_udpSocket->pendingDatagramSize(); if (datagramSize == -1) { qDebug() << "ERROR: Client::readyReadClient() - spurious readyReadClient (datagramSize == -1)"; return; } QByteArray byteArray(datagramSize, Qt::Uninitialized); QHostAddress host; quint16 port; qint64 readSize = m_udpSocket->readDatagram(byteArray.data(), datagramSize, &host, &port); if (readSize != datagramSize) { qDebug() << "Client::readyReadClient() - readDatagram() failed (readSize != datagramSize)"; return; } qDebug() << "Client::readyReadClient() - received: " << byteArray.toStdString().c_str(); m_requestCount++; m_receiveTimeout.start(); if (m_requestCount < 5) { sendRequest(); } else if (m_requestCount >= 6) { emit datagramReceived(host, port, byteArray); } } void errorUdpSocket(const QAbstractSocket::SocketError& socketError) { qDebug() << "Client::errorUdpSocket() - socketError: " << socketError; } void timerTimeout() { qDebug() << "Client::timerTimeout()"; if (m_receiveTimeout.elapsed() > 1000) { qDebug() << "ERROR: receiveTimeout > 1s detected"; if (m_udpSocket->hasPendingDatagrams()) { qDebug() << "ERROR: m_udpSocket->hasPendingDatagrams() returned true (lost readyRead signal detected)"; } exit(-1); } sendRequest(); } private: quint64 m_requestCount; QUdpSocket* m_udpSocket; QTime m_receiveTimeout; QTimer* m_resendTimer; }; class DatagramReceiver : public QObject { Q_OBJECT public: DatagramReceiver(const QString& receiverName) : QObject(), m_receiverName(receiverName), m_receivedFromSlotImplCalled(false) { } virtual ~DatagramReceiver() { } void receivedFromSlotImpl(const QHostAddress& host, const quint16& port, const QByteArray& byteArray) { Q_UNUSED(host); Q_UNUSED(port); Q_UNUSED(byteArray); qDebug() << "DatagramReceiver::receivedFromSlotImpl() - " << m_receiverName; m_receivedFromSlotImplCalled = true; } const QString m_receiverName; bool m_receivedFromSlotImplCalled; }; int main(int argc, char* argv[]) { QCoreApplication a(argc, argv); Server* server = new Server(); DatagramReceiver serverDatagramReceiver1("serverDatagramReceiver1"); QObject::connect(server, &Server::datagramReceived, &serverDatagramReceiver1, &DatagramReceiver::receivedFromSlotImpl); server->startServer(); Client* client = new Client(); DatagramReceiver clientDatagramReceiver1("clientDatagramReceiver1"); QObject::connect(client, &Client::datagramReceived, &clientDatagramReceiver1, &DatagramReceiver::receivedFromSlotImpl); client->startClient(); for(int i = 0; i < 1000000; i++) { QCoreApplication::processEvents(); if (serverDatagramReceiver1.m_receivedFromSlotImplCalled) { break; } } if (!serverDatagramReceiver1.m_receivedFromSlotImplCalled) { qDebug() << "ERROR: serverDatagramReceiver1.m_receivedFromSlotImplCalled = false"; exit(-1); } for(int i = 0; i < 10000000; i++) { QCoreApplication::processEvents(); if (clientDatagramReceiver1.m_receivedFromSlotImplCalled) { break; } } if (!clientDatagramReceiver1.m_receivedFromSlotImplCalled) { qDebug() << "ERROR: clientDatagramReceiver1.m_receivedFromSlotImplCalled = false"; exit(-1); } client->stopClient(); delete client; server->stopServer(); delete server; } #include "test_qudpsocket_003.moc"