diff options
author | Bradley T. Hughes <bradley.hughes@nokia.com> | 2010-05-05 14:20:07 (GMT) |
---|---|---|
committer | Bradley T. Hughes <bradley.hughes@nokia.com> | 2010-09-01 12:24:37 (GMT) |
commit | 3c35b0607e541008daaf53221da4e727015b5441 (patch) | |
tree | 552b064253cec07c132637b77938324782b798cd /src/network/socket | |
parent | 2abb3642bce6e4b32f44c3724dc8fcc9acf9976c (diff) | |
download | Qt-3c35b0607e541008daaf53221da4e727015b5441.zip Qt-3c35b0607e541008daaf53221da4e727015b5441.tar.gz Qt-3c35b0607e541008daaf53221da4e727015b5441.tar.bz2 |
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.
Diffstat (limited to 'src/network/socket')
-rw-r--r-- | src/network/socket/qnativesocketengine.cpp | 28 | ||||
-rw-r--r-- | src/network/socket/qnativesocketengine_p.h | 13 | ||||
-rw-r--r-- | src/network/socket/qnativesocketengine_unix.cpp | 119 |
3 files changed, 160 insertions, 0 deletions
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<QNetworkAddressEntry> 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; |