qt5base-lts/tests/auto/qudpsocket/tst_qudpsocket.cpp
Shane Kearns 85136496bc IPv4 + IPv6 dual stack sockets
Adds support for binding "dual stack" sockets (via QUdpSocket or
QTcpServer). A dual stack socket will accept incoming connections on
either IPv4 or IPv6 interfaces.

QHostAddress::Any     - use this to bind a dual stack socket
QHostAddress::AnyIPv6 - use this to bind a socket for IPv6 only
QHostAddress::AnyIPv4 - use this to bind a socket for IPv4 only

Binding to a specific address rather than one of the "any" addresses
is restricting you to a protocol anyway so no behaviour change there.
IPv6 sockets were previously dual stack on some OS and v6 only on others
Any previously meant IPv4 only

This commit implemented & tested on Windows 7, Linux (Ubuntu 10.04)
and Mac OS 10.6.7.

Windows XP and server 2003 do not support dual stack sockets, even though
they can support IPv6. On those versions, QHostAddress::Any will still
bind to IPv4 0.0.0.0 (which is also the behaviour anywhere QT_NO_IPV6 is
defined)

Autotests run:
qudpsocket (includes a new test case)
qtcpserver (includes a new test case)
qtcpsocket
qnetworkreply
qhostaddress

Task-number: QTBUG-17080
Change-Id: Id486677c4f832e18dc0ff1a86c5f5fc422c9eb4f
Reviewed-on: http://codereview.qt.nokia.com/421
Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com>
Reviewed-by: Thiago Macieira <thiago.macieira@nokia.com>
Reviewed-by: Markus Goetz
2011-06-22 11:01:00 +02:00

1360 lines
51 KiB
C++

