From e09778c7f3ef8a2272ab7dd18d00619e456fa84b Mon Sep 17 00:00:00 2001 From: Jens Bache-Wiig Date: Wed, 1 Sep 2010 14:05:05 +0200 Subject: Align icon positions when using TextUnderIcon for tool buttons The widget would explicitly ignore the TextUnderIcon flag if no text was set. This is wrong because it should be up to the style to decide such details and the end result are unaligned icons by default. Task-number: QTBUG-3715 Reviewed-by: richard --- src/gui/widgets/qtoolbutton.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/gui/widgets/qtoolbutton.cpp b/src/gui/widgets/qtoolbutton.cpp index 1822db8..c18f660 100644 --- a/src/gui/widgets/qtoolbutton.cpp +++ b/src/gui/widgets/qtoolbutton.cpp @@ -394,9 +394,6 @@ void QToolButton::initStyleOption(QStyleOptionToolButton *option) const option->toolButtonStyle = Qt::ToolButtonTextOnly; else if (option->toolButtonStyle != Qt::ToolButtonTextOnly) option->toolButtonStyle = Qt::ToolButtonIconOnly; - } else { - if (d->text.isEmpty() && option->toolButtonStyle != Qt::ToolButtonIconOnly) - option->toolButtonStyle = Qt::ToolButtonIconOnly; } option->pos = pos(); -- cgit v0.12 From acc7d0d4111916af4814f6521e5269e38c34e76f Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 5 May 2010 16:20:06 +0200 Subject: Add QUdpSocket::joinMulticastGroup() and leaveMulticastGroup() Each function takes a groupAddress and optional source address, network interface, and flags. --- src/network/socket/qudpsocket.h | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/network/socket/qudpsocket.h b/src/network/socket/qudpsocket.h index f477306..8cb6dc3 100644 --- a/src/network/socket/qudpsocket.h +++ b/src/network/socket/qudpsocket.h @@ -53,6 +53,7 @@ QT_MODULE(Network) #ifndef QT_NO_UDPSOCKET +class QNetworkInterface; class QUdpSocketPrivate; class Q_NETWORK_EXPORT QUdpSocket : public QAbstractSocket @@ -67,6 +68,12 @@ public: }; Q_DECLARE_FLAGS(BindMode, BindFlag) + enum MulticastFlag { + DefaultMulticastFlagForPlatform = 0x0, + MulticastLoopback = 0x1 + }; + Q_DECLARE_FLAGS(MulticastMode, MulticastFlag) + explicit QUdpSocket(QObject *parent = 0); virtual ~QUdpSocket(); @@ -76,6 +83,17 @@ public: bool bind(quint16 port, BindMode mode); // ### Qt 5: Merge the bind functions + bool joinMulticastGroup(const QHostAddress &groupAddress, + MulticastMode mode = DefaultMulticastFlagForPlatform); + bool joinMulticastGroup(const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface, + MulticastMode mode = DefaultMulticastFlagForPlatform); + bool leaveMulticastGroup(const QHostAddress &groupAddress); + bool leaveMulticastGroup(const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface); + bool hasPendingDatagrams() const; qint64 pendingDatagramSize() const; qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *host = 0, quint16 *port = 0); -- cgit v0.12 From e6b836293346d81c926bf17c5e9b128c969dc672 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 5 May 2010 16:20:06 +0200 Subject: Implement QUdpSocket::joinMulticastGroup() and leaveMulticastGroup() This is actually just done in the socket engine, so we need to add the API for setting the loopback socket option and joining/leaving the group. Implementation for the various engines will follow. --- src/network/socket/qabstractsocketengine_p.h | 11 ++++++- src/network/socket/qudpsocket.cpp | 43 ++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h index 3669852..9a2a7c3 100644 --- a/src/network/socket/qabstractsocketengine_p.h +++ b/src/network/socket/qabstractsocketengine_p.h @@ -61,6 +61,7 @@ QT_BEGIN_NAMESPACE class QAuthenticator; class QAbstractSocketEnginePrivate; +class QNetworkInterface; class QNetworkProxy; class QAbstractSocketEngineReceiver { @@ -94,7 +95,8 @@ public: BindExclusively, ReceiveOutOfBandData, LowDelayOption, - KeepAliveOption + KeepAliveOption, + MulticastLoopback }; virtual bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol) = 0; @@ -118,6 +120,13 @@ public: virtual qint64 write(const char *data, qint64 len) = 0; #ifndef QT_NO_UDPSOCKET + virtual bool joinMulticastGroup(const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface) = 0; + virtual bool leaveMulticastGroup(const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface) = 0; + virtual qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, quint16 *port = 0) = 0; virtual qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr, diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp index d5366d3..4fc88dc 100644 --- a/src/network/socket/qudpsocket.cpp +++ b/src/network/socket/qudpsocket.cpp @@ -145,6 +145,7 @@ */ #include "qhostaddress.h" +#include "qnetworkinterface.h" #include "qabstractsocket_p.h" #include "qudpsocket.h" @@ -329,6 +330,48 @@ bool QUdpSocket::bind(quint16 port, BindMode mode) } /*! + \since 4.6 +*/ +bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress, MulticastMode mode) +{ + return joinMulticastGroup(groupAddress, QHostAddress::Any, QNetworkInterface(), mode); +} + +/*! + \since 4.6 + \overload +*/ +bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface, + MulticastMode mode) +{ + Q_D(QUdpSocket); + d->socketEngine->setOption(QAbstractSocketEngine::MulticastLoopback, + (mode & MulticastLoopback) ? 1 : 0); + return d->socketEngine->joinMulticastGroup(groupAddress, sourceAddress, interface); +} + +/*! + \since 4.6 +*/ +bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress) +{ + return leaveMulticastGroup(groupAddress, QHostAddress::Any, QNetworkInterface()); +} + +/*! + \since 4.6 + \overload +*/ +bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface) +{ + return d_func()->socketEngine->leaveMulticastGroup(groupAddress, sourceAddress, interface); +} + +/*! Returns true if at least one datagram is waiting to be read; otherwise returns false. -- cgit v0.12 From 2abb3642bce6e4b32f44c3724dc8fcc9acf9976c Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 5 May 2010 16:20:07 +0200 Subject: Implementation of multicast API for the HTTP and SOCKS5 engines We don't support multicast via proxy, so these function just return false. --- src/network/socket/qhttpsocketengine.cpp | 18 ++++++++++++++++++ src/network/socket/qhttpsocketengine_p.h | 7 +++++++ src/network/socket/qsocks5socketengine.cpp | 18 ++++++++++++++++++ src/network/socket/qsocks5socketengine_p.h | 7 +++++++ 4 files changed, 50 insertions(+) diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp index dfda257..c7e5b95 100644 --- a/src/network/socket/qhttpsocketengine.cpp +++ b/src/network/socket/qhttpsocketengine.cpp @@ -239,6 +239,24 @@ qint64 QHttpSocketEngine::write(const char *data, qint64 len) } #ifndef QT_NO_UDPSOCKET +bool QHttpSocketEngine::joinMulticastGroup(const QHostAddress &, + const QHostAddress &, + const QNetworkInterface &) +{ + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Operation on socket is not supported")); + return false; +} + +bool QHttpSocketEngine::leaveMulticastGroup(const QHostAddress &, + const QHostAddress &, + const QNetworkInterface &) +{ + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Operation on socket is not supported")); + return false; +} + qint64 QHttpSocketEngine::readDatagram(char *, qint64, QHostAddress *, quint16 *) { diff --git a/src/network/socket/qhttpsocketengine_p.h b/src/network/socket/qhttpsocketengine_p.h index 5051def..18add3c 100644 --- a/src/network/socket/qhttpsocketengine_p.h +++ b/src/network/socket/qhttpsocketengine_p.h @@ -102,6 +102,13 @@ public: qint64 write(const char *data, qint64 len); #ifndef QT_NO_UDPSOCKET + bool joinMulticastGroup(const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface); + bool leaveMulticastGroup(const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface); + qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, quint16 *port = 0); qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr, diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index f68edfe..3da00ed 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -1544,6 +1544,24 @@ qint64 QSocks5SocketEngine::write(const char *data, qint64 len) } #ifndef QT_NO_UDPSOCKET +bool QSocks5SocketEngine::joinMulticastGroup(const QHostAddress &, + const QHostAddress &, + const QNetworkInterface &) +{ + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Operation on socket is not supported")); + return false; +} + +bool QSocks5SocketEngine::leaveMulticastGroup(const QHostAddress &, + const QHostAddress &, + const QNetworkInterface &) +{ + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Operation on socket is not supported")); + return false; +} + qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QHostAddress *addr, quint16 *port) { diff --git a/src/network/socket/qsocks5socketengine_p.h b/src/network/socket/qsocks5socketengine_p.h index 3d35b57..fcfa4ff 100644 --- a/src/network/socket/qsocks5socketengine_p.h +++ b/src/network/socket/qsocks5socketengine_p.h @@ -92,6 +92,13 @@ public: qint64 write(const char *data, qint64 len); #ifndef QT_NO_UDPSOCKET + bool joinMulticastGroup(const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface); + bool leaveMulticastGroup(const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface); + qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, quint16 *port = 0); qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &addr, -- cgit v0.12 From 3c35b0607e541008daaf53221da4e727015b5441 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 5 May 2010 16:20:07 +0200 Subject: Implement multicast for the native socket engine on UNIX According to the Stevens book, the socket must be in the bound state to be able to join the group, so we check for the same condition. We support ASM and SSM (for IPv4). Windows implementation TBD. --- src/network/socket/qnativesocketengine.cpp | 28 ++++++ src/network/socket/qnativesocketengine_p.h | 13 +++ src/network/socket/qnativesocketengine_unix.cpp | 119 ++++++++++++++++++++++++ 3 files changed, 160 insertions(+) diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp index a169ca0..d4092b6 100644 --- a/src/network/socket/qnativesocketengine.cpp +++ b/src/network/socket/qnativesocketengine.cpp @@ -647,6 +647,34 @@ int QNativeSocketEngine::accept() } /*! + \since 4.8 +*/ +bool QNativeSocketEngine::joinMulticastGroup(const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface) +{ + Q_D(QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::joinMulticastGroup(), false); + Q_CHECK_STATE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::BoundState, false); + Q_CHECK_TYPE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::UdpSocket, false); + return d->nativeJoinMulticastGroup(groupAddress, sourceAddress, interface); +} + +/*! + \since 4.8 +*/ +bool QNativeSocketEngine::leaveMulticastGroup(const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface) +{ + Q_D(QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::leaveMulticastGroup(), false); + Q_CHECK_STATE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::BoundState, false); + Q_CHECK_TYPE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::UdpSocket, false); + return d->nativeLeaveMulticastGroup(groupAddress, sourceAddress, interface); +} + +/*! Returns the number of bytes that are currently available for reading. On error, -1 is returned. diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h index eca16f6..7ec1166 100644 --- a/src/network/socket/qnativesocketengine_p.h +++ b/src/network/socket/qnativesocketengine_p.h @@ -123,6 +123,13 @@ public: int accept(); void close(); + bool joinMulticastGroup(const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface); + bool leaveMulticastGroup(const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface); + qint64 bytesAvailable() const; qint64 read(char *data, qint64 maxlen); @@ -237,6 +244,12 @@ public: bool nativeBind(const QHostAddress &address, quint16 port); bool nativeListen(int backlog); int nativeAccept(); + bool nativeJoinMulticastGroup(const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface); + bool nativeLeaveMulticastGroup(const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface); qint64 nativeBytesAvailable() const; bool nativeHasPendingDatagrams() const; diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index a9f6921..f10c692 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -247,6 +247,9 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co case QNativeSocketEngine::KeepAliveOption: n = SO_KEEPALIVE; break; + case QNativeSocketEngine::MulticastLoopback: + n = IP_MULTICAST_LOOP; + break; } int v = -1; @@ -330,6 +333,9 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt case QNativeSocketEngine::KeepAliveOption: n = SO_KEEPALIVE; break; + case QNativeSocketEngine::MulticastLoopback: + n = IP_MULTICAST_LOOP; + break; } return ::setsockopt(socketDescriptor, level, n, (char *) &v, sizeof(v)) == 0; @@ -579,6 +585,119 @@ int QNativeSocketEnginePrivate::nativeAccept() return acceptedDescriptor; } +static bool doMulticast(QNativeSocketEnginePrivate *d, + int howAsm6, + int howAsm4, + int howSsm4, + const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface) +{ + int sockOpt = 0; + void *sockArg; + int sockArgSize; + + ip_mreq asm4; + ip_mreq_source ssm4; +#ifndef QT_NO_IPV6 + ipv6_mreq asm6; + + if (groupAddress.protocol() == QAbstractSocket::IPv6Protocol) { + sockOpt = howAsm6; + sockArg = &asm6; + sockArgSize = sizeof(asm6); + memset(&asm6, 0, sizeof(asm6)); + Q_IPV6ADDR ip6 = groupAddress.toIPv6Address(); + memcpy(&asm6.ipv6mr_multiaddr, &ip6, sizeof(ip6)); + Q_UNUSED(sourceAddress); // ### not possible to specify a single source when using IPv6? + asm6.ipv6mr_interface = interface.index(); + } else +#endif + if (groupAddress.protocol() == QAbstractSocket::IPv4Protocol) { + if (!sourceAddress.isNull()) { + sockOpt = howSsm4; + sockArg = &ssm4; + sockArgSize = sizeof(ssm4); + memset(&ssm4, 0, sizeof(ssm4)); + ssm4.imr_multiaddr.s_addr = htonl(groupAddress.toIPv4Address()); + ssm4.imr_sourceaddr.s_addr = htonl(sourceAddress.toIPv4Address()); + } else { + sockOpt = howAsm4; + sockArg = &asm4; + sockArgSize = sizeof(asm4); + memset(&asm4, 0, sizeof(asm4)); + asm4.imr_multiaddr.s_addr = htonl(groupAddress.toIPv4Address()); + } + + if (interface.isValid()) { + QList addressEntries = interface.addressEntries(); + if (!addressEntries.isEmpty()) { + QHostAddress firstIP = addressEntries.first().ip(); + asm4.imr_interface.s_addr = htonl(firstIP.toIPv4Address()); + } else { + d->setError(QAbstractSocket::NetworkError, + QNativeSocketEnginePrivate::NetworkUnreachableErrorString); + return false; + } + } else { + asm4.imr_interface.s_addr = INADDR_ANY; + } + } else { + // unreachable + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::ProtocolUnsupportedErrorString); + return false; + } + + int res = setsockopt(d->socketDescriptor, IPPROTO_IP, sockOpt, sockArg, sockArgSize); + if (res == -1) { + switch (errno) { + case ENOPROTOOPT: + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::OperationUnsupportedErrorString); + break; + default: + break; + } + return false; + } + return true; +} + +bool QNativeSocketEnginePrivate::nativeJoinMulticastGroup(const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface) +{ + return doMulticast(this, +#ifndef QT_NO_IPV6 + IPV6_JOIN_GROUP, +#else + 0, +#endif + IP_ADD_MEMBERSHIP, + IP_ADD_SOURCE_MEMBERSHIP, + groupAddress, + sourceAddress, + interface); +} + +bool QNativeSocketEnginePrivate::nativeLeaveMulticastGroup(const QHostAddress &groupAddress, + const QHostAddress &sourceAddress, + const QNetworkInterface &interface) +{ + return doMulticast(this, +#ifndef QT_NO_IPV6 + IPV6_LEAVE_GROUP, +#else + 0, +#endif + IP_DROP_MEMBERSHIP, + IP_DROP_SOURCE_MEMBERSHIP, + groupAddress, + sourceAddress, + interface); +} + qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const { int nbytes = 0; -- cgit v0.12 From 4bdcc1a6f5d11ffb461f174ae3c808ce2512700a Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 5 May 2010 16:20:07 +0200 Subject: add an autotest for basic multicast support --- tests/auto/qudpsocket/tst_qudpsocket.cpp | 38 ++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/tests/auto/qudpsocket/tst_qudpsocket.cpp b/tests/auto/qudpsocket/tst_qudpsocket.cpp index abed55c..fdfbb5d 100644 --- a/tests/auto/qudpsocket/tst_qudpsocket.cpp +++ b/tests/auto/qudpsocket/tst_qudpsocket.cpp @@ -94,6 +94,7 @@ private slots: void outOfProcessConnectedClientServerTest(); void outOfProcessUnconnectedClientServerTest(); void zeroLengthDatagram(); + void multicast(); protected slots: void empty_readyReadSlot(); @@ -844,5 +845,42 @@ void tst_QUdpSocket::zeroLengthDatagram() QCOMPARE(receiver.readDatagram(&buf, 1), qint64(0)); } +void tst_QUdpSocket::multicast() +{ + QFETCH_GLOBAL(bool, setProxy); + if (setProxy) + QSKIP("Multicast does not work with a proxy", SkipAll); + + QHostAddress groupAddress("239.255.116.98"); + + QUdpSocket receiver; + QVERIFY2(receiver.bind(), + qPrintable(receiver.errorString())); + QVERIFY2(receiver.joinMulticastGroup(groupAddress, QUdpSocket::MulticastLoopback), + qPrintable(receiver.errorString())); + + QList datagrams = QList() + << QByteArray("0123") + << QByteArray("4567") + << QByteArray("89ab") + << QByteArray("cdef"); + + QUdpSocket sender; + foreach (const QByteArray &datagram, datagrams) + sender.writeDatagram(datagram, receiver.localAddress(), receiver.localPort()); + + QVERIFY2(receiver.waitForReadyRead(), + qPrintable(receiver.errorString())); + QVERIFY(receiver.hasPendingDatagrams()); + QList receivedDatagrams; + while (receiver.hasPendingDatagrams()) { + QByteArray datagram; + datagram.resize(receiver.pendingDatagramSize()); + receiver.readDatagram(datagram.data(), datagram.size(), 0, 0); + receivedDatagrams << datagram; + } + QCOMPARE(receivedDatagrams, datagrams); +} + QTEST_MAIN(tst_QUdpSocket) #include "tst_qudpsocket.moc" -- cgit v0.12 From 7b2a41340bd1296a6712cd22ad7e8806f28c796c Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Wed, 5 May 2010 16:49:15 +0200 Subject: Compile on systems without IP_ADD_SOURCE_MEMBERSHIP support If a system does not support SSM (e.g. Mac OS X does not), then requesting a specific sender results in a socket error. --- src/network/socket/qnativesocketengine_unix.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index f10c692..512cd34 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -46,6 +46,7 @@ #include "qhostaddress.h" #include "qelapsedtimer.h" #include "qvarlengtharray.h" +#include "qnetworkinterface.h" #include #include #include @@ -585,6 +586,12 @@ int QNativeSocketEnginePrivate::nativeAccept() return acceptedDescriptor; } +#if !defined(IP_ADD_SOURCE_MEMBERSHIP) +# define QT_NO_MULTICAST_SSM +# define IP_ADD_SOURCE_MEMBERSHIP -1 +# define IP_DROP_SOURCE_MEMBERSHIP -1 +#endif + static bool doMulticast(QNativeSocketEnginePrivate *d, int howAsm6, int howAsm4, @@ -593,12 +600,16 @@ static bool doMulticast(QNativeSocketEnginePrivate *d, const QHostAddress &sourceAddress, const QNetworkInterface &interface) { + Q_UNUSED(howSsm4); + int sockOpt = 0; void *sockArg; int sockArgSize; ip_mreq asm4; +#ifndef QT_NO_MULTICAST_SSM ip_mreq_source ssm4; +#endif #ifndef QT_NO_IPV6 ipv6_mreq asm6; @@ -615,12 +626,19 @@ static bool doMulticast(QNativeSocketEnginePrivate *d, #endif if (groupAddress.protocol() == QAbstractSocket::IPv4Protocol) { if (!sourceAddress.isNull()) { +#ifndef QT_NO_MULTICAST_SSM sockOpt = howSsm4; sockArg = &ssm4; sockArgSize = sizeof(ssm4); memset(&ssm4, 0, sizeof(ssm4)); ssm4.imr_multiaddr.s_addr = htonl(groupAddress.toIPv4Address()); ssm4.imr_sourceaddr.s_addr = htonl(sourceAddress.toIPv4Address()); +#else + // unreachable + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::ProtocolUnsupportedErrorString); + return false; +#endif } else { sockOpt = howAsm4; sockArg = &asm4; -- cgit v0.12 From b3a93f3d5d7aaaa9b41eb1a5fc9082e5bec86a68 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 6 May 2010 12:59:41 +0200 Subject: Improved auto-test for multicast support in QUdpSocket In particular: - test that leaving the group "works" (returns true, still unsure if the test should verify that the socket no longer gets packets) - joining a group is not possible before binding - leaving a group without being bound is not possible --- tests/auto/qudpsocket/tst_qudpsocket.cpp | 73 +++++++++++++++++++++----------- 1 file changed, 49 insertions(+), 24 deletions(-) diff --git a/tests/auto/qudpsocket/tst_qudpsocket.cpp b/tests/auto/qudpsocket/tst_qudpsocket.cpp index fdfbb5d..39a7ab1 100644 --- a/tests/auto/qudpsocket/tst_qudpsocket.cpp +++ b/tests/auto/qudpsocket/tst_qudpsocket.cpp @@ -850,36 +850,61 @@ void tst_QUdpSocket::multicast() QFETCH_GLOBAL(bool, setProxy); if (setProxy) QSKIP("Multicast does not work with a proxy", SkipAll); - QHostAddress groupAddress("239.255.116.98"); - QUdpSocket receiver; - QVERIFY2(receiver.bind(), - qPrintable(receiver.errorString())); - QVERIFY2(receiver.joinMulticastGroup(groupAddress, QUdpSocket::MulticastLoopback), - qPrintable(receiver.errorString())); + { + QUdpSocket receiver; - QList datagrams = QList() - << QByteArray("0123") - << QByteArray("4567") - << QByteArray("89ab") - << QByteArray("cdef"); + // cannot join group before binding + QTest::ignoreMessage(QtWarningMsg, "QUdpSocket::joinMulticastGroup() called on a QUdpSocket when not in QUdpSocket::BoundState"); + QVERIFY(!receiver.joinMulticastGroup(groupAddress)); + } - QUdpSocket sender; - foreach (const QByteArray &datagram, datagrams) - sender.writeDatagram(datagram, receiver.localAddress(), receiver.localPort()); + { + QUdpSocket receiver; + + // bind first, then verify that we can join the multicast group + QVERIFY2(receiver.bind(), + qPrintable(receiver.errorString())); + QVERIFY2(receiver.joinMulticastGroup(groupAddress, QUdpSocket::MulticastLoopback), + qPrintable(receiver.errorString())); + + QList datagrams = QList() + << QByteArray("0123") + << QByteArray("4567") + << QByteArray("89ab") + << QByteArray("cdef"); + + QUdpSocket sender; + foreach (const QByteArray &datagram, datagrams) + sender.writeDatagram(datagram, receiver.localAddress(), receiver.localPort()); + + QVERIFY2(receiver.waitForReadyRead(), + qPrintable(receiver.errorString())); + QVERIFY(receiver.hasPendingDatagrams()); + QList receivedDatagrams; + while (receiver.hasPendingDatagrams()) { + QByteArray datagram; + datagram.resize(receiver.pendingDatagramSize()); + receiver.readDatagram(datagram.data(), datagram.size(), 0, 0); + receivedDatagrams << datagram; + } + QCOMPARE(receivedDatagrams, datagrams); - QVERIFY2(receiver.waitForReadyRead(), - qPrintable(receiver.errorString())); - QVERIFY(receiver.hasPendingDatagrams()); - QList receivedDatagrams; - while (receiver.hasPendingDatagrams()) { - QByteArray datagram; - datagram.resize(receiver.pendingDatagramSize()); - receiver.readDatagram(datagram.data(), datagram.size(), 0, 0); - receivedDatagrams << datagram; + QVERIFY2(receiver.leaveMulticastGroup(groupAddress), qPrintable(receiver.errorString())); + } + + { + QUdpSocket receiver; + + QVERIFY2(receiver.bind(), + qPrintable(receiver.errorString())); + QVERIFY2(receiver.joinMulticastGroup(groupAddress, QUdpSocket::MulticastLoopback), + qPrintable(receiver.errorString())); + receiver.close(); + QTest::ignoreMessage(QtWarningMsg, "QUdpSocket::leaveMulticastGroup() called on a QUdpSocket when not in QUdpSocket::BoundState"); + QVERIFY(!receiver.leaveMulticastGroup(groupAddress)); } - QCOMPARE(receivedDatagrams, datagrams); } QTEST_MAIN(tst_QUdpSocket) -- cgit v0.12 From 140cde65eb7eb8ccede25f1759699835ff479bc5 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 6 May 2010 13:01:15 +0200 Subject: Don't crash when trying to join/leave a group with not bound d->socketEngine will be zero, which crashes. avoid this by returning if the socket is not in the BoundState --- src/network/socket/qudpsocket.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp index 4fc88dc..880cb2b 100644 --- a/src/network/socket/qudpsocket.cpp +++ b/src/network/socket/qudpsocket.cpp @@ -347,6 +347,7 @@ bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress, MulticastMode mode) { Q_D(QUdpSocket); + QT_CHECK_BOUND("QUdpSocket::joinMulticastGroup()", false); d->socketEngine->setOption(QAbstractSocketEngine::MulticastLoopback, (mode & MulticastLoopback) ? 1 : 0); return d->socketEngine->joinMulticastGroup(groupAddress, sourceAddress, interface); @@ -368,6 +369,7 @@ bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress, const QHostAddress &sourceAddress, const QNetworkInterface &interface) { + QT_CHECK_BOUND("QUdpSocket::leaveMulticastGroup()", false); return d_func()->socketEngine->leaveMulticastGroup(groupAddress, sourceAddress, interface); } -- cgit v0.12 From ace9e3f77632f5ef47cccf32a204880ebf6a0581 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 6 May 2010 13:03:40 +0200 Subject: Set multicast errors correctly If the system does not support SSM, but the source address is specified, the error is unsupported socket operation, not unsupported protocol. if setsockopt() returns an error we don't know about, set the error code/string to unknown error --- src/network/socket/qnativesocketengine_unix.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index 512cd34..4be45b8 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -634,9 +634,9 @@ static bool doMulticast(QNativeSocketEnginePrivate *d, ssm4.imr_multiaddr.s_addr = htonl(groupAddress.toIPv4Address()); ssm4.imr_sourceaddr.s_addr = htonl(sourceAddress.toIPv4Address()); #else - // unreachable + // system doesn't support SSM d->setError(QAbstractSocket::UnsupportedSocketOperationError, - QNativeSocketEnginePrivate::ProtocolUnsupportedErrorString); + QNativeSocketEnginePrivate::OperationUnsupportedErrorString); return false; #endif } else { @@ -675,6 +675,8 @@ static bool doMulticast(QNativeSocketEnginePrivate *d, QNativeSocketEnginePrivate::OperationUnsupportedErrorString); break; default: + d->setError(QAbstractSocket::UnknownSocketError, + QNativeSocketEnginePrivate::UnknownSocketErrorString); break; } return false; -- cgit v0.12 From 44862c25999736155e904c36dbcd84bcd2dad63c Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 6 May 2010 13:11:16 +0200 Subject: Make ASM work on systems without SSM support QHostAddress::Null and QHostAddress::Any are not the same, and we use QHostAddress::Any to signify that we want any-source behavior in QUdpSocket::joinMulticastGroup(). Any means we should skip the single- support SSM, like Mac OS X). --- src/network/socket/qnativesocketengine_unix.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index 4be45b8..4fa4a55 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -625,7 +625,7 @@ static bool doMulticast(QNativeSocketEnginePrivate *d, } else #endif if (groupAddress.protocol() == QAbstractSocket::IPv4Protocol) { - if (!sourceAddress.isNull()) { + if (sourceAddress != QHostAddress::Any) { #ifndef QT_NO_MULTICAST_SSM sockOpt = howSsm4; sockArg = &ssm4; -- cgit v0.12 From 47d66c7855b9c315db29c82f4306de1864384ff7 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Fri, 7 May 2010 16:06:55 +0200 Subject: Remove support for single-source multi-cast It's not supported everywhere, so remove it for now until the need to re-add it arises. --- src/network/socket/qabstractsocketengine_p.h | 2 - src/network/socket/qhttpsocketengine.cpp | 2 - src/network/socket/qhttpsocketengine_p.h | 2 - src/network/socket/qnativesocketengine.cpp | 6 +-- src/network/socket/qnativesocketengine_p.h | 4 -- src/network/socket/qnativesocketengine_unix.cpp | 69 ++++++------------------- src/network/socket/qsocks5socketengine.cpp | 6 +-- src/network/socket/qsocks5socketengine_p.h | 2 - src/network/socket/qudpsocket.cpp | 10 ++-- src/network/socket/qudpsocket.h | 2 - 10 files changed, 25 insertions(+), 80 deletions(-) diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h index 9a2a7c3..0632891 100644 --- a/src/network/socket/qabstractsocketengine_p.h +++ b/src/network/socket/qabstractsocketengine_p.h @@ -121,10 +121,8 @@ public: #ifndef QT_NO_UDPSOCKET virtual bool joinMulticastGroup(const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface) = 0; virtual bool leaveMulticastGroup(const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface) = 0; virtual qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp index c7e5b95..e467df8 100644 --- a/src/network/socket/qhttpsocketengine.cpp +++ b/src/network/socket/qhttpsocketengine.cpp @@ -240,7 +240,6 @@ qint64 QHttpSocketEngine::write(const char *data, qint64 len) #ifndef QT_NO_UDPSOCKET bool QHttpSocketEngine::joinMulticastGroup(const QHostAddress &, - const QHostAddress &, const QNetworkInterface &) { setError(QAbstractSocket::UnsupportedSocketOperationError, @@ -249,7 +248,6 @@ bool QHttpSocketEngine::joinMulticastGroup(const QHostAddress &, } bool QHttpSocketEngine::leaveMulticastGroup(const QHostAddress &, - const QHostAddress &, const QNetworkInterface &) { setError(QAbstractSocket::UnsupportedSocketOperationError, diff --git a/src/network/socket/qhttpsocketengine_p.h b/src/network/socket/qhttpsocketengine_p.h index 18add3c..1a6b4f6 100644 --- a/src/network/socket/qhttpsocketengine_p.h +++ b/src/network/socket/qhttpsocketengine_p.h @@ -103,10 +103,8 @@ public: #ifndef QT_NO_UDPSOCKET bool joinMulticastGroup(const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface); bool leaveMulticastGroup(const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface); qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp index d4092b6..ae2cad2 100644 --- a/src/network/socket/qnativesocketengine.cpp +++ b/src/network/socket/qnativesocketengine.cpp @@ -650,28 +650,26 @@ int QNativeSocketEngine::accept() \since 4.8 */ bool QNativeSocketEngine::joinMulticastGroup(const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface) { Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::joinMulticastGroup(), false); Q_CHECK_STATE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::BoundState, false); Q_CHECK_TYPE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::UdpSocket, false); - return d->nativeJoinMulticastGroup(groupAddress, sourceAddress, interface); + return d->nativeJoinMulticastGroup(groupAddress, interface); } /*! \since 4.8 */ bool QNativeSocketEngine::leaveMulticastGroup(const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface) { Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::leaveMulticastGroup(), false); Q_CHECK_STATE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::BoundState, false); Q_CHECK_TYPE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::UdpSocket, false); - return d->nativeLeaveMulticastGroup(groupAddress, sourceAddress, interface); + return d->nativeLeaveMulticastGroup(groupAddress, interface); } /*! diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h index 7ec1166..e44aa2e 100644 --- a/src/network/socket/qnativesocketengine_p.h +++ b/src/network/socket/qnativesocketengine_p.h @@ -124,10 +124,8 @@ public: void close(); bool joinMulticastGroup(const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface); bool leaveMulticastGroup(const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface); qint64 bytesAvailable() const; @@ -245,10 +243,8 @@ public: bool nativeListen(int backlog); int nativeAccept(); bool nativeJoinMulticastGroup(const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface); bool nativeLeaveMulticastGroup(const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface); qint64 nativeBytesAvailable() const; diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index 4fa4a55..8416ed2 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -586,79 +586,50 @@ int QNativeSocketEnginePrivate::nativeAccept() return acceptedDescriptor; } -#if !defined(IP_ADD_SOURCE_MEMBERSHIP) -# define QT_NO_MULTICAST_SSM -# define IP_ADD_SOURCE_MEMBERSHIP -1 -# define IP_DROP_SOURCE_MEMBERSHIP -1 -#endif static bool doMulticast(QNativeSocketEnginePrivate *d, - int howAsm6, - int howAsm4, - int howSsm4, + int how6, + int how4, const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface) { - Q_UNUSED(howSsm4); - int sockOpt = 0; void *sockArg; int sockArgSize; - ip_mreq asm4; -#ifndef QT_NO_MULTICAST_SSM - ip_mreq_source ssm4; -#endif + ip_mreq mreq4; #ifndef QT_NO_IPV6 - ipv6_mreq asm6; + ipv6_mreq mreq6; if (groupAddress.protocol() == QAbstractSocket::IPv6Protocol) { - sockOpt = howAsm6; - sockArg = &asm6; - sockArgSize = sizeof(asm6); - memset(&asm6, 0, sizeof(asm6)); + sockOpt = how6; + sockArg = &mreq6; + sockArgSize = sizeof(mreq6); + memset(&mreq6, 0, sizeof(mreq6)); Q_IPV6ADDR ip6 = groupAddress.toIPv6Address(); - memcpy(&asm6.ipv6mr_multiaddr, &ip6, sizeof(ip6)); - Q_UNUSED(sourceAddress); // ### not possible to specify a single source when using IPv6? - asm6.ipv6mr_interface = interface.index(); + memcpy(&mreq6.ipv6mr_multiaddr, &ip6, sizeof(ip6)); + mreq6.ipv6mr_interface = interface.index(); } else #endif if (groupAddress.protocol() == QAbstractSocket::IPv4Protocol) { - if (sourceAddress != QHostAddress::Any) { -#ifndef QT_NO_MULTICAST_SSM - sockOpt = howSsm4; - sockArg = &ssm4; - sockArgSize = sizeof(ssm4); - memset(&ssm4, 0, sizeof(ssm4)); - ssm4.imr_multiaddr.s_addr = htonl(groupAddress.toIPv4Address()); - ssm4.imr_sourceaddr.s_addr = htonl(sourceAddress.toIPv4Address()); -#else - // system doesn't support SSM - d->setError(QAbstractSocket::UnsupportedSocketOperationError, - QNativeSocketEnginePrivate::OperationUnsupportedErrorString); - return false; -#endif - } else { - sockOpt = howAsm4; - sockArg = &asm4; - sockArgSize = sizeof(asm4); - memset(&asm4, 0, sizeof(asm4)); - asm4.imr_multiaddr.s_addr = htonl(groupAddress.toIPv4Address()); - } + sockOpt = how4; + sockArg = &mreq4; + sockArgSize = sizeof(mreq4); + memset(&mreq4, 0, sizeof(mreq4)); + mreq4.imr_multiaddr.s_addr = htonl(groupAddress.toIPv4Address()); if (interface.isValid()) { QList addressEntries = interface.addressEntries(); if (!addressEntries.isEmpty()) { QHostAddress firstIP = addressEntries.first().ip(); - asm4.imr_interface.s_addr = htonl(firstIP.toIPv4Address()); + mreq4.imr_interface.s_addr = htonl(firstIP.toIPv4Address()); } else { d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::NetworkUnreachableErrorString); return false; } } else { - asm4.imr_interface.s_addr = INADDR_ANY; + mreq4.imr_interface.s_addr = INADDR_ANY; } } else { // unreachable @@ -685,7 +656,6 @@ static bool doMulticast(QNativeSocketEnginePrivate *d, } bool QNativeSocketEnginePrivate::nativeJoinMulticastGroup(const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface) { return doMulticast(this, @@ -695,14 +665,11 @@ bool QNativeSocketEnginePrivate::nativeJoinMulticastGroup(const QHostAddress &gr 0, #endif IP_ADD_MEMBERSHIP, - IP_ADD_SOURCE_MEMBERSHIP, groupAddress, - sourceAddress, interface); } bool QNativeSocketEnginePrivate::nativeLeaveMulticastGroup(const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface) { return doMulticast(this, @@ -712,9 +679,7 @@ bool QNativeSocketEnginePrivate::nativeLeaveMulticastGroup(const QHostAddress &g 0, #endif IP_DROP_MEMBERSHIP, - IP_DROP_SOURCE_MEMBERSHIP, groupAddress, - sourceAddress, interface); } diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index 3da00ed..dcaf561 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -1545,8 +1545,7 @@ qint64 QSocks5SocketEngine::write(const char *data, qint64 len) #ifndef QT_NO_UDPSOCKET bool QSocks5SocketEngine::joinMulticastGroup(const QHostAddress &, - const QHostAddress &, - const QNetworkInterface &) + const QNetworkInterface &) { setError(QAbstractSocket::UnsupportedSocketOperationError, QLatin1String("Operation on socket is not supported")); @@ -1554,8 +1553,7 @@ bool QSocks5SocketEngine::joinMulticastGroup(const QHostAddress &, } bool QSocks5SocketEngine::leaveMulticastGroup(const QHostAddress &, - const QHostAddress &, - const QNetworkInterface &) + const QNetworkInterface &) { setError(QAbstractSocket::UnsupportedSocketOperationError, QLatin1String("Operation on socket is not supported")); diff --git a/src/network/socket/qsocks5socketengine_p.h b/src/network/socket/qsocks5socketengine_p.h index fcfa4ff..298595a 100644 --- a/src/network/socket/qsocks5socketengine_p.h +++ b/src/network/socket/qsocks5socketengine_p.h @@ -93,10 +93,8 @@ public: #ifndef QT_NO_UDPSOCKET bool joinMulticastGroup(const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface); bool leaveMulticastGroup(const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface); qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp index 880cb2b..84657b4 100644 --- a/src/network/socket/qudpsocket.cpp +++ b/src/network/socket/qudpsocket.cpp @@ -334,7 +334,7 @@ bool QUdpSocket::bind(quint16 port, BindMode mode) */ bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress, MulticastMode mode) { - return joinMulticastGroup(groupAddress, QHostAddress::Any, QNetworkInterface(), mode); + return joinMulticastGroup(groupAddress, QNetworkInterface(), mode); } /*! @@ -342,7 +342,6 @@ bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress, MulticastM \overload */ bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface, MulticastMode mode) { @@ -350,7 +349,7 @@ bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress, QT_CHECK_BOUND("QUdpSocket::joinMulticastGroup()", false); d->socketEngine->setOption(QAbstractSocketEngine::MulticastLoopback, (mode & MulticastLoopback) ? 1 : 0); - return d->socketEngine->joinMulticastGroup(groupAddress, sourceAddress, interface); + return d->socketEngine->joinMulticastGroup(groupAddress, interface); } /*! @@ -358,7 +357,7 @@ bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress, */ bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress) { - return leaveMulticastGroup(groupAddress, QHostAddress::Any, QNetworkInterface()); + return leaveMulticastGroup(groupAddress, QNetworkInterface()); } /*! @@ -366,11 +365,10 @@ bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress) \overload */ bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface) { QT_CHECK_BOUND("QUdpSocket::leaveMulticastGroup()", false); - return d_func()->socketEngine->leaveMulticastGroup(groupAddress, sourceAddress, interface); + return d_func()->socketEngine->leaveMulticastGroup(groupAddress, interface); } /*! diff --git a/src/network/socket/qudpsocket.h b/src/network/socket/qudpsocket.h index 8cb6dc3..ff80712 100644 --- a/src/network/socket/qudpsocket.h +++ b/src/network/socket/qudpsocket.h @@ -86,12 +86,10 @@ public: bool joinMulticastGroup(const QHostAddress &groupAddress, MulticastMode mode = DefaultMulticastFlagForPlatform); bool joinMulticastGroup(const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface, MulticastMode mode = DefaultMulticastFlagForPlatform); bool leaveMulticastGroup(const QHostAddress &groupAddress); bool leaveMulticastGroup(const QHostAddress &groupAddress, - const QHostAddress &sourceAddress, const QNetworkInterface &interface); bool hasPendingDatagrams() const; -- cgit v0.12 From b7fae53bf12d61274de7d5131ddc15d8f356df59 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Mon, 10 May 2010 13:22:56 +0200 Subject: Implement UDP multicast on Windows The code is very similar to the UNIX version, but not identical. It appears the Microsoft compiler does not like the "interface" variable name, so they have been renamed. The autotest had a bug in it that I failed to notice before. The sender was not sending to the multicast group, it was sending to the local address of the receiver (which fails on Windows). --- src/network/socket/qabstractsocketengine_p.h | 4 +- src/network/socket/qnativesocketengine.cpp | 8 +- src/network/socket/qnativesocketengine_p.h | 9 ++- src/network/socket/qnativesocketengine_win.cpp | 104 +++++++++++++++++++++++++ src/network/socket/qudpsocket.cpp | 8 +- src/network/socket/qudpsocket.h | 4 +- tests/auto/qudpsocket/tst_qudpsocket.cpp | 6 +- 7 files changed, 125 insertions(+), 18 deletions(-) diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h index 0632891..bfd9b05 100644 --- a/src/network/socket/qabstractsocketengine_p.h +++ b/src/network/socket/qabstractsocketengine_p.h @@ -121,9 +121,9 @@ public: #ifndef QT_NO_UDPSOCKET virtual bool joinMulticastGroup(const QHostAddress &groupAddress, - const QNetworkInterface &interface) = 0; + const QNetworkInterface &iface) = 0; virtual bool leaveMulticastGroup(const QHostAddress &groupAddress, - const QNetworkInterface &interface) = 0; + const QNetworkInterface &iface) = 0; virtual qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, quint16 *port = 0) = 0; diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp index ae2cad2..7cb3a1b 100644 --- a/src/network/socket/qnativesocketengine.cpp +++ b/src/network/socket/qnativesocketengine.cpp @@ -650,26 +650,26 @@ int QNativeSocketEngine::accept() \since 4.8 */ bool QNativeSocketEngine::joinMulticastGroup(const QHostAddress &groupAddress, - const QNetworkInterface &interface) + const QNetworkInterface &iface) { Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::joinMulticastGroup(), false); Q_CHECK_STATE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::BoundState, false); Q_CHECK_TYPE(QNativeSocketEngine::joinMulticastGroup(), QAbstractSocket::UdpSocket, false); - return d->nativeJoinMulticastGroup(groupAddress, interface); + return d->nativeJoinMulticastGroup(groupAddress, iface); } /*! \since 4.8 */ bool QNativeSocketEngine::leaveMulticastGroup(const QHostAddress &groupAddress, - const QNetworkInterface &interface) + const QNetworkInterface &iface) { Q_D(QNativeSocketEngine); Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::leaveMulticastGroup(), false); Q_CHECK_STATE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::BoundState, false); Q_CHECK_TYPE(QNativeSocketEngine::leaveMulticastGroup(), QAbstractSocket::UdpSocket, false); - return d->nativeLeaveMulticastGroup(groupAddress, interface); + return d->nativeLeaveMulticastGroup(groupAddress, iface); } /*! diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h index e44aa2e..ce00747 100644 --- a/src/network/socket/qnativesocketengine_p.h +++ b/src/network/socket/qnativesocketengine_p.h @@ -101,6 +101,7 @@ union qt_sockaddr { }; class QNativeSocketEnginePrivate; +class QNetworkInterface; class Q_AUTOTEST_EXPORT QNativeSocketEngine : public QAbstractSocketEngine { @@ -124,9 +125,9 @@ public: void close(); bool joinMulticastGroup(const QHostAddress &groupAddress, - const QNetworkInterface &interface); + const QNetworkInterface &iface); bool leaveMulticastGroup(const QHostAddress &groupAddress, - const QNetworkInterface &interface); + const QNetworkInterface &iface); qint64 bytesAvailable() const; @@ -243,9 +244,9 @@ public: bool nativeListen(int backlog); int nativeAccept(); bool nativeJoinMulticastGroup(const QHostAddress &groupAddress, - const QNetworkInterface &interface); + const QNetworkInterface &iface); bool nativeLeaveMulticastGroup(const QHostAddress &groupAddress, - const QNetworkInterface &interface); + const QNetworkInterface &iface); qint64 nativeBytesAvailable() const; bool nativeHasPendingDatagrams() const; diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 477ef45..d222fd1 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -40,6 +40,7 @@ ****************************************************************************/ #include +#include #include "qnativesocketengine_p.h" @@ -47,6 +48,7 @@ #include #include #include +#include //#define QNATIVESOCKETENGINE_DEBUG #if defined(QNATIVESOCKETENGINE_DEBUG) @@ -399,6 +401,13 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co case QNativeSocketEngine::KeepAliveOption: n = SO_KEEPALIVE; break; + case QNativeSocketEngine::MulticastLoopback: + { + unsigned long val = 0; + if (WSAIoctl(socketDescriptor, SIO_MULTIPOINT_LOOPBACK, 0, 0, &val, sizeof(val), 0, 0, 0) == 0) + return val; + return -1; + } } int v = -1; @@ -459,6 +468,14 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt case QNativeSocketEngine::KeepAliveOption: n = SO_KEEPALIVE; break; + case QNativeSocketEngine::MulticastLoopback: + { + unsigned long val = v, outval; + if (WSAIoctl(socketDescriptor, SIO_MULTIPOINT_LOOPBACK, &val, sizeof(val), &outval, sizeof(outval), 0, 0, 0) == 0) + return true; + WS_ERROR_DEBUG(WSAGetLastError()); + return false; + } } if (::setsockopt(socketDescriptor, level, n, (char*)&v, sizeof(v)) != 0) { @@ -747,6 +764,93 @@ int QNativeSocketEnginePrivate::nativeAccept() return acceptedDescriptor; } +static bool doMulticast(QNativeSocketEnginePrivate *d, + int how6, + int how4, + const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + int sockOpt = 0; + char *sockArg; + int sockArgSize; + + struct ip_mreq mreq4; +#ifndef QT_NO_IPV6 + struct ipv6_mreq mreq6; + + if (groupAddress.protocol() == QAbstractSocket::IPv6Protocol) { + sockOpt = how6; + sockArg = reinterpret_cast(&mreq6); + sockArgSize = sizeof(mreq6); + memset(&mreq6, 0, sizeof(mreq6)); + Q_IPV6ADDR ip6 = groupAddress.toIPv6Address(); + memcpy(&mreq6.ipv6mr_multiaddr, &ip6, sizeof(ip6)); + mreq6.ipv6mr_interface = iface.index(); + } else +#endif + if (groupAddress.protocol() == QAbstractSocket::IPv4Protocol) { + sockOpt = how4; + sockArg = reinterpret_cast(&mreq4); + sockArgSize = sizeof(mreq4); + memset(&mreq4, 0, sizeof(mreq4)); + mreq4.imr_multiaddr.s_addr = htonl(groupAddress.toIPv4Address()); + + if (iface.isValid()) { + QList addressEntries = iface.addressEntries(); + if (!addressEntries.isEmpty()) { + QHostAddress firstIP = addressEntries.first().ip(); + mreq4.imr_interface.s_addr = htonl(firstIP.toIPv4Address()); + } else { + d->setError(QAbstractSocket::NetworkError, + QNativeSocketEnginePrivate::NetworkUnreachableErrorString); + return false; + } + } else { + mreq4.imr_interface.s_addr = INADDR_ANY; + } + } else { + // unreachable + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::ProtocolUnsupportedErrorString); + return false; + } + + int res = setsockopt(d->socketDescriptor, IPPROTO_IP, sockOpt, sockArg, sockArgSize); + if (res == -1) { + d->setError(QAbstractSocket::UnsupportedSocketOperationError, + QNativeSocketEnginePrivate::OperationUnsupportedErrorString); + return false; + } + return true; +} + +bool QNativeSocketEnginePrivate::nativeJoinMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + return doMulticast(this, +#ifndef QT_NO_IPV6 + IPV6_JOIN_GROUP, +#else + 0, +#endif + IP_ADD_MEMBERSHIP, + groupAddress, + iface); +} + +bool QNativeSocketEnginePrivate::nativeLeaveMulticastGroup(const QHostAddress &groupAddress, + const QNetworkInterface &iface) +{ + return doMulticast(this, +#ifndef QT_NO_IPV6 + IPV6_LEAVE_GROUP, +#else + 0, +#endif + IP_DROP_MEMBERSHIP, + groupAddress, + iface); +} qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const { diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp index 84657b4..29345ca 100644 --- a/src/network/socket/qudpsocket.cpp +++ b/src/network/socket/qudpsocket.cpp @@ -342,14 +342,14 @@ bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress, MulticastM \overload */ bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress, - const QNetworkInterface &interface, + const QNetworkInterface &iface, MulticastMode mode) { Q_D(QUdpSocket); QT_CHECK_BOUND("QUdpSocket::joinMulticastGroup()", false); d->socketEngine->setOption(QAbstractSocketEngine::MulticastLoopback, (mode & MulticastLoopback) ? 1 : 0); - return d->socketEngine->joinMulticastGroup(groupAddress, interface); + return d->socketEngine->joinMulticastGroup(groupAddress, iface); } /*! @@ -365,10 +365,10 @@ bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress) \overload */ bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress, - const QNetworkInterface &interface) + const QNetworkInterface &iface) { QT_CHECK_BOUND("QUdpSocket::leaveMulticastGroup()", false); - return d_func()->socketEngine->leaveMulticastGroup(groupAddress, interface); + return d_func()->socketEngine->leaveMulticastGroup(groupAddress, iface); } /*! diff --git a/src/network/socket/qudpsocket.h b/src/network/socket/qudpsocket.h index ff80712..c8a9678 100644 --- a/src/network/socket/qudpsocket.h +++ b/src/network/socket/qudpsocket.h @@ -86,11 +86,11 @@ public: bool joinMulticastGroup(const QHostAddress &groupAddress, MulticastMode mode = DefaultMulticastFlagForPlatform); bool joinMulticastGroup(const QHostAddress &groupAddress, - const QNetworkInterface &interface, + const QNetworkInterface &iface, MulticastMode mode = DefaultMulticastFlagForPlatform); bool leaveMulticastGroup(const QHostAddress &groupAddress); bool leaveMulticastGroup(const QHostAddress &groupAddress, - const QNetworkInterface &interface); + const QNetworkInterface &iface); bool hasPendingDatagrams() const; qint64 pendingDatagramSize() const; diff --git a/tests/auto/qudpsocket/tst_qudpsocket.cpp b/tests/auto/qudpsocket/tst_qudpsocket.cpp index 39a7ab1..3c08cd7 100644 --- a/tests/auto/qudpsocket/tst_qudpsocket.cpp +++ b/tests/auto/qudpsocket/tst_qudpsocket.cpp @@ -876,8 +876,10 @@ void tst_QUdpSocket::multicast() << QByteArray("cdef"); QUdpSocket sender; - foreach (const QByteArray &datagram, datagrams) - sender.writeDatagram(datagram, receiver.localAddress(), receiver.localPort()); + foreach (const QByteArray &datagram, datagrams) { + QCOMPARE(int(sender.writeDatagram(datagram, groupAddress, receiver.localPort())), + int(datagram.size())); + } QVERIFY2(receiver.waitForReadyRead(), qPrintable(receiver.errorString())); -- cgit v0.12 From 41d8edda671e42c916f45e6eb2716fd76982a121 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 22 Jul 2010 14:26:45 +0200 Subject: Add MulticastTtlOption and MulticastLoopbackOption to QAbstractSocket These options only make sense and work in QUdpSocket when using multicasting. Previously, the loopback was set when joining a multicast group, but the loopback flag is not a per-group flag, it's per socket, hence this change. The ability to set the multicast TTL is needed as well, so this is added as a socket option as well. Autotest updated to test both. --- src/network/socket/qabstractsocket.cpp | 15 +++++++++++++++ src/network/socket/qabstractsocket.h | 4 +++- src/network/socket/qabstractsocketengine_p.h | 3 ++- src/network/socket/qnativesocketengine_unix.cpp | 14 ++++++++++++-- src/network/socket/qnativesocketengine_win.cpp | 19 +++++++++++++++++-- src/network/socket/qudpsocket.cpp | 9 +++------ src/network/socket/qudpsocket.h | 12 ++---------- tests/auto/qudpsocket/tst_qudpsocket.cpp | 16 ++++++++++++++-- 8 files changed, 68 insertions(+), 24 deletions(-) diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index 3218662..8037d91 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -1627,6 +1627,14 @@ void QAbstractSocket::setSocketOption(QAbstractSocket::SocketOption option, cons case KeepAliveOption: d_func()->socketEngine->setOption(QAbstractSocketEngine::KeepAliveOption, value.toInt()); break; + + case MulticastTtlOption: + d_func()->socketEngine->setOption(QAbstractSocketEngine::MulticastTtlOption, value.toInt()); + break; + + case MulticastLoopbackOption: + d_func()->socketEngine->setOption(QAbstractSocketEngine::MulticastLoopbackOption, value.toInt()); + break; } } @@ -1656,6 +1664,13 @@ QVariant QAbstractSocket::socketOption(QAbstractSocket::SocketOption option) case KeepAliveOption: ret = d_func()->socketEngine->option(QAbstractSocketEngine::KeepAliveOption); break; + + case MulticastTtlOption: + ret = d_func()->socketEngine->option(QAbstractSocketEngine::MulticastTtlOption); + break; + case MulticastLoopbackOption: + ret = d_func()->socketEngine->option(QAbstractSocketEngine::MulticastLoopbackOption); + break; } if (ret == -1) return QVariant(); diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h index 9e487d2..f610db3 100644 --- a/src/network/socket/qabstractsocket.h +++ b/src/network/socket/qabstractsocket.h @@ -118,7 +118,9 @@ public: }; enum SocketOption { LowDelayOption, // TCP_NODELAY - KeepAliveOption // SO_KEEPALIVE + KeepAliveOption, // SO_KEEPALIVE + MulticastTtlOption, // IP_MULTICAST_TTL + MulticastLoopbackOption // IP_MULTICAST_LOOPBACK }; QAbstractSocket(SocketType socketType, QObject *parent); diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h index bfd9b05..616fdb2 100644 --- a/src/network/socket/qabstractsocketengine_p.h +++ b/src/network/socket/qabstractsocketengine_p.h @@ -96,7 +96,8 @@ public: ReceiveOutOfBandData, LowDelayOption, KeepAliveOption, - MulticastLoopback + MulticastTtlOption, + MulticastLoopbackOption }; virtual bool initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol = QAbstractSocket::IPv4Protocol) = 0; diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index 8416ed2..f9061f8 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -248,7 +248,12 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co case QNativeSocketEngine::KeepAliveOption: n = SO_KEEPALIVE; break; - case QNativeSocketEngine::MulticastLoopback: + case QNativeSocketEngine::MulticastTtlOption: + level = IPPROTO_IP; + n = IP_MULTICAST_TTL; + break; + case QNativeSocketEngine::MulticastLoopbackOption: + level = IPPROTO_IP; n = IP_MULTICAST_LOOP; break; } @@ -334,7 +339,12 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt case QNativeSocketEngine::KeepAliveOption: n = SO_KEEPALIVE; break; - case QNativeSocketEngine::MulticastLoopback: + case QNativeSocketEngine::MulticastTtlOption: + level = IPPROTO_IP; + n = IP_MULTICAST_TTL; + break; + case QNativeSocketEngine::MulticastLoopbackOption: + level = IPPROTO_IP; n = IP_MULTICAST_LOOP; break; } diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index d222fd1..d2479f3 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -401,7 +401,14 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co case QNativeSocketEngine::KeepAliveOption: n = SO_KEEPALIVE; break; - case QNativeSocketEngine::MulticastLoopback: + case QNativeSocketEngine::MulticastTtlOption: + { + unsigned long val = 0; + if (WSAIoctl(socketDescriptor, SIO_MULTICAST_SCOPE, 0, 0, &val, sizeof(val), 0, 0 ,0) == 0) + return val; + return -1; + } + case QNativeSocketEngine::MulticastLoopbackOption: { unsigned long val = 0; if (WSAIoctl(socketDescriptor, SIO_MULTIPOINT_LOOPBACK, 0, 0, &val, sizeof(val), 0, 0, 0) == 0) @@ -468,7 +475,15 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt case QNativeSocketEngine::KeepAliveOption: n = SO_KEEPALIVE; break; - case QNativeSocketEngine::MulticastLoopback: + case QNativeSocketEngine::MulticastTtlOption: + { + unsigned long val = v, outval; + if (WSAIoctl(socketDescriptor, SIO_MULTICAST_SCOPE, &val, sizeof(val), &outval, sizeof(outval), 0, 0, 0) == 0) + return true; + WS_ERROR_DEBUG(WSAGetLastError()); + return false; + } + case QNativeSocketEngine::MulticastLoopbackOption: { unsigned long val = v, outval; if (WSAIoctl(socketDescriptor, SIO_MULTIPOINT_LOOPBACK, &val, sizeof(val), &outval, sizeof(outval), 0, 0, 0) == 0) diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp index 29345ca..6eb1335 100644 --- a/src/network/socket/qudpsocket.cpp +++ b/src/network/socket/qudpsocket.cpp @@ -332,9 +332,9 @@ bool QUdpSocket::bind(quint16 port, BindMode mode) /*! \since 4.6 */ -bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress, MulticastMode mode) +bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress) { - return joinMulticastGroup(groupAddress, QNetworkInterface(), mode); + return joinMulticastGroup(groupAddress, QNetworkInterface()); } /*! @@ -342,13 +342,10 @@ bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress, MulticastM \overload */ bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress, - const QNetworkInterface &iface, - MulticastMode mode) + const QNetworkInterface &iface) { Q_D(QUdpSocket); QT_CHECK_BOUND("QUdpSocket::joinMulticastGroup()", false); - d->socketEngine->setOption(QAbstractSocketEngine::MulticastLoopback, - (mode & MulticastLoopback) ? 1 : 0); return d->socketEngine->joinMulticastGroup(groupAddress, iface); } diff --git a/src/network/socket/qudpsocket.h b/src/network/socket/qudpsocket.h index c8a9678..54f82c9 100644 --- a/src/network/socket/qudpsocket.h +++ b/src/network/socket/qudpsocket.h @@ -68,12 +68,6 @@ public: }; Q_DECLARE_FLAGS(BindMode, BindFlag) - enum MulticastFlag { - DefaultMulticastFlagForPlatform = 0x0, - MulticastLoopback = 0x1 - }; - Q_DECLARE_FLAGS(MulticastMode, MulticastFlag) - explicit QUdpSocket(QObject *parent = 0); virtual ~QUdpSocket(); @@ -83,11 +77,9 @@ public: bool bind(quint16 port, BindMode mode); // ### Qt 5: Merge the bind functions + bool joinMulticastGroup(const QHostAddress &groupAddress); bool joinMulticastGroup(const QHostAddress &groupAddress, - MulticastMode mode = DefaultMulticastFlagForPlatform); - bool joinMulticastGroup(const QHostAddress &groupAddress, - const QNetworkInterface &iface, - MulticastMode mode = DefaultMulticastFlagForPlatform); + const QNetworkInterface &iface); bool leaveMulticastGroup(const QHostAddress &groupAddress); bool leaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface); diff --git a/tests/auto/qudpsocket/tst_qudpsocket.cpp b/tests/auto/qudpsocket/tst_qudpsocket.cpp index 3c08cd7..852986d 100644 --- a/tests/auto/qudpsocket/tst_qudpsocket.cpp +++ b/tests/auto/qudpsocket/tst_qudpsocket.cpp @@ -866,7 +866,15 @@ void tst_QUdpSocket::multicast() // bind first, then verify that we can join the multicast group QVERIFY2(receiver.bind(), qPrintable(receiver.errorString())); - QVERIFY2(receiver.joinMulticastGroup(groupAddress, QUdpSocket::MulticastLoopback), + receiver.setSocketOption(QUdpSocket::MulticastTtlOption, 2); + QCOMPARE(receiver.socketOption(QUdpSocket::MulticastTtlOption).toInt(), 2); + receiver.setSocketOption(QUdpSocket::MulticastTtlOption, 128); + QCOMPARE(receiver.socketOption(QUdpSocket::MulticastTtlOption).toInt(), 128); + receiver.setSocketOption(QUdpSocket::MulticastTtlOption, 0); + QCOMPARE(receiver.socketOption(QUdpSocket::MulticastTtlOption).toInt(), 0); + receiver.setSocketOption(QUdpSocket::MulticastLoopbackOption, 1); + QCOMPARE(receiver.socketOption(QUdpSocket::MulticastLoopbackOption).toInt(), 1); + QVERIFY2(receiver.joinMulticastGroup(groupAddress), qPrintable(receiver.errorString())); QList datagrams = QList() @@ -901,7 +909,11 @@ void tst_QUdpSocket::multicast() QVERIFY2(receiver.bind(), qPrintable(receiver.errorString())); - QVERIFY2(receiver.joinMulticastGroup(groupAddress, QUdpSocket::MulticastLoopback), + receiver.setSocketOption(QUdpSocket::MulticastTtlOption, 128); + QCOMPARE(receiver.socketOption(QUdpSocket::MulticastTtlOption).toInt(), 128); + receiver.setSocketOption(QUdpSocket::MulticastLoopbackOption, 0); + QCOMPARE(receiver.socketOption(QUdpSocket::MulticastLoopbackOption).toInt(), 0); + QVERIFY2(receiver.joinMulticastGroup(groupAddress), qPrintable(receiver.errorString())); receiver.close(); QTest::ignoreMessage(QtWarningMsg, "QUdpSocket::leaveMulticastGroup() called on a QUdpSocket when not in QUdpSocket::BoundState"); -- cgit v0.12 From 11cb9c94db6e7c4d494faa071368179e1b4b4b4d Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 22 Jul 2010 17:01:51 +0200 Subject: Don't use WSAIoctl() to get/set the multicast socket options According to the MSDN docs, we can just use setsockopt() and getsockopt() like we do on UNIX, even if we are using Winsock2. --- src/network/socket/qnativesocketengine_win.cpp | 38 ++++++++------------------ 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index d2479f3..5580abc 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -402,19 +402,13 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co n = SO_KEEPALIVE; break; case QNativeSocketEngine::MulticastTtlOption: - { - unsigned long val = 0; - if (WSAIoctl(socketDescriptor, SIO_MULTICAST_SCOPE, 0, 0, &val, sizeof(val), 0, 0 ,0) == 0) - return val; - return -1; - } + level = IPPROTO_IP; + n = IP_MULTICAST_TTL; + break; case QNativeSocketEngine::MulticastLoopbackOption: - { - unsigned long val = 0; - if (WSAIoctl(socketDescriptor, SIO_MULTIPOINT_LOOPBACK, 0, 0, &val, sizeof(val), 0, 0, 0) == 0) - return val; - return -1; - } + level = IPPROTO_IP; + n = IP_MULTICAST_LOOP; + break; } int v = -1; @@ -476,21 +470,13 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt n = SO_KEEPALIVE; break; case QNativeSocketEngine::MulticastTtlOption: - { - unsigned long val = v, outval; - if (WSAIoctl(socketDescriptor, SIO_MULTICAST_SCOPE, &val, sizeof(val), &outval, sizeof(outval), 0, 0, 0) == 0) - return true; - WS_ERROR_DEBUG(WSAGetLastError()); - return false; - } + level = IPPROTO_IP; + n = IP_MULTICAST_TTL; + break; case QNativeSocketEngine::MulticastLoopbackOption: - { - unsigned long val = v, outval; - if (WSAIoctl(socketDescriptor, SIO_MULTIPOINT_LOOPBACK, &val, sizeof(val), &outval, sizeof(outval), 0, 0, 0) == 0) - return true; - WS_ERROR_DEBUG(WSAGetLastError()); - return false; - } + level = IPPROTO_IP; + n = IP_MULTICAST_LOOP; + break; } if (::setsockopt(socketDescriptor, level, n, (char*)&v, sizeof(v)) != 0) { -- cgit v0.12 From 0920ee7d257dd644256187dceab629015398933d Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Mon, 26 Jul 2010 11:41:23 +0200 Subject: Rename doMulticast() to multicastMembershipHelper() This is a simple rename done for clarity. --- src/network/socket/qnativesocketengine_unix.cpp | 34 ++++++++++++------------- src/network/socket/qnativesocketengine_win.cpp | 34 ++++++++++++------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index f9061f8..737e35a 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -597,11 +597,11 @@ int QNativeSocketEnginePrivate::nativeAccept() } -static bool doMulticast(QNativeSocketEnginePrivate *d, - int how6, - int how4, - const QHostAddress &groupAddress, - const QNetworkInterface &interface) +static bool multicastMembershipHelper(QNativeSocketEnginePrivate *d, + int how6, + int how4, + const QHostAddress &groupAddress, + const QNetworkInterface &interface) { int sockOpt = 0; void *sockArg; @@ -668,29 +668,29 @@ static bool doMulticast(QNativeSocketEnginePrivate *d, bool QNativeSocketEnginePrivate::nativeJoinMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &interface) { - return doMulticast(this, + return multicastMembershipHelper(this, #ifndef QT_NO_IPV6 - IPV6_JOIN_GROUP, + IPV6_JOIN_GROUP, #else - 0, + 0, #endif - IP_ADD_MEMBERSHIP, - groupAddress, - interface); + IP_ADD_MEMBERSHIP, + groupAddress, + interface); } bool QNativeSocketEnginePrivate::nativeLeaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &interface) { - return doMulticast(this, + return multicastMembershipHelper(this, #ifndef QT_NO_IPV6 - IPV6_LEAVE_GROUP, + IPV6_LEAVE_GROUP, #else - 0, + 0, #endif - IP_DROP_MEMBERSHIP, - groupAddress, - interface); + IP_DROP_MEMBERSHIP, + groupAddress, + interface); } qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 5580abc..a922b45 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -765,11 +765,11 @@ int QNativeSocketEnginePrivate::nativeAccept() return acceptedDescriptor; } -static bool doMulticast(QNativeSocketEnginePrivate *d, - int how6, - int how4, - const QHostAddress &groupAddress, - const QNetworkInterface &iface) +static bool multicastMembershipHelper(QNativeSocketEnginePrivate *d, + int how6, + int how4, + const QHostAddress &groupAddress, + const QNetworkInterface &iface) { int sockOpt = 0; char *sockArg; @@ -828,29 +828,29 @@ static bool doMulticast(QNativeSocketEnginePrivate *d, bool QNativeSocketEnginePrivate::nativeJoinMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface) { - return doMulticast(this, + return multicastMembershipHelper(this, #ifndef QT_NO_IPV6 - IPV6_JOIN_GROUP, + IPV6_JOIN_GROUP, #else - 0, + 0, #endif - IP_ADD_MEMBERSHIP, - groupAddress, - iface); + IP_ADD_MEMBERSHIP, + groupAddress, + iface); } bool QNativeSocketEnginePrivate::nativeLeaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface) { - return doMulticast(this, + return multicastMembershipHelper(this, #ifndef QT_NO_IPV6 - IPV6_LEAVE_GROUP, + IPV6_LEAVE_GROUP, #else - 0, + 0, #endif - IP_DROP_MEMBERSHIP, - groupAddress, - iface); + IP_DROP_MEMBERSHIP, + groupAddress, + iface); } qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const -- cgit v0.12 From 9573b9d9410af07449c2b9179ec9fdcbdbc11518 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Mon, 26 Jul 2010 12:35:06 +0200 Subject: Initial documentation for multicast socket options and API --- src/network/socket/qabstractsocket.cpp | 4 ++++ src/network/socket/qudpsocket.cpp | 36 ++++++++++++++++++++++++++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index 8037d91..66cdffb 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -356,6 +356,10 @@ to enable. \value KeepAliveOption Set this to 1 to enable the SO_KEEPALIVE socket option + \value MulticastTtlOption Set this to an integer value to set IP_MULTICAST_TTL (TTL for multicast datagrams) socket option. + + \value MulticastLoopbackOption Set this to 1 to enable the IP_MULTICAST_LOOP (multicast loopback) socket option. + \sa QAbstractSocket::setSocketOption(), QAbstractSocket::socketOption() */ diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp index 6eb1335..33f8e32 100644 --- a/src/network/socket/qudpsocket.cpp +++ b/src/network/socket/qudpsocket.cpp @@ -330,7 +330,16 @@ bool QUdpSocket::bind(quint16 port, BindMode mode) } /*! - \since 4.6 + \since 4.8 + + Joins the the multicast group specified by \a groupAddress on the default + interface chosen by the operating system. The socket must be in BoundState, + otherwise an error occurs. + + This function returns true if successful; otherwise it returns false + and sets the socket error accordingly. + + \sa leaveMulticastGroup() */ bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress) { @@ -338,8 +347,13 @@ bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress) } /*! - \since 4.6 + \since 4.8 \overload + + Joins the multicast group address \a groupAddress on the interface \a + iface. + + \sa leaveMulticastGroup() */ bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface) @@ -350,7 +364,16 @@ bool QUdpSocket::joinMulticastGroup(const QHostAddress &groupAddress, } /*! - \since 4.6 + \since 4.8 + + Leaves the multicast group specified by \a groupAddress on the default + interface chosen by the operating system. The socket must be in BoundState, + otherwise an error occurs. + + This function returns true if successful; otherwise it returns false and + sets the socket error accordingly. + + \sa joinMulticastGroup() */ bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress) { @@ -358,8 +381,13 @@ bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress) } /*! - \since 4.6 + \since 4.8 \overload + + Leaves the multicast group specified by \a groupAddress on the interface \a + iface. + + \sa joinMulticastGroup() */ bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface) -- cgit v0.12 From afdf40b4c29812fac6ff400479286817bc43bad0 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Mon, 26 Jul 2010 12:48:37 +0200 Subject: Update QUdpSocket's overview docs to include multicast references The API is very small, joinMulticastGroup(), leaveMulticastGroup(), and the two QAbstractSocket socket options, MulticastTtlOption and MulticastLoopbackOption. --- src/network/socket/qudpsocket.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp index 33f8e32..1ff62cc 100644 --- a/src/network/socket/qudpsocket.cpp +++ b/src/network/socket/qudpsocket.cpp @@ -77,6 +77,12 @@ \snippet doc/src/snippets/code/src_network_socket_qudpsocket.cpp 0 + QUdpSocket also supports UDP multicast. Use the joinMulticastGroup() and + leaveMulticastGroup() to control group membership, and the + QAbstractSocket::MulticastTtlOption and + QAbstractSocket::MulticastLoopbackOption to set the TTL and loopback socket + options. + With QUdpSocket, you can also establish a virtual connection to a UDP server using connectToHost() and then use read() and write() to exchange datagrams without specifying the receiver for each -- cgit v0.12 From 4bec2216312c231b2082f0b462328b1270b00836 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Tue, 27 Jul 2010 09:46:09 +0200 Subject: Add multicastsender and multicastreceiver examples These are clones of the broadcastsender and broadcastreceiver examples, modified to use the multicast API. I've made sure that the examples are linked in the appropriate places as well. --- doc/src/examples/multicastreceiver.qdoc | 36 +++++++++ doc/src/examples/multicastsender.qdoc | 36 +++++++++ doc/src/getting-started/examples.qdoc | 2 + doc/src/network-programming/qtnetwork.qdoc | 9 ++- examples/network/multicastreceiver/main.cpp | 51 +++++++++++++ .../multicastreceiver/multicastreceiver.pro | 12 +++ examples/network/multicastreceiver/receiver.cpp | 84 ++++++++++++++++++++ examples/network/multicastreceiver/receiver.h | 70 +++++++++++++++++ examples/network/multicastsender/main.cpp | 51 +++++++++++++ .../network/multicastsender/multicastsender.pro | 12 +++ examples/network/multicastsender/sender.cpp | 89 ++++++++++++++++++++++ examples/network/multicastsender/sender.h | 77 +++++++++++++++++++ examples/network/network.pro | 4 +- src/network/socket/qudpsocket.cpp | 8 +- 14 files changed, 535 insertions(+), 6 deletions(-) create mode 100644 doc/src/examples/multicastreceiver.qdoc create mode 100644 doc/src/examples/multicastsender.qdoc create mode 100644 examples/network/multicastreceiver/main.cpp create mode 100644 examples/network/multicastreceiver/multicastreceiver.pro create mode 100644 examples/network/multicastreceiver/receiver.cpp create mode 100644 examples/network/multicastreceiver/receiver.h create mode 100644 examples/network/multicastsender/main.cpp create mode 100644 examples/network/multicastsender/multicastsender.pro create mode 100644 examples/network/multicastsender/sender.cpp create mode 100644 examples/network/multicastsender/sender.h diff --git a/doc/src/examples/multicastreceiver.qdoc b/doc/src/examples/multicastreceiver.qdoc new file mode 100644 index 0000000..f769705 --- /dev/null +++ b/doc/src/examples/multicastreceiver.qdoc @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in a +** written agreement between you and Nokia. +** +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of this +** file. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example network/multicastreceiver + \title Multicast Receiver Example + + The Multicast Receiever example shows how to receive information that is + sent to a multicast group. + + \image multicastreceiver-example.png +*/ diff --git a/doc/src/examples/multicastsender.qdoc b/doc/src/examples/multicastsender.qdoc new file mode 100644 index 0000000..271be60 --- /dev/null +++ b/doc/src/examples/multicastsender.qdoc @@ -0,0 +1,36 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:FDL$ +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Commercial License Agreement provided with the +** Software or, alternatively, in accordance with the terms contained in a +** written agreement between you and Nokia. +** +** GNU Free Documentation License +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of this +** file. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example network/multicastsender + \title Multicast Sender Example + + The Multicast Sender example shows how to send information to multiple + clients in a multicast group. + + \image multicastsender-example.png +*/ diff --git a/doc/src/getting-started/examples.qdoc b/doc/src/getting-started/examples.qdoc index e8c85e6..f849bf0 100644 --- a/doc/src/getting-started/examples.qdoc +++ b/doc/src/getting-started/examples.qdoc @@ -473,6 +473,8 @@ \o \l{network/bearercloud}{Bearer Cloud}\raisedaster \o \l{network/bearermonitor}{Bearer Monitor} \o \l{network/securesocketclient}{Secure Socket Client} + \o \l{network/multicastreceiver}{Multicast Receiver} + \o \l{network/multicastsender}{Multicast Sender} \endlist Examples marked with an asterisk (*) are fully documented. diff --git a/doc/src/network-programming/qtnetwork.qdoc b/doc/src/network-programming/qtnetwork.qdoc index dcee1b8..fce12a7 100644 --- a/doc/src/network-programming/qtnetwork.qdoc +++ b/doc/src/network-programming/qtnetwork.qdoc @@ -257,8 +257,13 @@ QUdpSocket::readDatagram() to read the datagram. The \l{network/broadcastsender}{Broadcast Sender} and - \l{network/broadcastreceiver}{Broadcast Receiver} examples show - how to write a UDP sender and a UDP receiver using Qt. + \l{network/broadcastreceiver}{Broadcast Receiver} examples show how to + write a UDP sender and a UDP receiver using Qt. + + QUdpSocket also supports multicasting. The + \l{network/multicastsender}{Multicast Sender} and + \l{network/multicastreceiver}{Multicast Receiver} examples show how to use + write UDP multicast clients. \section1 Resolving Host Names using QHostInfo diff --git a/examples/network/multicastreceiver/main.cpp b/examples/network/multicastreceiver/main.cpp new file mode 100644 index 0000000..8483271 --- /dev/null +++ b/examples/network/multicastreceiver/main.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "receiver.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + Receiver receiver; + receiver.show(); + return receiver.exec(); +} diff --git a/examples/network/multicastreceiver/multicastreceiver.pro b/examples/network/multicastreceiver/multicastreceiver.pro new file mode 100644 index 0000000..400c92e --- /dev/null +++ b/examples/network/multicastreceiver/multicastreceiver.pro @@ -0,0 +1,12 @@ +HEADERS = receiver.h +SOURCES = receiver.cpp \ + main.cpp +QT += network + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/network/multicastreceiver +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS multicastreceiver.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/network/multicastreceiver +INSTALLS += target sources + +symbian: include($$QT_SOURCE_TREE/examples/symbianpkgrules.pri) diff --git a/examples/network/multicastreceiver/receiver.cpp b/examples/network/multicastreceiver/receiver.cpp new file mode 100644 index 0000000..073fdce --- /dev/null +++ b/examples/network/multicastreceiver/receiver.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "receiver.h" + +Receiver::Receiver(QWidget *parent) + : QDialog(parent) +{ + groupAddress = QHostAddress("239.255.43.21"); + + statusLabel = new QLabel(tr("Listening for multicasted messages")); + quitButton = new QPushButton(tr("&Quit")); + + udpSocket = new QUdpSocket(this); + udpSocket->bind(45454, QUdpSocket::ShareAddress); + udpSocket->joinMulticastGroup(groupAddress); + + connect(udpSocket, SIGNAL(readyRead()), + this, SLOT(processPendingDatagrams())); + connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); + + QHBoxLayout *buttonLayout = new QHBoxLayout; + buttonLayout->addStretch(1); + buttonLayout->addWidget(quitButton); + buttonLayout->addStretch(1); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addWidget(statusLabel); + mainLayout->addLayout(buttonLayout); + setLayout(mainLayout); + + setWindowTitle(tr("Multicast Receiver")); +} + +void Receiver::processPendingDatagrams() +{ + while (udpSocket->hasPendingDatagrams()) { + QByteArray datagram; + datagram.resize(udpSocket->pendingDatagramSize()); + udpSocket->readDatagram(datagram.data(), datagram.size()); + statusLabel->setText(tr("Received datagram: \"%1\"") + .arg(datagram.data())); + } +} diff --git a/examples/network/multicastreceiver/receiver.h b/examples/network/multicastreceiver/receiver.h new file mode 100644 index 0000000..e226028 --- /dev/null +++ b/examples/network/multicastreceiver/receiver.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef RECEIVER_H +#define RECEIVER_H + +#include +#include + +QT_BEGIN_NAMESPACE +class QLabel; +class QPushButton; +class QUdpSocket; +QT_END_NAMESPACE + +class Receiver : public QDialog +{ + Q_OBJECT + +public: + Receiver(QWidget *parent = 0); + +private slots: + void processPendingDatagrams(); + +private: + QLabel *statusLabel; + QPushButton *quitButton; + QUdpSocket *udpSocket; + QHostAddress groupAddress; +}; + +#endif diff --git a/examples/network/multicastsender/main.cpp b/examples/network/multicastsender/main.cpp new file mode 100644 index 0000000..9309322 --- /dev/null +++ b/examples/network/multicastsender/main.cpp @@ -0,0 +1,51 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "sender.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + Sender sender; + sender.show(); + return sender.exec(); +} diff --git a/examples/network/multicastsender/multicastsender.pro b/examples/network/multicastsender/multicastsender.pro new file mode 100644 index 0000000..7543f00 --- /dev/null +++ b/examples/network/multicastsender/multicastsender.pro @@ -0,0 +1,12 @@ +HEADERS = sender.h +SOURCES = sender.cpp \ + main.cpp +QT += network + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/network/multicastsender +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS multicastsender.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/network/multicastsender +INSTALLS += target sources + +symbian: include($$QT_SOURCE_TREE/examples/symbianpkgrules.pri) diff --git a/examples/network/multicastsender/sender.cpp b/examples/network/multicastsender/sender.cpp new file mode 100644 index 0000000..38d532c --- /dev/null +++ b/examples/network/multicastsender/sender.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "sender.h" + +Sender::Sender(QWidget *parent) + : QDialog(parent) +{ + groupAddress = QHostAddress("239.255.43.21"); + + statusLabel = new QLabel(tr("Ready to send datagrams to multicast group %1 on port 45454").arg(groupAddress.toString())); + + startButton = new QPushButton(tr("&Start")); + quitButton = new QPushButton(tr("&Quit")); + + buttonBox = new QDialogButtonBox; + buttonBox->addButton(startButton, QDialogButtonBox::ActionRole); + buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole); + + timer = new QTimer(this); + udpSocket = new QUdpSocket(this); + messageNo = 1; + + connect(startButton, SIGNAL(clicked()), this, SLOT(startSending())); + connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); + connect(timer, SIGNAL(timeout()), this, SLOT(sendDatagram())); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addWidget(statusLabel); + mainLayout->addWidget(buttonBox); + setLayout(mainLayout); + + setWindowTitle(tr("Multicast Sender")); +} + +void Sender::startSending() +{ + startButton->setEnabled(false); + timer->start(1000); +} + +void Sender::sendDatagram() +{ + statusLabel->setText(tr("Now sending datagram %1").arg(messageNo)); + QByteArray datagram = "Multicast message " + QByteArray::number(messageNo); + udpSocket->writeDatagram(datagram.data(), datagram.size(), + groupAddress, 45454); + ++messageNo; +} diff --git a/examples/network/multicastsender/sender.h b/examples/network/multicastsender/sender.h new file mode 100644 index 0000000..176830b --- /dev/null +++ b/examples/network/multicastsender/sender.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor +** the names of its contributors may be used to endorse or promote +** products derived from this software without specific prior written +** permission. +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SENDER_H +#define SENDER_H + +#include +#include + +QT_BEGIN_NAMESPACE +class QDialogButtonBox; +class QLabel; +class QPushButton; +class QTimer; +class QUdpSocket; +QT_END_NAMESPACE + +class Sender : public QDialog +{ + Q_OBJECT + +public: + Sender(QWidget *parent = 0); + +private slots: + void startSending(); + void sendDatagram(); + +private: + QLabel *statusLabel; + QPushButton *startButton; + QPushButton *quitButton; + QDialogButtonBox *buttonBox; + QUdpSocket *udpSocket; + QTimer *timer; + QHostAddress groupAddress; + int messageNo; +}; + +#endif diff --git a/examples/network/network.pro b/examples/network/network.pro index 458561a..0012a97 100644 --- a/examples/network/network.pro +++ b/examples/network/network.pro @@ -16,7 +16,9 @@ SUBDIRS = \ threadedfortuneserver \ googlesuggest \ torrent \ - bearermonitor + bearermonitor \ + multicastreceiver \ + multicastsender contains(QT_CONFIG, svg) { SUBDIRS += bearercloud diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp index 1ff62cc..dffb5df 100644 --- a/src/network/socket/qudpsocket.cpp +++ b/src/network/socket/qudpsocket.cpp @@ -88,9 +88,11 @@ to exchange datagrams without specifying the receiver for each datagram. - The \l{network/broadcastsender}{Broadcast Sender} and - \l{network/broadcastreceiver}{Broadcast Receiver} examples - illustrate how to use QUdpSocket in applications. + The \l{network/broadcastsender}{Broadcast Sender}, + \l{network/broadcastreceiver}{Broadcast Receiver}, + \l{network/multicastsender}{Multicast Sender}, and + \l{network/multicastreceiver}{Multicast Receiver} examples illustrate how + to use QUdpSocket in applications. \section1 Symbian Platform Security Requirements -- cgit v0.12 From f64cb987bf1fa8776ee72139e96f4660f6f32d6a Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Tue, 27 Jul 2010 10:05:17 +0200 Subject: Add a spinbox to the multicastsender example to change the datagram's TTL --- examples/network/multicastsender/sender.cpp | 18 +++++++++++++++++- examples/network/multicastsender/sender.h | 4 ++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/examples/network/multicastsender/sender.cpp b/examples/network/multicastsender/sender.cpp index 38d532c..7fa9750 100644 --- a/examples/network/multicastsender/sender.cpp +++ b/examples/network/multicastsender/sender.cpp @@ -48,7 +48,15 @@ Sender::Sender(QWidget *parent) { groupAddress = QHostAddress("239.255.43.21"); - statusLabel = new QLabel(tr("Ready to send datagrams to multicast group %1 on port 45454").arg(groupAddress.toString())); + statusLabel = new QLabel(tr("Ready to multicast datagrams to group %1 on port 45454").arg(groupAddress.toString())); + + ttlLabel = new QLabel(tr("TTL for multicast datagrams:")); + ttlSpinBox = new QSpinBox; + ttlSpinBox->setRange(0, 255); + + QHBoxLayout *ttlLayout = new QHBoxLayout; + ttlLayout->addWidget(ttlLabel); + ttlLayout->addWidget(ttlSpinBox); startButton = new QPushButton(tr("&Start")); quitButton = new QPushButton(tr("&Quit")); @@ -61,16 +69,24 @@ Sender::Sender(QWidget *parent) udpSocket = new QUdpSocket(this); messageNo = 1; + connect(ttlSpinBox, SIGNAL(valueChanged(int)), this, SLOT(ttlChanged(int))); connect(startButton, SIGNAL(clicked()), this, SLOT(startSending())); connect(quitButton, SIGNAL(clicked()), this, SLOT(close())); connect(timer, SIGNAL(timeout()), this, SLOT(sendDatagram())); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(statusLabel); + mainLayout->addLayout(ttlLayout); mainLayout->addWidget(buttonBox); setLayout(mainLayout); setWindowTitle(tr("Multicast Sender")); + ttlSpinBox->setValue(1); +} + +void Sender::ttlChanged(int newTtl) +{ + udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption, newTtl); } void Sender::startSending() diff --git a/examples/network/multicastsender/sender.h b/examples/network/multicastsender/sender.h index 176830b..f119883 100644 --- a/examples/network/multicastsender/sender.h +++ b/examples/network/multicastsender/sender.h @@ -50,6 +50,7 @@ class QLabel; class QPushButton; class QTimer; class QUdpSocket; +class QSpinBox; QT_END_NAMESPACE class Sender : public QDialog @@ -60,11 +61,14 @@ public: Sender(QWidget *parent = 0); private slots: + void ttlChanged(int newTtl); void startSending(); void sendDatagram(); private: QLabel *statusLabel; + QLabel *ttlLabel; + QSpinBox *ttlSpinBox; QPushButton *startButton; QPushButton *quitButton; QDialogButtonBox *buttonBox; -- cgit v0.12 From 385948abcedcc4febd077f24e88018df87023158 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Tue, 27 Jul 2010 10:17:16 +0200 Subject: add images for multicastsender and multicastreceiver examples --- doc/src/images/multicastreceiver-example.png | Bin 0 -> 13042 bytes doc/src/images/multicastsender-example.png | Bin 0 -> 21092 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/src/images/multicastreceiver-example.png create mode 100644 doc/src/images/multicastsender-example.png diff --git a/doc/src/images/multicastreceiver-example.png b/doc/src/images/multicastreceiver-example.png new file mode 100644 index 0000000..8de11cd Binary files /dev/null and b/doc/src/images/multicastreceiver-example.png differ diff --git a/doc/src/images/multicastsender-example.png b/doc/src/images/multicastsender-example.png new file mode 100644 index 0000000..a619b04 Binary files /dev/null and b/doc/src/images/multicastsender-example.png differ -- cgit v0.12 From 8082ee277b44a158ce87d646e0d2c1d11e7d8348 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Fri, 30 Jul 2010 10:54:12 +0200 Subject: Improve multicast test Split out the individual socket option tests, and add more test data to these. Move the state checks into their own separate tests, and verify behavior of QUdpSocket multicast in the presence of a proxy (instead of just skipping the test). --- tests/auto/qudpsocket/tst_qudpsocket.cpp | 226 ++++++++++++++++++++++--------- 1 file changed, 164 insertions(+), 62 deletions(-) diff --git a/tests/auto/qudpsocket/tst_qudpsocket.cpp b/tests/auto/qudpsocket/tst_qudpsocket.cpp index 852986d..a1dd200 100644 --- a/tests/auto/qudpsocket/tst_qudpsocket.cpp +++ b/tests/auto/qudpsocket/tst_qudpsocket.cpp @@ -94,6 +94,15 @@ private slots: 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 multicast_data(); void multicast(); protected slots: @@ -845,80 +854,173 @@ void tst_QUdpSocket::zeroLengthDatagram() QCOMPARE(receiver.readDatagram(&buf, 1), qint64(0)); } -void tst_QUdpSocket::multicast() +void tst_QUdpSocket::multicastTtlOption_data() +{ + QTest::addColumn("ttl"); + QTest::addColumn("expected"); + QTest::newRow("0") << 0 << 0; + QTest::newRow("1") << 1 << 1; + QTest::newRow("2") << 2 << 2; + QTest::newRow("128") << 128 << 128; + QTest::newRow("255") << 255 << 255; + QTest::newRow("1024") << 1024 << 1; +} + +void tst_QUdpSocket::multicastTtlOption() { QFETCH_GLOBAL(bool, setProxy); - if (setProxy) - QSKIP("Multicast does not work with a proxy", SkipAll); - QHostAddress groupAddress("239.255.116.98"); + QFETCH(int, ttl); + QFETCH(int, expected); + if (setProxy) { + // UDP multicast does not work with proxies + expected = 0; + } - { - QUdpSocket receiver; + QUdpSocket udpSocket; + QVERIFY2(udpSocket.bind(), + qPrintable(udpSocket.errorString())); + udpSocket.setSocketOption(QUdpSocket::MulticastTtlOption, ttl); + QCOMPARE(udpSocket.socketOption(QUdpSocket::MulticastTtlOption).toInt(), expected); +} + +void tst_QUdpSocket::multicastLoopbackOption_data() +{ + QTest::addColumn("loopback"); + QTest::addColumn("expected"); + QTest::newRow("0") << 0 << 0; + QTest::newRow("1") << 1 << 1; + QTest::newRow("2") << 2 << 1; + QTest::newRow("0 again") << 0 << 0; + QTest::newRow("2 again") << 2 << 1; + QTest::newRow("0 last time") << 0 << 0; + QTest::newRow("1 again") << 1 << 1; +} - // cannot join group before binding - QTest::ignoreMessage(QtWarningMsg, "QUdpSocket::joinMulticastGroup() called on a QUdpSocket when not in QUdpSocket::BoundState"); - QVERIFY(!receiver.joinMulticastGroup(groupAddress)); +void tst_QUdpSocket::multicastLoopbackOption() +{ + QFETCH_GLOBAL(bool, setProxy); + QFETCH(int, loopback); + QFETCH(int, expected); + if (setProxy) { + // UDP multicast does not work with proxies + expected = 0; } - { - QUdpSocket receiver; - - // bind first, then verify that we can join the multicast group - QVERIFY2(receiver.bind(), - qPrintable(receiver.errorString())); - receiver.setSocketOption(QUdpSocket::MulticastTtlOption, 2); - QCOMPARE(receiver.socketOption(QUdpSocket::MulticastTtlOption).toInt(), 2); - receiver.setSocketOption(QUdpSocket::MulticastTtlOption, 128); - QCOMPARE(receiver.socketOption(QUdpSocket::MulticastTtlOption).toInt(), 128); - receiver.setSocketOption(QUdpSocket::MulticastTtlOption, 0); - QCOMPARE(receiver.socketOption(QUdpSocket::MulticastTtlOption).toInt(), 0); - receiver.setSocketOption(QUdpSocket::MulticastLoopbackOption, 1); - QCOMPARE(receiver.socketOption(QUdpSocket::MulticastLoopbackOption).toInt(), 1); - QVERIFY2(receiver.joinMulticastGroup(groupAddress), - qPrintable(receiver.errorString())); - - QList datagrams = QList() - << QByteArray("0123") - << QByteArray("4567") - << QByteArray("89ab") - << QByteArray("cdef"); - - QUdpSocket sender; - foreach (const QByteArray &datagram, datagrams) { - QCOMPARE(int(sender.writeDatagram(datagram, groupAddress, receiver.localPort())), - int(datagram.size())); - } + QUdpSocket udpSocket; + QVERIFY2(udpSocket.bind(), + qPrintable(udpSocket.errorString())); + udpSocket.setSocketOption(QUdpSocket::MulticastLoopbackOption, loopback); + QCOMPARE(udpSocket.socketOption(QUdpSocket::MulticastLoopbackOption).toInt(), expected); +} + +void tst_QUdpSocket::multicastJoinBeforeBind_data() +{ + QTest::addColumn("groupAddress"); + QTest::newRow("valid group address") << QHostAddress("239.255.118.62"); + QTest::newRow("invalid group address") << QHostAddress(QHostAddress::Broadcast); +} + +void tst_QUdpSocket::multicastJoinBeforeBind() +{ + QFETCH(QHostAddress, groupAddress); + + QUdpSocket udpSocket; + // 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("groupAddress"); + QTest::newRow("valid group address") << QHostAddress("239.255.118.62"); +} - QVERIFY2(receiver.waitForReadyRead(), - qPrintable(receiver.errorString())); - QVERIFY(receiver.hasPendingDatagrams()); - QList receivedDatagrams; - while (receiver.hasPendingDatagrams()) { - QByteArray datagram; - datagram.resize(receiver.pendingDatagramSize()); - receiver.readDatagram(datagram.data(), datagram.size(), 0, 0); - receivedDatagrams << datagram; +void tst_QUdpSocket::multicastLeaveAfterClose() +{ + QFETCH_GLOBAL(bool, setProxy); + QFETCH(QHostAddress, groupAddress); + if (setProxy) { + QSKIP("UDP Multicast does not work with proxies", SkipAll); + } + + QUdpSocket udpSocket; + QVERIFY2(udpSocket.bind(), + qPrintable(udpSocket.errorString())); + 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::multicast_data() +{ + QHostAddress anyAddress = QHostAddress(QHostAddress::Any); + QHostAddress groupAddress = QHostAddress("239.255.118.62"); + + QTest::addColumn("bindAddress"); + QTest::addColumn("bindResult"); + QTest::addColumn("groupAddress"); + QTest::addColumn("joinResult"); + QTest::newRow("valid bind, group address") << anyAddress << true << groupAddress << true; + QTest::newRow("same bind, group address") << groupAddress << true << groupAddress << 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 ((bindAddress.toIPv4Address() & 0xffff0000) == 0xefff0000) { + // proxy cannot bind to a multicast address + bindResult = false; } - QCOMPARE(receivedDatagrams, datagrams); + joinResult = false; + } + + QUdpSocket receiver; + + // bind first, then verify that we can join the multicast group + QVERIFY2(receiver.bind(bindAddress, 0) == bindResult, + qPrintable(receiver.errorString())); + if (!bindResult) + return; - QVERIFY2(receiver.leaveMulticastGroup(groupAddress), qPrintable(receiver.errorString())); + QVERIFY2(receiver.joinMulticastGroup(groupAddress) == joinResult, + qPrintable(receiver.errorString())); + if (!joinResult) + return; + + QList datagrams = QList() + << QByteArray("0123") + << QByteArray("4567") + << QByteArray("89ab") + << QByteArray("cdef"); + + QUdpSocket sender; + foreach (const QByteArray &datagram, datagrams) { + QCOMPARE(int(sender.writeDatagram(datagram, groupAddress, receiver.localPort())), + int(datagram.size())); } - { - QUdpSocket receiver; - - QVERIFY2(receiver.bind(), - qPrintable(receiver.errorString())); - receiver.setSocketOption(QUdpSocket::MulticastTtlOption, 128); - QCOMPARE(receiver.socketOption(QUdpSocket::MulticastTtlOption).toInt(), 128); - receiver.setSocketOption(QUdpSocket::MulticastLoopbackOption, 0); - QCOMPARE(receiver.socketOption(QUdpSocket::MulticastLoopbackOption).toInt(), 0); - QVERIFY2(receiver.joinMulticastGroup(groupAddress), - qPrintable(receiver.errorString())); - receiver.close(); - QTest::ignoreMessage(QtWarningMsg, "QUdpSocket::leaveMulticastGroup() called on a QUdpSocket when not in QUdpSocket::BoundState"); - QVERIFY(!receiver.leaveMulticastGroup(groupAddress)); + QVERIFY2(receiver.waitForReadyRead(), + qPrintable(receiver.errorString())); + QVERIFY(receiver.hasPendingDatagrams()); + QList receivedDatagrams; + while (receiver.hasPendingDatagrams()) { + QByteArray datagram; + datagram.resize(receiver.pendingDatagramSize()); + receiver.readDatagram(datagram.data(), datagram.size(), 0, 0); + receivedDatagrams << datagram; } + QCOMPARE(receivedDatagrams, datagrams); + + QVERIFY2(receiver.leaveMulticastGroup(groupAddress), qPrintable(receiver.errorString())); } QTEST_MAIN(tst_QUdpSocket) -- cgit v0.12 From 75a56ce44908eb14ef058d111129ef3d285c5364 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 5 Aug 2010 12:48:44 +0200 Subject: Add QUdpSocket::setMulticastInterface() and ::multicastInterface() This API allows the programmer to set/query the outgoing interface for multicast packets for the socket. Both functions need an initialized socket to work. Autotest updated to test setting each interface in the system as the multicast interface for IPv4 and IPv6 UDP sockets. --- src/network/socket/qabstractsocketengine_p.h | 2 + src/network/socket/qhttpsocketengine.cpp | 13 +++ src/network/socket/qhttpsocketengine_p.h | 2 + src/network/socket/qnativesocketengine.cpp | 20 +++++ src/network/socket/qnativesocketengine_p.h | 4 + src/network/socket/qnativesocketengine_unix.cpp | 111 ++++++++++++++++++++++-- src/network/socket/qnativesocketengine_win.cpp | 63 ++++++++++++++ src/network/socket/qsocks5socketengine.cpp | 14 +++ src/network/socket/qsocks5socketengine_p.h | 2 + src/network/socket/qudpsocket.cpp | 46 +++++++++- src/network/socket/qudpsocket.h | 3 + tests/auto/qudpsocket/tst_qudpsocket.cpp | 44 ++++++++++ 12 files changed, 313 insertions(+), 11 deletions(-) diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h index 616fdb2..ec5fbd3 100644 --- a/src/network/socket/qabstractsocketengine_p.h +++ b/src/network/socket/qabstractsocketengine_p.h @@ -125,6 +125,8 @@ public: const QNetworkInterface &iface) = 0; virtual bool leaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface) = 0; + virtual QNetworkInterface multicastInterface() const = 0; + virtual bool setMulticastInterface(const QNetworkInterface &iface) = 0; virtual qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, quint16 *port = 0) = 0; diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp index e467df8..cb0e296 100644 --- a/src/network/socket/qhttpsocketengine.cpp +++ b/src/network/socket/qhttpsocketengine.cpp @@ -45,6 +45,7 @@ #include "qurl.h" #include "qhttp.h" #include "qelapsedtimer.h" +#include "qnetworkinterface.h" #if !defined(QT_NO_NETWORKPROXY) && !defined(QT_NO_HTTP) #include @@ -255,6 +256,18 @@ bool QHttpSocketEngine::leaveMulticastGroup(const QHostAddress &, return false; } +QNetworkInterface QHttpSocketEngine::multicastInterface() const +{ + return QNetworkInterface(); +} + +bool QHttpSocketEngine::setMulticastInterface(const QNetworkInterface &) +{ + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Operation on socket is not supported")); + return false; +} + qint64 QHttpSocketEngine::readDatagram(char *, qint64, QHostAddress *, quint16 *) { diff --git a/src/network/socket/qhttpsocketengine_p.h b/src/network/socket/qhttpsocketengine_p.h index 1a6b4f6..b68b7117 100644 --- a/src/network/socket/qhttpsocketengine_p.h +++ b/src/network/socket/qhttpsocketengine_p.h @@ -106,6 +106,8 @@ public: const QNetworkInterface &interface); bool leaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &interface); + QNetworkInterface multicastInterface() const; + bool setMulticastInterface(const QNetworkInterface &iface); qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, quint16 *port = 0); diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp index 7cb3a1b..df73b9c 100644 --- a/src/network/socket/qnativesocketengine.cpp +++ b/src/network/socket/qnativesocketengine.cpp @@ -98,6 +98,7 @@ #include #include +#include #include "qnativesocketengine_p.h" #include @@ -672,6 +673,25 @@ bool QNativeSocketEngine::leaveMulticastGroup(const QHostAddress &groupAddress, return d->nativeLeaveMulticastGroup(groupAddress, iface); } +/*! \since 4.8 */ +QNetworkInterface QNativeSocketEngine::multicastInterface() const +{ + Q_D(const QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::multicastInterface(), QNetworkInterface()); + Q_CHECK_TYPE(QNativeSocketEngine::multicastInterface(), QAbstractSocket::UdpSocket, QNetworkInterface()); + return d->nativeMulticastInterface(); +} + + +/*! \since 4.8 */ +bool QNativeSocketEngine::setMulticastInterface(const QNetworkInterface &iface) +{ + Q_D(QNativeSocketEngine); + Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::setMulticastInterface(), false); + Q_CHECK_TYPE(QNativeSocketEngine::setMulticastInterface(), QAbstractSocket::UdpSocket, false); + return d->nativeSetMulticastInterface(iface); +} + /*! Returns the number of bytes that are currently available for reading. On error, -1 is returned. diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h index ce00747..9baacf0 100644 --- a/src/network/socket/qnativesocketengine_p.h +++ b/src/network/socket/qnativesocketengine_p.h @@ -128,6 +128,8 @@ public: const QNetworkInterface &iface); bool leaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface); + QNetworkInterface multicastInterface() const; + bool setMulticastInterface(const QNetworkInterface &iface); qint64 bytesAvailable() const; @@ -247,6 +249,8 @@ public: const QNetworkInterface &iface); bool nativeLeaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface); + QNetworkInterface nativeMulticastInterface() const; + bool nativeSetMulticastInterface(const QNetworkInterface &iface); qint64 nativeBytesAvailable() const; bool nativeHasPendingDatagrams() const; diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index 737e35a..e7f6c7e 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -249,12 +249,28 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co n = SO_KEEPALIVE; break; case QNativeSocketEngine::MulticastTtlOption: - level = IPPROTO_IP; - n = IP_MULTICAST_TTL; +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_HOPS; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_TTL; + } break; case QNativeSocketEngine::MulticastLoopbackOption: - level = IPPROTO_IP; - n = IP_MULTICAST_LOOP; +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_LOOP; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_LOOP; + } break; } @@ -340,12 +356,28 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt n = SO_KEEPALIVE; break; case QNativeSocketEngine::MulticastTtlOption: - level = IPPROTO_IP; - n = IP_MULTICAST_TTL; +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_HOPS; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_TTL; + } break; case QNativeSocketEngine::MulticastLoopbackOption: - level = IPPROTO_IP; - n = IP_MULTICAST_LOOP; +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_LOOP; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_LOOP; + } break; } @@ -693,6 +725,69 @@ bool QNativeSocketEnginePrivate::nativeLeaveMulticastGroup(const QHostAddress &g interface); } +QNetworkInterface QNativeSocketEnginePrivate::nativeMulticastInterface() const +{ +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + uint v; + QT_SOCKOPTLEN_T sizeofv = sizeof(v); + if (::getsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, &v, &sizeofv) == -1) + return QNetworkInterface(); + return QNetworkInterface::interfaceFromIndex(v); + } +#endif + + struct in_addr v = { 0 }; + QT_SOCKOPTLEN_T sizeofv = sizeof(v); + if (::getsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, &v, &sizeofv) == -1) + return QNetworkInterface(); + if (v.s_addr != 0 && sizeofv >= sizeof(v)) { + QHostAddress ipv4(ntohl(v.s_addr)); + QList ifaces = QNetworkInterface::allInterfaces(); + for (int i = 0; i < ifaces.count(); ++i) { + const QNetworkInterface &iface = ifaces.at(i); + if (!(iface.flags() & QNetworkInterface::CanMulticast)) + continue; + QList entries = iface.addressEntries(); + for (int j = 0; j < entries.count(); ++j) { + const QNetworkAddressEntry &entry = entries.at(j); + if (entry.ip() == ipv4) + return iface; + } + } + } + return QNetworkInterface(); +} + +bool QNativeSocketEnginePrivate::nativeSetMulticastInterface(const QNetworkInterface &iface) +{ +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + uint v = iface.isValid() ? iface.index() : 0; + return (::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, &v, sizeof(v)) != -1); + } +#endif + + struct in_addr v; + if (iface.isValid()) { + QList entries = iface.addressEntries(); + for (int i = 0; i < entries.count(); ++i) { + const QNetworkAddressEntry &entry = entries.at(i); + const QHostAddress &ip = entry.ip(); + if (ip.protocol() == QAbstractSocket::IPv4Protocol) { + v.s_addr = htonl(ip.toIPv4Address()); + int r = ::setsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, &v, sizeof(v)); + if (r != -1) + return true; + } + } + return false; + } + + v.s_addr = INADDR_ANY; + return (::setsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, &v, sizeof(v)) != -1); +} + qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const { int nbytes = 0; diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index a922b45..2ca1f41 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -853,6 +853,69 @@ bool QNativeSocketEnginePrivate::nativeLeaveMulticastGroup(const QHostAddress &g iface); } +QNetworkInterface QNativeSocketEnginePrivate::nativeMulticastInterface() const +{ +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + uint v; + QT_SOCKOPTLEN_T sizeofv = sizeof(v); + if (::getsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *) &v, &sizeofv) == -1) + return QNetworkInterface(); + return QNetworkInterface::interfaceFromIndex(v); + } +#endif + + struct in_addr v = { 0 }; + QT_SOCKOPTLEN_T sizeofv = sizeof(v); + if (::getsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, (char *) &v, &sizeofv) == -1) + return QNetworkInterface(); + if (v.s_addr != 0 && sizeofv >= sizeof(v)) { + QHostAddress ipv4(ntohl(v.s_addr)); + QList ifaces = QNetworkInterface::allInterfaces(); + for (int i = 0; i < ifaces.count(); ++i) { + const QNetworkInterface &iface = ifaces.at(i); + if (!(iface.flags() & QNetworkInterface::CanMulticast)) + continue; + QList entries = iface.addressEntries(); + for (int j = 0; j < entries.count(); ++j) { + const QNetworkAddressEntry &entry = entries.at(j); + if (entry.ip() == ipv4) + return iface; + } + } + } + return QNetworkInterface(); +} + +bool QNativeSocketEnginePrivate::nativeSetMulticastInterface(const QNetworkInterface &iface) +{ +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + uint v = iface.isValid() ? iface.index() : 0; + return (::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *) &v, sizeof(v)) != -1); + } +#endif + + struct in_addr v; + if (iface.isValid()) { + QList entries = iface.addressEntries(); + for (int i = 0; i < entries.count(); ++i) { + const QNetworkAddressEntry &entry = entries.at(i); + const QHostAddress &ip = entry.ip(); + if (ip.protocol() == QAbstractSocket::IPv4Protocol) { + v.s_addr = htonl(ip.toIPv4Address()); + int r = ::setsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, (char *) &v, sizeof(v)); + if (r != -1) + return true; + } + } + return false; + } + + v.s_addr = INADDR_ANY; + return (::setsockopt(socketDescriptor, IPPROTO_IP, IP_MULTICAST_IF, (char *) &v, sizeof(v)) != -1); +} + qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const { unsigned long nbytes = 0; diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index dcaf561..17cf5b1 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -56,6 +56,7 @@ #include "qurl.h" #include "qauthenticator.h" #include +#include QT_BEGIN_NAMESPACE @@ -1560,6 +1561,19 @@ bool QSocks5SocketEngine::leaveMulticastGroup(const QHostAddress &, return false; } + +QNetworkInterface QSocks5SocketEngine::multicastInterface() const +{ + return QNetworkInterface(); +} + +bool QSocks5SocketEngine::setMulticastInterface(const QNetworkInterface &) +{ + setError(QAbstractSocket::UnsupportedSocketOperationError, + QLatin1String("Operation on socket is not supported")); + return false; +} + qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QHostAddress *addr, quint16 *port) { diff --git a/src/network/socket/qsocks5socketengine_p.h b/src/network/socket/qsocks5socketengine_p.h index 298595a..06fcad2 100644 --- a/src/network/socket/qsocks5socketengine_p.h +++ b/src/network/socket/qsocks5socketengine_p.h @@ -96,6 +96,8 @@ public: const QNetworkInterface &interface); bool leaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &interface); + QNetworkInterface multicastInterface() const; + bool setMulticastInterface(const QNetworkInterface &iface); qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *addr = 0, quint16 *port = 0); diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp index dffb5df..5d8f4c0 100644 --- a/src/network/socket/qudpsocket.cpp +++ b/src/network/socket/qudpsocket.cpp @@ -77,11 +77,12 @@ \snippet doc/src/snippets/code/src_network_socket_qudpsocket.cpp 0 - QUdpSocket also supports UDP multicast. Use the joinMulticastGroup() and - leaveMulticastGroup() to control group membership, and the + QUdpSocket also supports UDP multicast. Use joinMulticastGroup() and + leaveMulticastGroup() to control group membership, and QAbstractSocket::MulticastTtlOption and QAbstractSocket::MulticastLoopbackOption to set the TTL and loopback socket - options. + options. Use setMulticastInterface() to control the outgoing interface for + multicast datagrams, and multicastInterface() to query it. With QUdpSocket, you can also establish a virtual connection to a UDP server using connectToHost() and then use read() and write() @@ -405,6 +406,45 @@ bool QUdpSocket::leaveMulticastGroup(const QHostAddress &groupAddress, } /*! + \since 4.8 + + Returns the interface for the outgoing interface for multicast datagrams. + This corresponds to the IP_MULTICAST_IF socket option for IPv4 sockets and + the IPV6_MULTICAST_IF socket option for IPv6 sockets. If no interface has + been previously set, this function returns an invalid QNetworkInterface. + The socket must be in BoundState, otherwise an invalid QNetworkInterface is + returned. + + \sa setMulticastInterface() +*/ +QNetworkInterface QUdpSocket::multicastInterface() const +{ + Q_D(const QUdpSocket); + QT_CHECK_BOUND("QUdpSocket::multicastInterface()", QNetworkInterface()); + return d->socketEngine->multicastInterface(); +} + +/*! + \since 4.8 + + Sets the outgoing interface for multicast datagrams to the interface \a + iface. This corresponds to the IP_MULTICAST_IF socket option for IPv4 + sockets and the IPV6_MULTICAST_IF socket option for IPv6 sockets. The + socket must be in BoundState, otherwise this function does nothing. + + \sa multicastInterface(), joinMulticastGroup(), leaveMulticastGroup() +*/ +void QUdpSocket::setMulticastInterface(const QNetworkInterface &iface) +{ + Q_D(QUdpSocket); + if (!isValid()) { + qWarning("QUdpSocket::setMulticastInterface() called on a QUdpSocket when not in QUdpSocket::BoundState"); + return; + } + d->socketEngine->setMulticastInterface(iface); +} + +/*! Returns true if at least one datagram is waiting to be read; otherwise returns false. diff --git a/src/network/socket/qudpsocket.h b/src/network/socket/qudpsocket.h index 54f82c9..b277d3f 100644 --- a/src/network/socket/qudpsocket.h +++ b/src/network/socket/qudpsocket.h @@ -84,6 +84,9 @@ public: bool leaveMulticastGroup(const QHostAddress &groupAddress, const QNetworkInterface &iface); + QNetworkInterface multicastInterface() const; + void setMulticastInterface(const QNetworkInterface &iface); + bool hasPendingDatagrams() const; qint64 pendingDatagramSize() const; qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *host = 0, quint16 *port = 0); diff --git a/tests/auto/qudpsocket/tst_qudpsocket.cpp b/tests/auto/qudpsocket/tst_qudpsocket.cpp index a1dd200..2b78706 100644 --- a/tests/auto/qudpsocket/tst_qudpsocket.cpp +++ b/tests/auto/qudpsocket/tst_qudpsocket.cpp @@ -50,11 +50,13 @@ #include #include #include +#include #include #include "../network-settings.h" Q_DECLARE_METATYPE(QHostAddress) +Q_DECLARE_METATYPE(QNetworkInterface) //TESTED_CLASS= //TESTED_FILES= @@ -102,6 +104,8 @@ private slots: void multicastJoinBeforeBind(); void multicastLeaveAfterClose_data(); void multicastLeaveAfterClose(); + void setMulticastInterface_data(); + void setMulticastInterface(); void multicast_data(); void multicast(); @@ -954,6 +958,46 @@ void tst_QUdpSocket::multicastLeaveAfterClose() QVERIFY(!udpSocket.leaveMulticastGroup(groupAddress)); } +void tst_QUdpSocket::setMulticastInterface_data() +{ + QTest::addColumn("iface"); + QTest::addColumn("address"); + QList 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() +{ + 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()); + } +} + void tst_QUdpSocket::multicast_data() { QHostAddress anyAddress = QHostAddress(QHostAddress::Any); -- cgit v0.12 From 615e39c255e39c4372602daf44ce8576c257c845 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Thu, 5 Aug 2010 15:10:33 +0200 Subject: Test IPv6 multicast Add test data to the multicast tests to bind to ipv6 addresses and to join an ipv6 group. Often, bind() results are unintersting (since we are only looking to initialize the socket) and proxies don't support IPv6 (so we have to expect failures in some cases). --- tests/auto/qudpsocket/tst_qudpsocket.cpp | 76 +++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/tests/auto/qudpsocket/tst_qudpsocket.cpp b/tests/auto/qudpsocket/tst_qudpsocket.cpp index 2b78706..7938afa 100644 --- a/tests/auto/qudpsocket/tst_qudpsocket.cpp +++ b/tests/auto/qudpsocket/tst_qudpsocket.cpp @@ -860,19 +860,28 @@ void tst_QUdpSocket::zeroLengthDatagram() void tst_QUdpSocket::multicastTtlOption_data() { + QTest::addColumn("bindAddress"); QTest::addColumn("ttl"); QTest::addColumn("expected"); - QTest::newRow("0") << 0 << 0; - QTest::newRow("1") << 1 << 1; - QTest::newRow("2") << 2 << 2; - QTest::newRow("128") << 128 << 128; - QTest::newRow("255") << 255 << 255; - QTest::newRow("1024") << 1024 << 1; + + QList addresses; + addresses += QHostAddress(QHostAddress::Any); + addresses += QHostAddress(QHostAddress::AnyIPv6); + + foreach (const QHostAddress &address, addresses) { + QTest::newRow("0") << address << 0 << 0; + QTest::newRow("1") << address << 1 << 1; + QTest::newRow("2") << address << 2 << 2; + QTest::newRow("128") << address << 128 << 128; + QTest::newRow("255") << address << 255 << 255; + QTest::newRow("1024") << address << 1024 << 1; + } } void tst_QUdpSocket::multicastTtlOption() { QFETCH_GLOBAL(bool, setProxy); + QFETCH(QHostAddress, bindAddress); QFETCH(int, ttl); QFETCH(int, expected); if (setProxy) { @@ -881,28 +890,37 @@ void tst_QUdpSocket::multicastTtlOption() } QUdpSocket udpSocket; - QVERIFY2(udpSocket.bind(), - qPrintable(udpSocket.errorString())); + // 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("bindAddress"); QTest::addColumn("loopback"); QTest::addColumn("expected"); - QTest::newRow("0") << 0 << 0; - QTest::newRow("1") << 1 << 1; - QTest::newRow("2") << 2 << 1; - QTest::newRow("0 again") << 0 << 0; - QTest::newRow("2 again") << 2 << 1; - QTest::newRow("0 last time") << 0 << 0; - QTest::newRow("1 again") << 1 << 1; + + QList addresses; + addresses += QHostAddress(QHostAddress::Any); + addresses += QHostAddress(QHostAddress::AnyIPv6); + + foreach (const QHostAddress &address, addresses) { + QTest::newRow("0") << address << 0 << 0; + QTest::newRow("1") << address << 1 << 1; + QTest::newRow("2") << address << 2 << 1; + QTest::newRow("0 again") << address << 0 << 0; + QTest::newRow("2 again") << address << 2 << 1; + QTest::newRow("0 last time") << address << 0 << 0; + QTest::newRow("1 again") << address << 1 << 1; + } } void tst_QUdpSocket::multicastLoopbackOption() { QFETCH_GLOBAL(bool, setProxy); + QFETCH(QHostAddress, bindAddress); QFETCH(int, loopback); QFETCH(int, expected); if (setProxy) { @@ -911,8 +929,8 @@ void tst_QUdpSocket::multicastLoopbackOption() } QUdpSocket udpSocket; - QVERIFY2(udpSocket.bind(), - qPrintable(udpSocket.errorString())); + // 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); } @@ -920,8 +938,10 @@ void tst_QUdpSocket::multicastLoopbackOption() void tst_QUdpSocket::multicastJoinBeforeBind_data() { QTest::addColumn("groupAddress"); - QTest::newRow("valid group address") << QHostAddress("239.255.118.62"); - QTest::newRow("invalid group address") << QHostAddress(QHostAddress::Broadcast); + 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() @@ -937,7 +957,8 @@ void tst_QUdpSocket::multicastJoinBeforeBind() void tst_QUdpSocket::multicastLeaveAfterClose_data() { QTest::addColumn("groupAddress"); - QTest::newRow("valid group address") << QHostAddress("239.255.118.62"); + QTest::newRow("valid ipv4 group address") << QHostAddress("239.255.118.62"); + QTest::newRow("valid ipv6 group address") << QHostAddress("FF01::114"); } void tst_QUdpSocket::multicastLeaveAfterClose() @@ -949,7 +970,7 @@ void tst_QUdpSocket::multicastLeaveAfterClose() } QUdpSocket udpSocket; - QVERIFY2(udpSocket.bind(), + QVERIFY2(udpSocket.bind(groupAddress, 0), qPrintable(udpSocket.errorString())); QVERIFY2(udpSocket.joinMulticastGroup(groupAddress), qPrintable(udpSocket.errorString())); @@ -1002,13 +1023,17 @@ void tst_QUdpSocket::multicast_data() { QHostAddress anyAddress = QHostAddress(QHostAddress::Any); QHostAddress groupAddress = QHostAddress("239.255.118.62"); + QHostAddress any6Address = QHostAddress(QHostAddress::AnyIPv6); + QHostAddress group6Address = QHostAddress("FF01::114"); QTest::addColumn("bindAddress"); QTest::addColumn("bindResult"); QTest::addColumn("groupAddress"); QTest::addColumn("joinResult"); - QTest::newRow("valid bind, group address") << anyAddress << true << groupAddress << true; - QTest::newRow("same bind, group address") << groupAddress << true << groupAddress << true; + QTest::newRow("valid bind, group ipv4 address") << anyAddress << true << groupAddress << true; + QTest::newRow("same bind, group ipv4 address") << groupAddress << true << groupAddress << true; + QTest::newRow("valid bind, group ipv6 address") << any6Address << true << group6Address << true; + QTest::newRow("same bind, group ipv6 address") << group6Address << true << group6Address << true; } void tst_QUdpSocket::multicast() @@ -1020,8 +1045,9 @@ void tst_QUdpSocket::multicast() QFETCH(bool, joinResult); if (setProxy) { // UDP multicast does not work with proxies - if ((bindAddress.toIPv4Address() & 0xffff0000) == 0xefff0000) { - // proxy cannot bind to a multicast address + if ((bindAddress.protocol() == QAbstractSocket::IPv4Protocol && (bindAddress.toIPv4Address() & 0xffff0000) == 0xefff0000) + || bindAddress.protocol() == QAbstractSocket::IPv6Protocol) { + // proxy cannot bind to IPv6 or multicast addresses bindResult = false; } joinResult = false; -- cgit v0.12 From a06052ce8212f3ab35e9b89573b92322a3978bc6 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Fri, 6 Aug 2010 08:16:02 +0200 Subject: Fix IPv6 multicast on UNIX The setsockopt() to join/leave a group should change level depending on the type of socket/address being used (and not always use IPPROTO_IP). --- src/network/socket/qnativesocketengine_unix.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index e7f6c7e..360dab5 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -635,6 +635,7 @@ static bool multicastMembershipHelper(QNativeSocketEnginePrivate *d, const QHostAddress &groupAddress, const QNetworkInterface &interface) { + int level = 0; int sockOpt = 0; void *sockArg; int sockArgSize; @@ -644,7 +645,8 @@ static bool multicastMembershipHelper(QNativeSocketEnginePrivate *d, ipv6_mreq mreq6; if (groupAddress.protocol() == QAbstractSocket::IPv6Protocol) { - sockOpt = how6; + level = IPPROTO_IPV6; + sockOpt = how6; sockArg = &mreq6; sockArgSize = sizeof(mreq6); memset(&mreq6, 0, sizeof(mreq6)); @@ -654,6 +656,7 @@ static bool multicastMembershipHelper(QNativeSocketEnginePrivate *d, } else #endif if (groupAddress.protocol() == QAbstractSocket::IPv4Protocol) { + level = IPPROTO_IP; sockOpt = how4; sockArg = &mreq4; sockArgSize = sizeof(mreq4); @@ -680,13 +683,17 @@ static bool multicastMembershipHelper(QNativeSocketEnginePrivate *d, return false; } - int res = setsockopt(d->socketDescriptor, IPPROTO_IP, sockOpt, sockArg, sockArgSize); + int res = setsockopt(d->socketDescriptor, level, sockOpt, sockArg, sockArgSize); if (res == -1) { switch (errno) { case ENOPROTOOPT: d->setError(QAbstractSocket::UnsupportedSocketOperationError, QNativeSocketEnginePrivate::OperationUnsupportedErrorString); break; + case EADDRNOTAVAIL: + d->setError(QAbstractSocket::SocketAddressNotAvailableError, + QNativeSocketEnginePrivate::AddressNotAvailableErrorString); + break; default: d->setError(QAbstractSocket::UnknownSocketError, QNativeSocketEnginePrivate::UnknownSocketErrorString); @@ -763,7 +770,7 @@ bool QNativeSocketEnginePrivate::nativeSetMulticastInterface(const QNetworkInter { #ifndef QT_NO_IPV6 if (socketProtocol == QAbstractSocket::IPv6Protocol) { - uint v = iface.isValid() ? iface.index() : 0; + uint v = iface.index(); return (::setsockopt(socketDescriptor, IPPROTO_IPV6, IPV6_MULTICAST_IF, &v, sizeof(v)) != -1); } #endif -- cgit v0.12 From a6cb69085975352f10dcf8e6c93c6470ac9c459e Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Fri, 6 Aug 2010 09:36:33 +0200 Subject: test that joining invalid group fails, make test data names more verbose when testing the ttl and loopback socket options, include the bind address as part of the test data name --- tests/auto/qudpsocket/tst_qudpsocket.cpp | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/tests/auto/qudpsocket/tst_qudpsocket.cpp b/tests/auto/qudpsocket/tst_qudpsocket.cpp index 7938afa..32fab6b 100644 --- a/tests/auto/qudpsocket/tst_qudpsocket.cpp +++ b/tests/auto/qudpsocket/tst_qudpsocket.cpp @@ -869,12 +869,12 @@ void tst_QUdpSocket::multicastTtlOption_data() addresses += QHostAddress(QHostAddress::AnyIPv6); foreach (const QHostAddress &address, addresses) { - QTest::newRow("0") << address << 0 << 0; - QTest::newRow("1") << address << 1 << 1; - QTest::newRow("2") << address << 2 << 2; - QTest::newRow("128") << address << 128 << 128; - QTest::newRow("255") << address << 255 << 255; - QTest::newRow("1024") << address << 1024 << 1; + 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; } } @@ -907,13 +907,13 @@ void tst_QUdpSocket::multicastLoopbackOption_data() addresses += QHostAddress(QHostAddress::AnyIPv6); foreach (const QHostAddress &address, addresses) { - QTest::newRow("0") << address << 0 << 0; - QTest::newRow("1") << address << 1 << 1; - QTest::newRow("2") << address << 2 << 1; - QTest::newRow("0 again") << address << 0 << 0; - QTest::newRow("2 again") << address << 2 << 1; - QTest::newRow("0 last time") << address << 0 << 0; - QTest::newRow("1 again") << address << 1 << 1; + 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; } } @@ -1031,8 +1031,10 @@ void tst_QUdpSocket::multicast_data() QTest::addColumn("groupAddress"); QTest::addColumn("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; } -- cgit v0.12 From 44368b4174eedc9a2eebd9f67cf0e96f42b67188 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Fri, 6 Aug 2010 09:45:34 +0200 Subject: Fix IPv6 support on Windows We need to make sure that we use the right setsockopt level and option for the multicast TTL and loopback options. Like on UNIX, we need to use IPPROTO_IPV6 level when joining and leaving a group. Also, we cannot bind to a multicast address on Windows (but it is possible on UNIX). work around this by binding to the Any/AnyIPv6 addresses if a multicast address is passed to bind. --- src/network/socket/qnativesocketengine_win.cpp | 73 ++++++++++++++++++++++---- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index 2ca1f41..f952cee 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -402,12 +402,28 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co n = SO_KEEPALIVE; break; case QNativeSocketEngine::MulticastTtlOption: - level = IPPROTO_IP; - n = IP_MULTICAST_TTL; +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_HOPS; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_TTL; + } break; case QNativeSocketEngine::MulticastLoopbackOption: - level = IPPROTO_IP; - n = IP_MULTICAST_LOOP; +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_LOOP; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_LOOP; + } break; } @@ -470,12 +486,28 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt n = SO_KEEPALIVE; break; case QNativeSocketEngine::MulticastTtlOption: - level = IPPROTO_IP; - n = IP_MULTICAST_TTL; +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_HOPS; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_TTL; + } break; case QNativeSocketEngine::MulticastLoopbackOption: - level = IPPROTO_IP; - n = IP_MULTICAST_LOOP; +#ifndef QT_NO_IPV6 + if (socketProtocol == QAbstractSocket::IPv6Protocol) { + level = IPPROTO_IPV6; + n = IPV6_MULTICAST_LOOP; + } else +#endif + { + level = IPPROTO_IP; + n = IP_MULTICAST_LOOP; + } break; } @@ -666,8 +698,24 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin } -bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &address, quint16 port) +bool QNativeSocketEnginePrivate::nativeBind(const QHostAddress &a, quint16 port) { + QHostAddress address = a; + switch (address.protocol()) { + case QAbstractSocket::IPv6Protocol: + if (address.toIPv6Address()[0] == 0xff) { + // binding to a multicast address + address = QHostAddress(QHostAddress::AnyIPv6); + } + break; + case QAbstractSocket::IPv4Protocol: + if ((address.toIPv4Address() & 0xffff0000) == 0xefff0000) { + // binding to a multicast address + address = QHostAddress(QHostAddress::Any); + } + break; + } + struct sockaddr_in sockAddrIPv4; qt_sockaddr_in6 sockAddrIPv6; struct sockaddr *sockAddrPtr = 0; @@ -771,6 +819,7 @@ static bool multicastMembershipHelper(QNativeSocketEnginePrivate *d, const QHostAddress &groupAddress, const QNetworkInterface &iface) { + int level = 0; int sockOpt = 0; char *sockArg; int sockArgSize; @@ -780,7 +829,8 @@ static bool multicastMembershipHelper(QNativeSocketEnginePrivate *d, struct ipv6_mreq mreq6; if (groupAddress.protocol() == QAbstractSocket::IPv6Protocol) { - sockOpt = how6; + level = IPPROTO_IPV6; + sockOpt = how6; sockArg = reinterpret_cast(&mreq6); sockArgSize = sizeof(mreq6); memset(&mreq6, 0, sizeof(mreq6)); @@ -790,6 +840,7 @@ static bool multicastMembershipHelper(QNativeSocketEnginePrivate *d, } else #endif if (groupAddress.protocol() == QAbstractSocket::IPv4Protocol) { + level = IPPROTO_IP; sockOpt = how4; sockArg = reinterpret_cast(&mreq4); sockArgSize = sizeof(mreq4); @@ -816,7 +867,7 @@ static bool multicastMembershipHelper(QNativeSocketEnginePrivate *d, return false; } - int res = setsockopt(d->socketDescriptor, IPPROTO_IP, sockOpt, sockArg, sockArgSize); + int res = setsockopt(d->socketDescriptor, level, sockOpt, sockArg, sockArgSize); if (res == -1) { d->setError(QAbstractSocket::UnsupportedSocketOperationError, QNativeSocketEnginePrivate::OperationUnsupportedErrorString); -- cgit v0.12 From 6e28216b0b64ca69b26d4a3dc356fd36800e4894 Mon Sep 17 00:00:00 2001 From: "Bradley T. Hughes" Date: Mon, 9 Aug 2010 12:40:44 +0200 Subject: Don't ignore interfaces that cannot multicast when querying the mcast iface If getsockopt() returns an address for an interface that doesn't advertise that it can multicast, don't second guess the OS, just return the iface. --- src/network/socket/qnativesocketengine_unix.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index 360dab5..aa55009 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -753,8 +753,6 @@ QNetworkInterface QNativeSocketEnginePrivate::nativeMulticastInterface() const QList ifaces = QNetworkInterface::allInterfaces(); for (int i = 0; i < ifaces.count(); ++i) { const QNetworkInterface &iface = ifaces.at(i); - if (!(iface.flags() & QNetworkInterface::CanMulticast)) - continue; QList entries = iface.addressEntries(); for (int j = 0; j < entries.count(); ++j) { const QNetworkAddressEntry &entry = entries.at(j); -- cgit v0.12