diff options
author | Bradley T. Hughes <bradley.hughes@nokia.com> | 2010-05-10 11:22:56 (GMT) |
---|---|---|
committer | Bradley T. Hughes <bradley.hughes@nokia.com> | 2010-09-01 12:24:41 (GMT) |
commit | b7fae53bf12d61274de7d5131ddc15d8f356df59 (patch) | |
tree | 60bba43f4003bd58cef79dfb6f9e4bfc2c1d69f7 | |
parent | 47d66c7855b9c315db29c82f4306de1864384ff7 (diff) | |
download | Qt-b7fae53bf12d61274de7d5131ddc15d8f356df59.zip Qt-b7fae53bf12d61274de7d5131ddc15d8f356df59.tar.gz Qt-b7fae53bf12d61274de7d5131ddc15d8f356df59.tar.bz2 |
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).
-rw-r--r-- | src/network/socket/qabstractsocketengine_p.h | 4 | ||||
-rw-r--r-- | src/network/socket/qnativesocketengine.cpp | 8 | ||||
-rw-r--r-- | src/network/socket/qnativesocketengine_p.h | 9 | ||||
-rw-r--r-- | src/network/socket/qnativesocketengine_win.cpp | 104 | ||||
-rw-r--r-- | src/network/socket/qudpsocket.cpp | 8 | ||||
-rw-r--r-- | src/network/socket/qudpsocket.h | 4 | ||||
-rw-r--r-- | 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 <winsock2.h> +#include <ws2tcpip.h> #include "qnativesocketengine_p.h" @@ -47,6 +48,7 @@ #include <qsocketnotifier.h> #include <qdebug.h> #include <qdatetime.h> +#include <qnetworkinterface.h> //#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<char *>(&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<char *>(&mreq4); + sockArgSize = sizeof(mreq4); + memset(&mreq4, 0, sizeof(mreq4)); + mreq4.imr_multiaddr.s_addr = htonl(groupAddress.toIPv4Address()); + + if (iface.isValid()) { + QList<QNetworkAddressEntry> 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())); |