/****************************************************************************
**
** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the test suite of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtTest/QtTest>
#include <qcoreapplication.h>
#include <qfileinfo.h>
#include <qdatastream.h>
#include <qudpsocket.h>
#include <qhostaddress.h>
#include <qhostinfo.h>
#include <qmap.h>
#include <QNetworkProxy>
#include <QNetworkInterface>
#include <qstringlist.h>
#include "../network-settings.h"
#ifndef QT_NO_BEARERMANAGEMENT
#include <QtNetwork/qnetworkconfigmanager.h>
#include <QtNetwork/qnetworkconfiguration.h>
#include <QtNetwork/qnetworksession.h>
#endif
Q_DECLARE_METATYPE(QHostAddress)
Q_DECLARE_METATYPE(QNetworkInterface)
Q_DECLARE_METATYPE(QSharedPointer<QNetworkSession>)
//TESTED_CLASS=
//TESTED_FILES=
QT_FORWARD_DECLARE_CLASS(QUdpSocket)
class tst_QUdpSocket : public QObject
{
Q_OBJECT
public:
tst_QUdpSocket();
virtual ~tst_QUdpSocket();
public slots:
void initTestCase_data();
void init();
void cleanup();
private slots:
void constructing();
void unconnectedServerAndClientTest();
void broadcasting();
void loop_data();
void loop();
void ipv6Loop_data();
void ipv6Loop();
void dualStack();
void readLine();
void pendingDatagramSize();
void writeDatagram();
void performance();
void bindMode();
void writeDatagramToNonExistingPeer_data();
void writeDatagramToNonExistingPeer();
void writeToNonExistingPeer_data();
void writeToNonExistingPeer();
void outOfProcessConnectedClientServerTest();
void outOfProcessUnconnectedClientServerTest();
void zeroLengthDatagram();
void multicastTtlOption_data();
void multicastTtlOption();
void multicastLoopbackOption_data();
void multicastLoopbackOption();
void multicastJoinBeforeBind_data();
void multicastJoinBeforeBind();
void multicastLeaveAfterClose_data();
void multicastLeaveAfterClose();
void setMulticastInterface_data();
void setMulticastInterface();
void multicast_data();
void multicast();
void echo_data();
void echo();
protected slots:
void empty_readyReadSlot();
void empty_connectedSlot();
private:
#ifndef QT_NO_BEARERMANAGEMENT
QNetworkConfigurationManager *netConfMan;
QNetworkConfiguration networkConfiguration;
QSharedPointer<QNetworkSession> networkSession;
#endif
};
tst_QUdpSocket::tst_QUdpSocket()
{
Q_SET_DEFAULT_IAP
}
tst_QUdpSocket::~tst_QUdpSocket()
{
}
void tst_QUdpSocket::initTestCase_data()
{
QTest::addColumn<bool>("setProxy");
QTest::addColumn<int>("proxyType");
QTest::newRow("WithoutProxy") << false << 0;
QTest::newRow("WithSocks5Proxy") << true << int(QNetworkProxy::Socks5Proxy);
#ifndef QT_NO_BEARERMANAGEMENT
netConfMan = new QNetworkConfigurationManager(this);
networkConfiguration = netConfMan->defaultConfiguration();
networkSession = QSharedPointer<QNetworkSession>(new QNetworkSession(networkConfiguration));
if (!networkSession->isOpen()) {
networkSession->open();
QVERIFY(networkSession->waitForOpened(30000));
}
#endif
}
void tst_QUdpSocket::init()
{
QFETCH_GLOBAL(bool, setProxy);
if (setProxy) {
QFETCH_GLOBAL(int, proxyType);
if (proxyType == QNetworkProxy::Socks5Proxy) {
QNetworkProxy::setApplicationProxy(QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::serverName(), 1080));
}
}
}
void tst_QUdpSocket::cleanup()
{
QNetworkProxy::setApplicationProxy(QNetworkProxy::DefaultProxy);
}
//----------------------------------------------------------------------------------
void tst_QUdpSocket::constructing()
{
QUdpSocket socket;
#ifdef FORCE_SESSION
socket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
QVERIFY(socket.isSequential());
QVERIFY(!socket.isOpen());
QVERIFY(socket.socketType() == QUdpSocket::UdpSocket);
QCOMPARE((int) socket.bytesAvailable(), 0);
QCOMPARE(socket.canReadLine(), false);
QCOMPARE(socket.readLine(), QByteArray());
QCOMPARE(socket.socketDescriptor(), -1);
QCOMPARE(socket.error(), QUdpSocket::UnknownSocketError);
QCOMPARE(socket.errorString(), QString("Unknown error"));
// Check the state of the socket api
}
void tst_QUdpSocket::unconnectedServerAndClientTest()
{
QUdpSocket serverSocket;
#ifdef FORCE_SESSION
serverSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
qRegisterMetaType<QAbstractSocket::SocketState>("QAbstractSocket::SocketState");
QSignalSpy stateChangedSpy(&serverSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)));
QVERIFY2(serverSocket.bind(), serverSocket.errorString().toLatin1().constData());
QCOMPARE(stateChangedSpy.count(), 1);
const char *message[] = {"Yo mista", "Yo", "Wassap"};
QHostAddress serverAddress = QHostAddress::LocalHost;
if (!(serverSocket.localAddress() == QHostAddress::AnyIPv4 || serverSocket.localAddress() == QHostAddress::AnyIPv6))
serverAddress = serverSocket.localAddress();
for (int i = 0; i < 3; ++i) {
QUdpSocket clientSocket;
#ifdef FORCE_SESSION
clientSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
QCOMPARE(int(clientSocket.writeDatagram(message[i], strlen(message[i]),
serverAddress, serverSocket.localPort())),
int(strlen(message[i])));
char buf[1024];
QHostAddress host;
quint16 port;
QVERIFY(serverSocket.waitForReadyRead(5000));
QCOMPARE(int(serverSocket.readDatagram(buf, sizeof(buf), &host, &port)),
int(strlen(message[i])));
buf[strlen(message[i])] = '\0';
QCOMPARE(QByteArray(buf), QByteArray(message[i]));
}
}
//----------------------------------------------------------------------------------
void tst_QUdpSocket::broadcasting()
{
QFETCH_GLOBAL(bool, setProxy);
if (setProxy) {
QFETCH_GLOBAL(int, proxyType);
if (proxyType == QNetworkProxy::Socks5Proxy) {
QSKIP("With socks5 Broadcast is not supported.", SkipSingle);
}
}
#ifdef Q_OS_AIX
QSKIP("Broadcast does not work on darko", SkipAll);
#endif
const char *message[] = {"Yo mista", "", "Yo", "Wassap"};
QList<QHostAddress> broadcastAddresses;
foreach (QNetworkInterface iface, QNetworkInterface::allInterfaces()) {
if ((iface.flags() & QNetworkInterface::CanBroadcast)
&& iface.flags() & QNetworkInterface::IsUp) {
for (int i=0;i<iface.addressEntries().count();i++)
broadcastAddresses.append(iface.addressEntries().at(i).broadcast());
}
}
if (broadcastAddresses.isEmpty())
QSKIP("No interface can broadcast", SkipAll);
for (int i = 0; i < 4; ++i) {
QUdpSocket serverSocket;
#ifdef FORCE_SESSION
serverSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
QVERIFY2(serverSocket.bind(QHostAddress::Any, 5000), serverSocket.errorString().toLatin1().constData());
QCOMPARE(serverSocket.state(), QUdpSocket::BoundState);
connect(&serverSocket, SIGNAL(readyRead()), SLOT(empty_readyReadSlot()));
QUdpSocket broadcastSocket;
#ifdef FORCE_SESSION
broadcastSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
broadcastSocket.bind();
for (int j = 0; j < 100; ++j) {
for (int k = 0; k < 4; k++) {
broadcastSocket.writeDatagram(message[i], strlen(message[i]),
QHostAddress::Broadcast, 5000);
foreach (QHostAddress addr, broadcastAddresses)
broadcastSocket.writeDatagram(message[i], strlen(message[i]), addr, 5000);
}
QTestEventLoop::instance().enterLoop(15);
if (QTestEventLoop::instance().timeout()) {
#if defined(Q_OS_FREEBSD)
QEXPECT_FAIL("",
"Broadcasting to 255.255.255.255 does not work on FreeBSD",
Abort);
QVERIFY(false); // seems that QFAIL() doesn't respect the QEXPECT_FAIL() :/
#endif
QFAIL("Network operation timed out");
}
QVERIFY(serverSocket.hasPendingDatagrams());
do {
QByteArray arr; arr.resize(serverSocket.pendingDatagramSize() + 1);
QHostAddress host;
quint16 port;
QCOMPARE((int) serverSocket.readDatagram(arr.data(), arr.size() - 1, &host, &port),
(int) strlen(message[i]));
arr.resize(strlen(message[i]));
QCOMPARE(arr, QByteArray(message[i]));
} while (serverSocket.hasPendingDatagrams());
}
}
}
//----------------------------------------------------------------------------------
void tst_QUdpSocket::loop_data()
{
QTest::addColumn<QByteArray>("peterMessage");
QTest::addColumn<QByteArray>("paulMessage");
QTest::addColumn<bool>("success");
QTest::newRow("\"Almond!\" | \"Joy!\"") << QByteArray("Almond!") << QByteArray("Joy!") << true;
QTest::newRow("\"A\" | \"B\"") << QByteArray("A") << QByteArray("B") << true;
QTest::newRow("\"AB\" | \"B\"") << QByteArray("AB") << QByteArray("B") << true;
QTest::newRow("\"AB\" | \"BB\"") << QByteArray("AB") << QByteArray("BB") << true;
QTest::newRow("\"A\\0B\" | \"B\\0B\"") << QByteArray::fromRawData("A\0B", 3) << QByteArray::fromRawData("B\0B", 3) << true;
QTest::newRow("\"(nil)\" | \"(nil)\"") << QByteArray() << QByteArray() << true;
QTest::newRow("Bigmessage") << QByteArray(600, '@') << QByteArray(600, '@') << true;
}
void tst_QUdpSocket::loop()
{
QFETCH(QByteArray, peterMessage);
QFETCH(QByteArray, paulMessage);
QFETCH(bool, success);
QUdpSocket peter;
QUdpSocket paul;
#ifdef FORCE_SESSION
peter.setProperty("_q_networksession", QVariant::fromValue(networkSession));
paul.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
QVERIFY2(peter.bind(), peter.errorString().toLatin1().constData());
QVERIFY2(paul.bind(), paul.errorString().toLatin1().constData());
QHostAddress peterAddress = QHostAddress::LocalHost;
if (!(peter.localAddress() == QHostAddress::AnyIPv4 || peter.localAddress() == QHostAddress::AnyIPv6))
peterAddress = peter.localAddress();
QHostAddress pualAddress = QHostAddress::LocalHost;
if (!(paul.localAddress() == QHostAddress::AnyIPv4 || paul.localAddress() == QHostAddress::AnyIPv6))
pualAddress = paul.localAddress();
QCOMPARE(peter.writeDatagram(peterMessage.data(), peterMessage.length(),
pualAddress, paul.localPort()), qint64(peterMessage.length()));
QCOMPARE(paul.writeDatagram(paulMessage.data(), paulMessage.length(),
peterAddress, peter.localPort()), qint64(paulMessage.length()));
QVERIFY(peter.waitForReadyRead(9000));
QVERIFY(paul.waitForReadyRead(9000));
char peterBuffer[16*1024];
char paulBuffer[16*1024];
if (success) {
QCOMPARE(peter.readDatagram(peterBuffer, sizeof(peterBuffer)), qint64(paulMessage.length()));
QCOMPARE(paul.readDatagram(paulBuffer, sizeof(peterBuffer)), qint64(peterMessage.length()));
} else {
QVERIFY(peter.readDatagram(peterBuffer, sizeof(peterBuffer)) != paulMessage.length());
QVERIFY(paul.readDatagram(paulBuffer, sizeof(peterBuffer)) != peterMessage.length());
}
QCOMPARE(QByteArray(peterBuffer, paulMessage.length()), paulMessage);
QCOMPARE(QByteArray(paulBuffer, peterMessage.length()), peterMessage);
}
//----------------------------------------------------------------------------------
void tst_QUdpSocket::ipv6Loop_data()
{
loop_data();
}
void tst_QUdpSocket::ipv6Loop()
{
#if defined(QT_NO_IPV6)
QSKIP("IPv6 is not yet supported", SkipAll);
#endif
QFETCH(QByteArray, peterMessage);
QFETCH(QByteArray, paulMessage);
QFETCH(bool, success);
QUdpSocket peter;
QUdpSocket paul;
#ifdef FORCE_SESSION
peter.setProperty("_q_networksession", QVariant::fromValue(networkSession));
paul.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
quint16 peterPort = 28124;
quint16 paulPort = 28123;
if (!peter.bind(QHostAddress::LocalHostIPv6, peterPort)) {
QCOMPARE(peter.error(), QUdpSocket::UnsupportedSocketOperationError);
} else {
QVERIFY(paul.bind(QHostAddress::LocalHostIPv6, paulPort));
QCOMPARE(peter.writeDatagram(peterMessage.data(), peterMessage.length(), QHostAddress("::1"),
paulPort), qint64(peterMessage.length()));
QCOMPARE(paul.writeDatagram(paulMessage.data(), paulMessage.length(),
QHostAddress("::1"), peterPort), qint64(paulMessage.length()));
char peterBuffer[16*1024];
char paulBuffer[16*1024];
#if !defined(Q_OS_WINCE)
QVERIFY(peter.waitForReadyRead(5000));
QVERIFY(paul.waitForReadyRead(5000));
#else
QVERIFY(peter.waitForReadyRead(15000));
QVERIFY(paul.waitForReadyRead(15000));
#endif
if (success) {
QCOMPARE(peter.readDatagram(peterBuffer, sizeof(peterBuffer)), qint64(paulMessage.length()));
QCOMPARE(paul.readDatagram(paulBuffer, sizeof(peterBuffer)), qint64(peterMessage.length()));
} else {
QVERIFY(peter.readDatagram(peterBuffer, sizeof(peterBuffer)) != paulMessage.length());
QVERIFY(paul.readDatagram(paulBuffer, sizeof(peterBuffer)) != peterMessage.length());
}
QCOMPARE(QByteArray(peterBuffer, paulMessage.length()), paulMessage);
QCOMPARE(QByteArray(paulBuffer, peterMessage.length()), peterMessage);
}
}
void tst_QUdpSocket::dualStack()
{
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
QSKIP("test server SOCKS proxy doesn't support IPv6", SkipSingle);
QUdpSocket dualSock;
QByteArray dualData("dual");
QVERIFY(dualSock.bind(QHostAddress(QHostAddress::Any), 0));
QUdpSocket v4Sock;
QByteArray v4Data("v4");
QVERIFY(v4Sock.bind(QHostAddress(QHostAddress::AnyIPv4), 0));
QUdpSocket v6Sock;
QByteArray v6Data("v6");
QVERIFY(v6Sock.bind(QHostAddress(QHostAddress::AnyIPv6), 0));
QHostAddress from;
quint16 port;
QByteArray buffer;
//test v4 -> dual
QCOMPARE((int)v4Sock.writeDatagram(v4Data.constData(), v4Data.length(), QHostAddress(QHostAddress::LocalHost), dualSock.localPort()), v4Data.length());
QVERIFY(dualSock.waitForReadyRead(5000));
buffer.reserve(100);
qint64 size = dualSock.readDatagram(buffer.data(), 100, &from, &port);
QCOMPARE((int)size, v4Data.length());
buffer.resize(size);
QCOMPARE(buffer, v4Data);
//test v6 -> dual
QCOMPARE((int)v6Sock.writeDatagram(v6Data.constData(), v6Data.length(), QHostAddress(QHostAddress::LocalHostIPv6), dualSock.localPort()), v6Data.length());
QVERIFY(dualSock.waitForReadyRead(5000));
buffer.reserve(100);
size = dualSock.readDatagram(buffer.data(), 100, &from, &port);
QCOMPARE((int)size, v6Data.length());
buffer.resize(size);
QCOMPARE(buffer, v6Data);
//test dual -> v4
QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.length(), QHostAddress(QHostAddress::LocalHost), v4Sock.localPort()), dualData.length());
QVERIFY(v4Sock.waitForReadyRead(5000));
buffer.reserve(100);
size = v4Sock.readDatagram(buffer.data(), 100, &from, &port);
QCOMPARE((int)size, dualData.length());
buffer.resize(size);
QCOMPARE(buffer, dualData);
//test dual -> v6
QCOMPARE((int)dualSock.writeDatagram(dualData.constData(), dualData.length(), QHostAddress(QHostAddress::LocalHostIPv6), v6Sock.localPort()), dualData.length());
QVERIFY(v6Sock.waitForReadyRead(5000));
buffer.reserve(100);
size = v6Sock.readDatagram(buffer.data(), 100, &from, &port);
QCOMPARE((int)size, dualData.length());
buffer.resize(size);
QCOMPARE(buffer, dualData);
}
void tst_QUdpSocket::empty_readyReadSlot()
{
QTestEventLoop::instance().exitLoop();
}
void tst_QUdpSocket::empty_connectedSlot()
{
QTestEventLoop::instance().exitLoop();
}
//----------------------------------------------------------------------------------
void tst_QUdpSocket::readLine()
{
QUdpSocket socket1;
QUdpSocket socket2;
#ifdef FORCE_SESSION
socket1.setProperty("_q_networksession", QVariant::fromValue(networkSession));
socket2.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
QVERIFY2(socket1.bind(), socket1.errorString().toLatin1().constData());
socket2.connectToHost("127.0.0.1", socket1.localPort());
QVERIFY(socket2.waitForConnected(5000));
}
//----------------------------------------------------------------------------------
void tst_QUdpSocket::pendingDatagramSize()
{
QUdpSocket server;
#ifdef FORCE_SESSION
server.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
QVERIFY2(server.bind(), server.errorString().toLatin1().constData());
QHostAddress serverAddress = QHostAddress::LocalHost;
if (!(server.localAddress() == QHostAddress::AnyIPv4 || server.localAddress() == QHostAddress::AnyIPv6))
serverAddress = server.localAddress();
QUdpSocket client;
#ifdef FORCE_SESSION
client.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
QVERIFY(client.writeDatagram("this is", 7, serverAddress, server.localPort()) == 7);
QVERIFY(client.writeDatagram(0, 0, serverAddress, server.localPort()) == 0);
QVERIFY(client.writeDatagram("3 messages", 10, serverAddress, server.localPort()) == 10);
char c = 0;
QVERIFY(server.waitForReadyRead());
if (server.hasPendingDatagrams()) {
#if defined Q_OS_HPUX && defined __ia64
QEXPECT_FAIL("", "HP-UX 11i v2 can't determine the datagram size correctly.", Abort);
#endif
QCOMPARE(server.pendingDatagramSize(), qint64(7));
c = '\0';
QCOMPARE(server.readDatagram(&c, 1), qint64(1));
QCOMPARE(c, 't');
c = '\0';
} else {
QSKIP("does not have the 1st datagram", SkipSingle);
}
if (server.hasPendingDatagrams()) {
QCOMPARE(server.pendingDatagramSize(), qint64(0));
QCOMPARE(server.readDatagram(&c, 1), qint64(0));
QCOMPARE(c, '\0'); // untouched
c = '\0';
} else {
QSKIP("does not have the 2nd datagram", SkipSingle);
}
if (server.hasPendingDatagrams()) {
QCOMPARE(server.pendingDatagramSize(), qint64(10));
QCOMPARE(server.readDatagram(&c, 1), qint64(1));
QCOMPARE(c, '3');
} else {
QSKIP("does not have the 3rd datagram", SkipSingle);
}
}
void tst_QUdpSocket::writeDatagram()
{
QUdpSocket server;
#ifdef FORCE_SESSION
server.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
QVERIFY2(server.bind(), server.errorString().toLatin1().constData());
QHostAddress serverAddress = QHostAddress::LocalHost;
if (!(server.localAddress() == QHostAddress::AnyIPv4 || server.localAddress() == QHostAddress::AnyIPv6))
serverAddress = server.localAddress();
QUdpSocket client;
#ifdef FORCE_SESSION
client.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
qRegisterMetaType<qint64>("qint64");
qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
for(int i=0;;i++) {
QSignalSpy errorspy(&client, SIGNAL(error(QAbstractSocket::SocketError)));
QSignalSpy bytesspy(&client, SIGNAL(bytesWritten(qint64)));
qint64 written = client.writeDatagram(QByteArray(i * 1024, 'w'), serverAddress,
server.localPort());
if (written != i * 1024) {
#if defined (Q_OS_HPUX)
QSKIP("HP-UX 11.11 on hai (PA-RISC 64) truncates too long datagrams.", SkipSingle);
#endif
QCOMPARE(bytesspy.count(), 0);
QCOMPARE(errorspy.count(), 1);
QCOMPARE(*static_cast<const int *>(errorspy.at(0).at(0).constData()),
int(QUdpSocket::DatagramTooLargeError));
QCOMPARE(client.error(), QUdpSocket::DatagramTooLargeError);
break;
}
QVERIFY(bytesspy.count() == 1);
QCOMPARE(*static_cast<const qint64 *>(bytesspy.at(0).at(0).constData()),
qint64(i * 1024));
QCOMPARE(errorspy.count(), 0);
if (!server.waitForReadyRead(5000)) {
#ifdef Q_OS_SYMBIAN
//symbian receive buffer for datagrams is ~30k, but it can send datagrams up to the maximum 64k...
if (i > 28) {
i = 64;
continue;
}
#endif
QSKIP(QString("UDP packet lost at size %1, unable to complete the test.").arg(i * 1024).toLatin1().data(), SkipSingle);
}
QCOMPARE(server.pendingDatagramSize(), qint64(i * 1024));
QCOMPARE(server.readDatagram(0, 0), qint64(0));
}
}
void tst_QUdpSocket::performance()
{
#if defined(Q_OS_SYMBIAN)
// Large packets seems not to go through on Symbian
// Reason might be also fragmentation due to VPN connection etc
QFETCH_GLOBAL(bool, setProxy);
QFETCH_GLOBAL(int, proxyType);
int arrSize = 8192;
if (setProxy && proxyType == QNetworkProxy::Socks5Proxy)
arrSize = 1024;
QByteArray arr(arrSize, '@');
#else
QByteArray arr(8192, '@');
#endif // Q_OS_SYMBIAN
QUdpSocket server;
#ifdef FORCE_SESSION
server.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
QVERIFY2(server.bind(), server.errorString().toLatin1().constData());
QHostAddress serverAddress = QHostAddress::LocalHost;
if (!(server.localAddress() == QHostAddress::AnyIPv4 || server.localAddress() == QHostAddress::AnyIPv6))
serverAddress = server.localAddress();
QUdpSocket client;
#ifdef FORCE_SESSION
client.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
client.connectToHost(serverAddress, server.localPort());
QVERIFY(client.waitForConnected(10000));
QTime stopWatch;
stopWatch.start();
qint64 nbytes = 0;
while (stopWatch.elapsed() < 5000) {
for (int i = 0; i < 100; ++i) {
if (client.write(arr.data(), arr.size()) > 0) {
do {
nbytes += server.readDatagram(arr.data(), arr.size());
} while (server.hasPendingDatagrams());
}
}
}
float secs = stopWatch.elapsed() / 1000.0;
qDebug("\t%.2fMB/%.2fs: %.2fMB/s", float(nbytes / (1024.0*1024.0)),
secs, float(nbytes / (1024.0*1024.0)) / secs);
#if defined(Q_OS_SYMBIAN)
if(nbytes == 0) {
qDebug("No bytes passed through local UDP socket, since UDP socket write returns EWOULDBLOCK");
qDebug("Should try with blocking sockets, but it is not currently possible due to Open C defect");
}
#endif
}
void tst_QUdpSocket::bindMode()
{
QFETCH_GLOBAL(bool, setProxy);
if (setProxy) {
QFETCH_GLOBAL(int, proxyType);
if (proxyType == QNetworkProxy::Socks5Proxy) {
QSKIP("With socks5 explicit port binding is not supported.", SkipAll);
}
}
QUdpSocket socket;
#ifdef FORCE_SESSION
socket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
QVERIFY2(socket.bind(), socket.errorString().toLatin1().constData());
QUdpSocket socket2;
#ifdef FORCE_SESSION
socket2.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
QVERIFY(!socket2.bind(socket.localPort()));
#if defined(Q_OS_SYMBIAN)
if(RProcess().HasCapability(ECapabilityNetworkControl)) {
qDebug("Test executed *with* NetworkControl capability");
// In Symbian OS ReuseAddressHint together with NetworkControl capability
// gives application *always* right to bind to port. I.e. it does not matter
// if first socket was bound with any bind flag. Since autotests in Symbian
// are currently executed with ALL -TCB rights, this path is the one executed.
QVERIFY(socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint));
socket.close();
socket2.close();
QVERIFY2(socket.bind(0, QUdpSocket::ShareAddress), socket.errorString().toLatin1().constData());
QVERIFY(!socket2.bind(socket.localPort()));
QVERIFY2(socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint), socket2.errorString().toLatin1().constData());
socket.close();
socket2.close();
QVERIFY2(socket.bind(0, QUdpSocket::DontShareAddress), socket.errorString().toLatin1().constData());
QVERIFY(!socket2.bind(socket.localPort()));
QVERIFY(socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint));
socket.close();
socket2.close();
} else {
qDebug("Test executed *without* NetworkControl capability");
// If we don't have NetworkControl capability, attempt to bind already bound
// address will *always* fail. I.e. it does not matter if first socket was
// bound with any bind flag.
QVERIFY(!socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint));
socket.close();
QVERIFY2(socket.bind(0, QUdpSocket::ShareAddress), socket.errorString().toLatin1().constData());
QVERIFY(!socket2.bind(socket.localPort()));
QVERIFY2(!socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint), socket2.errorString().toLatin1().constData());
socket.close();
QVERIFY2(socket.bind(0, QUdpSocket::DontShareAddress), socket.errorString().toLatin1().constData());
QVERIFY(!socket2.bind(socket.localPort()));
QVERIFY(!socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint));
socket.close();
}
#elif defined(Q_OS_UNIX)
QVERIFY(!socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint));
socket.close();
QVERIFY2(socket.bind(0, QUdpSocket::ShareAddress), socket.errorString().toLatin1().constData());
QVERIFY2(socket2.bind(socket.localPort()), socket2.errorString().toLatin1().constData());
socket2.close();
QVERIFY2(socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint), socket2.errorString().toLatin1().constData());
#else
// Depending on the user's privileges, this or will succeed or
// fail. Admins are allowed to reuse the address, but nobody else.
if (!socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint), socket2.errorString().toLatin1().constData())
qWarning("Failed to bind with QUdpSocket::ReuseAddressHint, user isn't an adminstrator?");
socket.close();
QVERIFY2(socket.bind(0, QUdpSocket::ShareAddress), socket.errorString().toLatin1().constData());
QVERIFY(!socket2.bind(socket.localPort()));
socket.close();
QVERIFY2(socket.bind(0, QUdpSocket::DontShareAddress), socket.errorString().toLatin1().constData());
QVERIFY(!socket2.bind(socket.localPort()));
QVERIFY(!socket2.bind(socket.localPort(), QUdpSocket::ReuseAddressHint));
#endif
}
void tst_QUdpSocket::writeDatagramToNonExistingPeer_data()
{
QTest::addColumn<bool>("bind");
QTest::addColumn<QHostAddress>("peerAddress");
QHostAddress localhost(QHostAddress::LocalHost);
QHostAddress remote = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first();
QTest::newRow("localhost-unbound") << false << localhost;
QTest::newRow("localhost-bound") << true << localhost;
QTest::newRow("remote-unbound") << false << remote;
QTest::newRow("remote-bound") << true << remote;
}
void tst_QUdpSocket::writeDatagramToNonExistingPeer()
{
QFETCH(bool, bind);
QFETCH(QHostAddress, peerAddress);
quint16 peerPort = 33533 + int(bind);
QUdpSocket sUdp;
#ifdef FORCE_SESSION
sUdp.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
QSignalSpy sReadyReadSpy(&sUdp, SIGNAL(readyRead()));
if (bind)
QVERIFY(sUdp.bind());
QCOMPARE(sUdp.writeDatagram("", 1, peerAddress, peerPort), qint64(1));
QTestEventLoop::instance().enterLoop(1);
QCOMPARE(sReadyReadSpy.count(), 0);
}
void tst_QUdpSocket::writeToNonExistingPeer_data()
{
QTest::addColumn<QHostAddress>("peerAddress");
QHostAddress localhost(QHostAddress::LocalHost);
QHostAddress remote = QHostInfo::fromName(QtNetworkSettings::serverName()).addresses().first();
// write (required to be connected)
QTest::newRow("localhost") << localhost;
QTest::newRow("remote") << remote;
}
void tst_QUdpSocket::writeToNonExistingPeer()
{
QSKIP("Connected-mode UDP sockets and their behaviour are erratic", SkipAll);
QFETCH(QHostAddress, peerAddress);
quint16 peerPort = 34534;
qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
QUdpSocket sConnected;
#ifdef FORCE_SESSION
sConnected.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
QSignalSpy sConnectedReadyReadSpy(&sConnected, SIGNAL(readyRead()));
QSignalSpy sConnectedErrorSpy(&sConnected, SIGNAL(error(QAbstractSocket::SocketError)));
sConnected.connectToHost(peerAddress, peerPort, QIODevice::ReadWrite);
QVERIFY(sConnected.waitForConnected(10000));
// the first write succeeds...
QCOMPARE(sConnected.write("", 1), qint64(1));
// the second one should fail!
QTest::qSleep(1000); // do not process events
QCOMPARE(sConnected.write("", 1), qint64(-1));
QCOMPARE(int(sConnected.error()), int(QUdpSocket::ConnectionRefusedError));
// the third one will succeed...
QCOMPARE(sConnected.write("", 1), qint64(1));
QTestEventLoop::instance().enterLoop(1);
QCOMPARE(sConnectedReadyReadSpy.count(), 0);
QCOMPARE(sConnectedErrorSpy.count(), 1);
QCOMPARE(int(sConnected.error()), int(QUdpSocket::ConnectionRefusedError));
// we should now get a read error
QCOMPARE(sConnected.write("", 1), qint64(1));
QTest::qSleep(1000); // do not process events
char buf[2];
QVERIFY(!sConnected.hasPendingDatagrams());
QCOMPARE(sConnected.bytesAvailable(), Q_INT64_C(0));
QCOMPARE(sConnected.pendingDatagramSize(), Q_INT64_C(-1));
QCOMPARE(sConnected.readDatagram(buf, 2), Q_INT64_C(-1));
QCOMPARE(int(sConnected.error()), int(QUdpSocket::ConnectionRefusedError));
QCOMPARE(sConnected.write("", 1), qint64(1));
QTest::qSleep(1000); // do not process events
QCOMPARE(sConnected.read(buf, 2), Q_INT64_C(0));
QCOMPARE(int(sConnected.error()), int(QUdpSocket::ConnectionRefusedError));
// we should still be connected
QCOMPARE(int(sConnected.state()), int(QUdpSocket::ConnectedState));
}
void tst_QUdpSocket::outOfProcessConnectedClientServerTest()
{
#if defined(Q_OS_WINCE) || defined (Q_OS_SYMBIAN)
QSKIP("This test depends on reading data from QProcess (not supported on Qt/WinCE and Symbian).", SkipAll);
#endif
#if defined(QT_NO_PROCESS)
QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll);
#else
QProcess serverProcess;
serverProcess.start(QLatin1String("clientserver/clientserver server 1 1"),
QIODevice::ReadWrite | QIODevice::Text);
QVERIFY2(serverProcess.waitForStarted(3000),
qPrintable("Failed to start subprocess: " + serverProcess.errorString()));
// Wait until the server has started and reports success.
while (!serverProcess.canReadLine())
QVERIFY(serverProcess.waitForReadyRead(3000));
QByteArray serverGreeting = serverProcess.readLine();
QVERIFY(serverGreeting != QByteArray("XXX\n"));
int serverPort = serverGreeting.trimmed().toInt();
QVERIFY(serverPort > 0 && serverPort < 65536);
QProcess clientProcess;
clientProcess.start(QString::fromLatin1("clientserver/clientserver connectedclient %1 %2")
.arg(QLatin1String("127.0.0.1")).arg(serverPort),
QIODevice::ReadWrite | QIODevice::Text);
QVERIFY2(clientProcess.waitForStarted(3000),
qPrintable("Failed to start subprocess: " + clientProcess.errorString()));
// Wait until the server has started and reports success.
while (!clientProcess.canReadLine())
QVERIFY(clientProcess.waitForReadyRead(3000));
QByteArray clientGreeting = clientProcess.readLine();
QCOMPARE(clientGreeting, QByteArray("ok\n"));
// Let the client and server talk for 3 seconds
QTest::qWait(3000);
QStringList serverData = QString::fromLocal8Bit(serverProcess.readAll()).split("\n");
QStringList clientData = QString::fromLocal8Bit(clientProcess.readAll()).split("\n");
QVERIFY(serverData.size() > 5);
QVERIFY(clientData.size() > 5);
for (int i = 0; i < clientData.size() / 2; ++i) {
QCOMPARE(clientData.at(i * 2), QString("readData()"));
QCOMPARE(serverData.at(i * 3), QString("readData()"));
QString cdata = clientData.at(i * 2 + 1);
QString sdata = serverData.at(i * 3 + 1);
QVERIFY(cdata.startsWith(QLatin1String("got ")));
QCOMPARE(cdata.mid(4).trimmed().toInt(), sdata.mid(4).trimmed().toInt() * 2);
QVERIFY(serverData.at(i * 3 + 2).startsWith(QLatin1String("sending ")));
QCOMPARE(serverData.at(i * 3 + 2).trimmed().mid(8).toInt(),
sdata.mid(4).trimmed().toInt() * 2);
}
clientProcess.kill();
QVERIFY(clientProcess.waitForFinished());
serverProcess.kill();
QVERIFY(serverProcess.waitForFinished());
#endif
}
void tst_QUdpSocket::outOfProcessUnconnectedClientServerTest()
{
#if defined(Q_OS_WINCE) || defined (Q_OS_SYMBIAN)
QSKIP("This test depends on reading data from QProcess (not supported on Qt/WinCE and Symbian).", SkipAll);
#endif
#if defined(QT_NO_PROCESS)
QSKIP("Qt was compiled with QT_NO_PROCESS", SkipAll);
#else
QProcess serverProcess;
serverProcess.start(QLatin1String("clientserver/clientserver server 1 1"),
QIODevice::ReadWrite | QIODevice::Text);
QVERIFY2(serverProcess.waitForStarted(3000),
qPrintable("Failed to start subprocess: " + serverProcess.errorString()));
// Wait until the server has started and reports success.
while (!serverProcess.canReadLine())
QVERIFY(serverProcess.waitForReadyRead(3000));
QByteArray serverGreeting = serverProcess.readLine();
QVERIFY(serverGreeting != QByteArray("XXX\n"));
int serverPort = serverGreeting.trimmed().toInt();
QVERIFY(serverPort > 0 && serverPort < 65536);
QProcess clientProcess;
clientProcess.start(QString::fromLatin1("clientserver/clientserver unconnectedclient %1 %2")
.arg(QLatin1String("127.0.0.1")).arg(serverPort),
QIODevice::ReadWrite | QIODevice::Text);
QVERIFY2(clientProcess.waitForStarted(3000),
qPrintable("Failed to start subprocess: " + clientProcess.errorString()));
// Wait until the server has started and reports success.
while (!clientProcess.canReadLine())
QVERIFY(clientProcess.waitForReadyRead(3000));
QByteArray clientGreeting = clientProcess.readLine();
QCOMPARE(clientGreeting, QByteArray("ok\n"));
// Let the client and server talk for 3 seconds
QTest::qWait(3000);
QStringList serverData = QString::fromLocal8Bit(serverProcess.readAll()).split("\n");
QStringList clientData = QString::fromLocal8Bit(clientProcess.readAll()).split("\n");
QVERIFY(serverData.size() > 5);
QVERIFY(clientData.size() > 5);
for (int i = 0; i < clientData.size() / 2; ++i) {
QCOMPARE(clientData.at(i * 2), QString("readData()"));
QCOMPARE(serverData.at(i * 3), QString("readData()"));
QString cdata = clientData.at(i * 2 + 1);
QString sdata = serverData.at(i * 3 + 1);
QVERIFY(cdata.startsWith(QLatin1String("got ")));
QCOMPARE(cdata.mid(4).trimmed().toInt(), sdata.mid(4).trimmed().toInt() * 2);
QVERIFY(serverData.at(i * 3 + 2).startsWith(QLatin1String("sending ")));
QCOMPARE(serverData.at(i * 3 + 2).trimmed().mid(8).toInt(),
sdata.mid(4).trimmed().toInt() * 2);
}
clientProcess.kill();
QVERIFY(clientProcess.waitForFinished());
serverProcess.kill();
QVERIFY(serverProcess.waitForFinished());
#endif
}
void tst_QUdpSocket::zeroLengthDatagram()
{
QFETCH_GLOBAL(bool, setProxy);
if (setProxy)
return;
QUdpSocket receiver;
#ifdef FORCE_SESSION
receiver.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
QVERIFY(receiver.bind());
QVERIFY(!receiver.waitForReadyRead(100));
QVERIFY(!receiver.hasPendingDatagrams());
QUdpSocket sender;
#ifdef FORCE_SESSION
sender.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
QCOMPARE(sender.writeDatagram(QByteArray(), QHostAddress::LocalHost, receiver.localPort()), qint64(0));
QVERIFY(receiver.waitForReadyRead(1000));
QVERIFY(receiver.hasPendingDatagrams());
char buf;
QCOMPARE(receiver.readDatagram(&buf, 1), qint64(0));
}
void tst_QUdpSocket::multicastTtlOption_data()
{
QTest::addColumn<QHostAddress>("bindAddress");
QTest::addColumn<int>("ttl");
QTest::addColumn<int>("expected");
QList<QHostAddress> addresses;
addresses += QHostAddress(QHostAddress::Any);
addresses += QHostAddress(QHostAddress::AnyIPv6);
foreach (const QHostAddress &address, addresses) {
QTest::newRow(QString("%1 0").arg(address.toString()).toAscii()) << address << 0 << 0;
QTest::newRow(QString("%1 1").arg(address.toString()).toAscii()) << address << 1 << 1;
QTest::newRow(QString("%1 2").arg(address.toString()).toAscii()) << address << 2 << 2;
QTest::newRow(QString("%1 128").arg(address.toString()).toAscii()) << address << 128 << 128;
QTest::newRow(QString("%1 255").arg(address.toString()).toAscii()) << address << 255 << 255;
QTest::newRow(QString("%1 1024").arg(address.toString()).toAscii()) << address << 1024 << 1;
}
}
void tst_QUdpSocket::multicastTtlOption()
{
QFETCH_GLOBAL(bool, setProxy);
QFETCH(QHostAddress, bindAddress);
QFETCH(int, ttl);
QFETCH(int, expected);
if (setProxy) {
// UDP multicast does not work with proxies
expected = 0;
}
QUdpSocket udpSocket;
#ifdef FORCE_SESSION
udpSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
// bind, but ignore the result, we are only interested in initializing the socket
(void) udpSocket.bind(bindAddress, 0);
udpSocket.setSocketOption(QUdpSocket::MulticastTtlOption, ttl);
QCOMPARE(udpSocket.socketOption(QUdpSocket::MulticastTtlOption).toInt(), expected);
}
void tst_QUdpSocket::multicastLoopbackOption_data()
{
QTest::addColumn<QHostAddress>("bindAddress");
QTest::addColumn<int>("loopback");
QTest::addColumn<int>("expected");
QList<QHostAddress> addresses;
addresses += QHostAddress(QHostAddress::Any);
addresses += QHostAddress(QHostAddress::AnyIPv6);
foreach (const QHostAddress &address, addresses) {
QTest::newRow(QString("%1 0").arg(address.toString()).toAscii()) << address << 0 << 0;
QTest::newRow(QString("%1 1").arg(address.toString()).toAscii()) << address << 1 << 1;
QTest::newRow(QString("%1 2").arg(address.toString()).toAscii()) << address << 2 << 1;
QTest::newRow(QString("%1 0 again").arg(address.toString()).toAscii()) << address << 0 << 0;
QTest::newRow(QString("%1 2 again").arg(address.toString()).toAscii()) << address << 2 << 1;
QTest::newRow(QString("%1 0 last time").arg(address.toString()).toAscii()) << address << 0 << 0;
QTest::newRow(QString("%1 1 again").arg(address.toString()).toAscii()) << address << 1 << 1;
}
}
void tst_QUdpSocket::multicastLoopbackOption()
{
QFETCH_GLOBAL(bool, setProxy);
QFETCH(QHostAddress, bindAddress);
QFETCH(int, loopback);
QFETCH(int, expected);
if (setProxy) {
// UDP multicast does not work with proxies
expected = 0;
}
QUdpSocket udpSocket;
#ifdef FORCE_SESSION
udpSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
// bind, but ignore the result, we are only interested in initializing the socket
(void) udpSocket.bind(bindAddress, 0);
udpSocket.setSocketOption(QUdpSocket::MulticastLoopbackOption, loopback);
QCOMPARE(udpSocket.socketOption(QUdpSocket::MulticastLoopbackOption).toInt(), expected);
}
void tst_QUdpSocket::multicastJoinBeforeBind_data()
{
QTest::addColumn<QHostAddress>("groupAddress");
QTest::newRow("valid ipv4 group address") << QHostAddress("239.255.118.62");
QTest::newRow("invalid ipv4 group address") << QHostAddress(QHostAddress::Broadcast);
QTest::newRow("valid ipv6 group address") << QHostAddress("FF01::114");
QTest::newRow("invalid ipv6 group address") << QHostAddress(QHostAddress::AnyIPv6);
}
void tst_QUdpSocket::multicastJoinBeforeBind()
{
QFETCH(QHostAddress, groupAddress);
QUdpSocket udpSocket;
#ifdef FORCE_SESSION
udpSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
// cannot join group before binding
QTest::ignoreMessage(QtWarningMsg, "QUdpSocket::joinMulticastGroup() called on a QUdpSocket when not in QUdpSocket::BoundState");
QVERIFY(!udpSocket.joinMulticastGroup(groupAddress));
}
void tst_QUdpSocket::multicastLeaveAfterClose_data()
{
QTest::addColumn<QHostAddress>("groupAddress");
QTest::newRow("valid ipv4 group address") << QHostAddress("239.255.118.62");
QTest::newRow("valid ipv6 group address") << QHostAddress("FF01::114");
}
void tst_QUdpSocket::multicastLeaveAfterClose()
{
QFETCH_GLOBAL(bool, setProxy);
QFETCH(QHostAddress, groupAddress);
if (setProxy) {
QSKIP("UDP Multicast does not work with proxies", SkipAll);
}
QUdpSocket udpSocket;
#ifdef FORCE_SESSION
udpSocket.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
#ifdef Q_OS_SYMBIAN
QVERIFY2(udpSocket.bind(),
qPrintable(udpSocket.errorString()));
#else
QVERIFY2(udpSocket.bind(groupAddress, 0),
qPrintable(udpSocket.errorString()));
#endif
QVERIFY2(udpSocket.joinMulticastGroup(groupAddress),
qPrintable(udpSocket.errorString()));
udpSocket.close();
QTest::ignoreMessage(QtWarningMsg, "QUdpSocket::leaveMulticastGroup() called on a QUdpSocket when not in QUdpSocket::BoundState");
QVERIFY(!udpSocket.leaveMulticastGroup(groupAddress));
}
void tst_QUdpSocket::setMulticastInterface_data()
{
QTest::addColumn<QNetworkInterface>("iface");
QTest::addColumn<QHostAddress>("address");
QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
foreach (const QNetworkInterface &iface, interfaces) {
foreach (const QNetworkAddressEntry &entry, iface.addressEntries()) {
QTest::newRow(QString("%1:%2").arg(iface.name()).arg(entry.ip().toString()).toAscii())
<< iface
<< entry.ip();
}
}
}
void tst_QUdpSocket::setMulticastInterface()
{
#ifdef Q_OS_SYMBIAN
QSKIP("Symbian has no IPV6_MULTICAST_IF equivalent", SkipAll);
#else
QFETCH_GLOBAL(bool, setProxy);
QFETCH(QNetworkInterface, iface);
QFETCH(QHostAddress, address);
QUdpSocket udpSocket;
// bind initializes the socket
bool bound = udpSocket.bind((address.protocol() == QAbstractSocket::IPv6Protocol
? QHostAddress(QHostAddress::AnyIPv6)
: QHostAddress(QHostAddress::Any)),
0);
if (!bound)
QTest::ignoreMessage(QtWarningMsg, "QUdpSocket::setMulticastInterface() called on a QUdpSocket when not in QUdpSocket::BoundState");
udpSocket.setMulticastInterface(iface);
if (!bound)
QTest::ignoreMessage(QtWarningMsg, "QUdpSocket::multicastInterface() called on a QUdpSocket when not in QUdpSocket::BoundState");
QNetworkInterface iface2 = udpSocket.multicastInterface();
if (!setProxy) {
QVERIFY(iface2.isValid());
QCOMPARE(iface.name(), iface2.name());
} else {
QVERIFY(!iface2.isValid());
}
#endif
}
void tst_QUdpSocket::multicast_data()
{
QHostAddress anyAddress = QHostAddress(QHostAddress::AnyIPv4);
QHostAddress groupAddress = QHostAddress("239.255.118.62");
QHostAddress any6Address = QHostAddress(QHostAddress::AnyIPv6);
QHostAddress group6Address = QHostAddress("FF01::114");
QTest::addColumn<QHostAddress>("bindAddress");
QTest::addColumn<bool>("bindResult");
QTest::addColumn<QHostAddress>("groupAddress");
QTest::addColumn<bool>("joinResult");
QTest::newRow("valid bind, group ipv4 address") << anyAddress << true << groupAddress << true;
QTest::newRow("valid bind, invalid group ipv4 address") << anyAddress << true << anyAddress << false;
QTest::newRow("same bind, group ipv4 address") << groupAddress << true << groupAddress << true;
QTest::newRow("valid bind, group ipv6 address") << any6Address << true << group6Address << true;
QTest::newRow("valid bind, invalid group ipv6 address") << any6Address << true << any6Address << false;
QTest::newRow("same bind, group ipv6 address") << group6Address << true << group6Address << true;
}
void tst_QUdpSocket::multicast()
{
QFETCH_GLOBAL(bool, setProxy);
QFETCH(QHostAddress, bindAddress);
QFETCH(bool, bindResult);
QFETCH(QHostAddress, groupAddress);
QFETCH(bool, joinResult);
if (setProxy) {
// UDP multicast does not work with proxies
if (
#ifndef Q_OS_WIN
//windows native socket engine binds 0.0.0.0 instead of the requested multicast address
(bindAddress.protocol() == QAbstractSocket::IPv4Protocol && (bindAddress.toIPv4Address() & 0xffff0000) == 0xefff0000) ||
#endif
bindAddress.protocol() == QAbstractSocket::IPv6Protocol) {
// proxy cannot bind to IPv6 or multicast addresses
bindResult = false;
}
joinResult = false;
}
QUdpSocket receiver;
#ifdef FORCE_SESSION
receiver.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
// bind first, then verify that we can join the multicast group
#ifdef Q_OS_SYMBIAN
if (!setProxy) {
QEXPECT_FAIL("same bind, group ipv4 address", "bind to group address not supported on symbian", Abort);
QEXPECT_FAIL("same bind, group ipv6 address", "bind to group address not supported on symbian", Abort);
}
#endif
QVERIFY2(receiver.bind(bindAddress, 0) == bindResult,
qPrintable(receiver.errorString()));
if (!bindResult)
return;
QVERIFY2(receiver.joinMulticastGroup(groupAddress) == joinResult,
qPrintable(receiver.errorString()));
if (!joinResult)
return;
QList<QByteArray> datagrams = QList<QByteArray>()
<< QByteArray("0123")
<< QByteArray("4567")
<< QByteArray("89ab")
<< QByteArray("cdef");
QUdpSocket sender;
#ifdef FORCE_SESSION
sender.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
sender.bind();
foreach (const QByteArray &datagram, datagrams) {
QCOMPARE(int(sender.writeDatagram(datagram, groupAddress, receiver.localPort())),
int(datagram.size()));
}
QVERIFY2(receiver.waitForReadyRead(),
qPrintable(receiver.errorString()));
QVERIFY(receiver.hasPendingDatagrams());
QList<QByteArray> receivedDatagrams;
while (receiver.hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(receiver.pendingDatagramSize());
receiver.readDatagram(datagram.data(), datagram.size(), 0, 0);
receivedDatagrams << datagram;
}
#ifdef Q_OS_SYMBIAN
QEXPECT_FAIL("valid bind, group ipv4 address", "IPv4 multicast not supported on symbian", Abort);
#endif
QCOMPARE(receivedDatagrams, datagrams);
QVERIFY2(receiver.leaveMulticastGroup(groupAddress), qPrintable(receiver.errorString()));
}
void tst_QUdpSocket::echo_data()
{
QTest::addColumn<bool>("connect");
QTest::newRow("writeDatagram") << false;
QTest::newRow("write") << true;
}
void tst_QUdpSocket::echo()
{
QFETCH(bool, connect);
QHostInfo info = QHostInfo::fromName(QtNetworkSettings::serverName());
QVERIFY(info.addresses().count());
QHostAddress remote = info.addresses().first();
QUdpSocket sock;
#ifdef FORCE_SESSION
sock.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
if (connect) {
sock.connectToHost(remote, 7);
QVERIFY(sock.waitForConnected(10000));
} else {
sock.bind();
}
QByteArray out(30, 'x');
QByteArray in;
int successes = 0;
for (int i=0;i<10;i++) {
if (connect) {
sock.write(out);
} else {
sock.writeDatagram(out, remote, 7);
}
if (sock.waitForReadyRead(1000)) {
while (sock.hasPendingDatagrams()) {
QHostAddress from;
quint16 port;
if (connect) {
in = sock.read(sock.pendingDatagramSize());
} else {
in.resize(sock.pendingDatagramSize());
sock.readDatagram(in.data(), in.length(), &from, &port);
}
if (in==out)
successes++;
}
}
if (!sock.isValid())
QFAIL(sock.errorString().toLatin1().constData());
qDebug() << "packets in" << successes << "out" << i;
QTest::qWait(50); //choke to avoid triggering flood/DDoS protections on echo service
}
QVERIFY(successes >= 9);
}
QTEST_MAIN(tst_QUdpSocket)
#include "tst_qudpsocket.moc"