Move support for socket binding from QUdpSocket upstream to QAbstractSocket.
This should be API-compatible with Qt 4, but is not ABI-compatible, due to removing the enum from QUdpSocket. Task-number: QTBUG-121 Change-Id: I967968c6cb6f96d3ab1d6300eadd5bde6154b300 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
3aa81c55e2
commit
03f852cb47
@ -291,10 +291,10 @@
|
||||
bytes).
|
||||
\value NetworkError An error occurred with the network (e.g., the
|
||||
network cable was accidentally plugged out).
|
||||
\value AddressInUseError The address specified to QUdpSocket::bind() is
|
||||
\value AddressInUseError The address specified to QAbstractSocket::bind() is
|
||||
already in use and was set to be exclusive.
|
||||
\value SocketAddressNotAvailableError The address specified to
|
||||
QUdpSocket::bind() does not belong to the host.
|
||||
QAbstractSocket::bind() does not belong to the host.
|
||||
\value UnsupportedSocketOperationError The requested socket operation is
|
||||
not supported by the local operating system (e.g., lack of
|
||||
IPv6 support).
|
||||
@ -329,7 +329,7 @@
|
||||
\value HostLookupState The socket is performing a host name lookup.
|
||||
\value ConnectingState The socket has started establishing a connection.
|
||||
\value ConnectedState A connection is established.
|
||||
\value BoundState The socket is bound to an address and port (for servers).
|
||||
\value BoundState The socket is bound to an address and port.
|
||||
\value ClosingState The socket is about to close (data may still
|
||||
be waiting to be written).
|
||||
\value ListeningState For internal use only.
|
||||
@ -363,6 +363,51 @@
|
||||
\sa QAbstractSocket::setSocketOption(), QAbstractSocket::socketOption()
|
||||
*/
|
||||
|
||||
/*! \enum QAbstractSocket::BindFlag
|
||||
\since 5.0
|
||||
|
||||
This enum describes the different flags you can pass to modify the
|
||||
behavior of QAbstractSocket::bind().
|
||||
|
||||
\note On Symbian OS bind flags behaviour depends on process capabilties.
|
||||
If process has NetworkControl capability, the bind attempt with
|
||||
ReuseAddressHint will always succeed even if the address and port is already
|
||||
bound by another socket with any flags. If process does not have
|
||||
NetworkControl capability, the bind attempt to address and port already
|
||||
bound by another socket will always fail.
|
||||
|
||||
\value ShareAddress Allow other services to bind to the same address
|
||||
and port. This is useful when multiple processes share
|
||||
the load of a single service by listening to the same address and port
|
||||
(e.g., a web server with several pre-forked listeners can greatly
|
||||
improve response time). However, because any service is allowed to
|
||||
rebind, this option is subject to certain security considerations.
|
||||
Note that by combining this option with ReuseAddressHint, you will
|
||||
also allow your service to rebind an existing shared address. On
|
||||
Unix, this is equivalent to the SO_REUSEADDR socket option. On Windows,
|
||||
this option is ignored.
|
||||
|
||||
\value DontShareAddress Bind the address and port exclusively, so that
|
||||
no other services are allowed to rebind. By passing this option to
|
||||
QAbstractSocket::bind(), you are guaranteed that on successs, your service
|
||||
is the only one that listens to the address and port. No services are
|
||||
allowed to rebind, even if they pass ReuseAddressHint. This option
|
||||
provides more security than ShareAddress, but on certain operating
|
||||
systems, it requires you to run the server with administrator privileges.
|
||||
On Unix and Mac OS X, not sharing is the default behavior for binding
|
||||
an address and port, so this option is ignored. On Windows, this
|
||||
option uses the SO_EXCLUSIVEADDRUSE socket option.
|
||||
|
||||
\value ReuseAddressHint Provides a hint to QAbstractSocket that it should try
|
||||
to rebind the service even if the address and port are already bound by
|
||||
another socket. On Windows, this is equivalent to the SO_REUSEADDR
|
||||
socket option. On Unix, this option is ignored.
|
||||
|
||||
\value DefaultForPlatform The default option for the current platform.
|
||||
On Unix and Mac OS X, this is equivalent to (DontShareAddress
|
||||
+ ReuseAddressHint), and on Windows, its equivalent to ShareAddress.
|
||||
*/
|
||||
|
||||
#include "qabstractsocket.h"
|
||||
#include "qabstractsocket_p.h"
|
||||
|
||||
@ -1293,6 +1338,94 @@ QAbstractSocket::~QAbstractSocket()
|
||||
abort();
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 5.0
|
||||
|
||||
Binds to \a address on port \a port, using the BindMode \a mode.
|
||||
|
||||
Binds this socket to the address \a address and the port \a port.
|
||||
|
||||
For UDP sockets, after binding, the signal QUdpSocket::readyRead() is emitted
|
||||
whenever a UDP datagram arrives on the specified address and port.
|
||||
Thus, This function is useful to write UDP servers.
|
||||
|
||||
For TCP sockets, this function may be used to specify which interface to use
|
||||
for an outgoing connection, which is useful in case of multiple network
|
||||
interfaces.
|
||||
|
||||
By default, the socket is bound using the DefaultForPlatform BindMode.
|
||||
If a port is not specified, a random port is chosen.
|
||||
|
||||
On success, the functions returns true and the socket enters
|
||||
BoundState; otherwise it returns false.
|
||||
|
||||
*/
|
||||
bool QAbstractSocket::bind(const QHostAddress &address, quint16 port, BindMode mode)
|
||||
{
|
||||
Q_D(QAbstractSocket);
|
||||
|
||||
// now check if the socket engine is initialized and to the right type
|
||||
if (!d->socketEngine || !d->socketEngine->isValid()) {
|
||||
QHostAddress nullAddress;
|
||||
d->resolveProxy(nullAddress.toString(), port);
|
||||
|
||||
QAbstractSocket::NetworkLayerProtocol protocol = address.protocol();
|
||||
if (protocol == QAbstractSocket::UnknownNetworkLayerProtocol)
|
||||
protocol = nullAddress.protocol();
|
||||
|
||||
if (!d->initSocketLayer(protocol))
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
if ((mode & ShareAddress) || (mode & ReuseAddressHint))
|
||||
d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1);
|
||||
else
|
||||
d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 0);
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
if (mode & ReuseAddressHint)
|
||||
d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1);
|
||||
else
|
||||
d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 0);
|
||||
if (mode & DontShareAddress)
|
||||
d->socketEngine->setOption(QAbstractSocketEngine::BindExclusively, 1);
|
||||
else
|
||||
d->socketEngine->setOption(QAbstractSocketEngine::BindExclusively, 0);
|
||||
#endif
|
||||
bool result = d->socketEngine->bind(address, port);
|
||||
d->cachedSocketDescriptor = d->socketEngine->socketDescriptor();
|
||||
|
||||
if (!result) {
|
||||
d->socketError = d->socketEngine->error();
|
||||
setErrorString(d->socketEngine->errorString());
|
||||
emit error(d->socketError);
|
||||
return false;
|
||||
}
|
||||
|
||||
d->state = BoundState;
|
||||
d->localAddress = d->socketEngine->localAddress();
|
||||
d->localPort = d->socketEngine->localPort();
|
||||
|
||||
emit stateChanged(d->state);
|
||||
d->socketEngine->setReadNotificationEnabled(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 5.0
|
||||
\overload
|
||||
|
||||
Binds to QHostAddress:Any on port \a port, using the BindMode \a mode.
|
||||
|
||||
By default, the socket is bound using the DefaultForPlatform BindMode.
|
||||
If a port is not specified, a random port is chosen.
|
||||
*/
|
||||
bool QAbstractSocket::bind(quint16 port, BindMode mode)
|
||||
{
|
||||
return bind(QHostAddress::Any, port, mode);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns true if the socket is valid and ready for use; otherwise
|
||||
returns false.
|
||||
@ -2345,7 +2478,7 @@ qint64 QAbstractSocket::writeData(const char *data, qint64 size)
|
||||
proxy connections for virtual connection settings.
|
||||
|
||||
Note that this function does not bind the local port of the socket
|
||||
prior to a connection (e.g., QUdpSocket::bind()).
|
||||
prior to a connection (e.g., QAbstractSocket::bind()).
|
||||
|
||||
\sa localAddress(), setLocalAddress(), setPeerPort()
|
||||
*/
|
||||
@ -2367,7 +2500,7 @@ void QAbstractSocket::setLocalPort(quint16 port)
|
||||
proxy connections for virtual connection settings.
|
||||
|
||||
Note that this function does not bind the local address of the socket
|
||||
prior to a connection (e.g., QUdpSocket::bind()).
|
||||
prior to a connection (e.g., QAbstractSocket::bind()).
|
||||
|
||||
\sa localAddress(), setLocalPort(), setPeerAddress()
|
||||
*/
|
||||
|
@ -115,10 +115,20 @@ public:
|
||||
MulticastTtlOption, // IP_MULTICAST_TTL
|
||||
MulticastLoopbackOption // IP_MULTICAST_LOOPBACK
|
||||
};
|
||||
enum BindFlag {
|
||||
DefaultForPlatform = 0x0,
|
||||
ShareAddress = 0x1,
|
||||
DontShareAddress = 0x2,
|
||||
ReuseAddressHint = 0x4
|
||||
};
|
||||
Q_DECLARE_FLAGS(BindMode, BindFlag)
|
||||
|
||||
QAbstractSocket(SocketType socketType, QObject *parent);
|
||||
virtual ~QAbstractSocket();
|
||||
|
||||
bool bind(const QHostAddress &address, quint16 port = 0, BindMode mode = DefaultForPlatform);
|
||||
bool bind(quint16 port = 0, BindMode mode = DefaultForPlatform);
|
||||
|
||||
// ### Qt 5: Make connectToHost() and disconnectFromHost() virtual.
|
||||
void connectToHost(const QString &hostName, quint16 port, OpenMode mode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);
|
||||
void connectToHost(const QHostAddress &address, quint16 port, OpenMode mode = ReadWrite);
|
||||
@ -214,6 +224,9 @@ private:
|
||||
Q_PRIVATE_SLOT(d_func(), void _q_forceDisconnect())
|
||||
};
|
||||
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractSocket::BindMode)
|
||||
|
||||
#ifndef QT_NO_DEBUG_STREAM
|
||||
Q_NETWORK_EXPORT QDebug operator<<(QDebug, QAbstractSocket::SocketError);
|
||||
Q_NETWORK_EXPORT QDebug operator<<(QDebug, QAbstractSocket::SocketState);
|
||||
|
@ -108,51 +108,6 @@
|
||||
\sa QTcpSocket
|
||||
*/
|
||||
|
||||
/*! \enum QUdpSocket::BindFlag
|
||||
\since 4.1
|
||||
|
||||
This enum describes the different flags you can pass to modify the
|
||||
behavior of QUdpSocket::bind().
|
||||
|
||||
\note On Symbian OS bind flags behaviour depends on process capabilties.
|
||||
If process has NetworkControl capability, the bind attempt with
|
||||
ReuseAddressHint will always succeed even if the address and port is already
|
||||
bound by another socket with any flags. If process does not have
|
||||
NetworkControl capability, the bind attempt to address and port already
|
||||
bound by another socket will always fail.
|
||||
|
||||
\value ShareAddress Allow other services to bind to the same address
|
||||
and port. This is useful when multiple processes share
|
||||
the load of a single service by listening to the same address and port
|
||||
(e.g., a web server with several pre-forked listeners can greatly
|
||||
improve response time). However, because any service is allowed to
|
||||
rebind, this option is subject to certain security considerations.
|
||||
Note that by combining this option with ReuseAddressHint, you will
|
||||
also allow your service to rebind an existing shared address. On
|
||||
Unix, this is equivalent to the SO_REUSEADDR socket option. On Windows,
|
||||
this option is ignored.
|
||||
|
||||
\value DontShareAddress Bind the address and port exclusively, so that
|
||||
no other services are allowed to rebind. By passing this option to
|
||||
QUdpSocket::bind(), you are guaranteed that on successs, your service
|
||||
is the only one that listens to the address and port. No services are
|
||||
allowed to rebind, even if they pass ReuseAddressHint. This option
|
||||
provides more security than ShareAddress, but on certain operating
|
||||
systems, it requires you to run the server with administrator privileges.
|
||||
On Unix and Mac OS X, not sharing is the default behavior for binding
|
||||
an address and port, so this option is ignored. On Windows, this
|
||||
option uses the SO_EXCLUSIVEADDRUSE socket option.
|
||||
|
||||
\value ReuseAddressHint Provides a hint to QUdpSocket that it should try
|
||||
to rebind the service even if the address and port are already bound by
|
||||
another socket. On Windows, this is equivalent to the SO_REUSEADDR
|
||||
socket option. On Unix, this option is ignored.
|
||||
|
||||
\value DefaultForPlatform The default option for the current platform.
|
||||
On Unix and Mac OS X, this is equivalent to (DontShareAddress
|
||||
+ ReuseAddressHint), and on Windows, its equivalent to ShareAddress.
|
||||
*/
|
||||
|
||||
#include "qhostaddress.h"
|
||||
#include "qnetworkinterface.h"
|
||||
#include "qabstractsocket_p.h"
|
||||
@ -224,111 +179,6 @@ QUdpSocket::~QUdpSocket()
|
||||
{
|
||||
}
|
||||
|
||||
/*!
|
||||
Binds this socket to the address \a address and the port \a port.
|
||||
When bound, the signal readyRead() is emitted whenever a UDP
|
||||
datagram arrives on the specified address and port. This function
|
||||
is useful to write UDP servers.
|
||||
|
||||
On success, the functions returns true and the socket enters
|
||||
BoundState; otherwise it returns false.
|
||||
|
||||
The socket is bound using the DefaultForPlatform BindMode.
|
||||
|
||||
\sa readDatagram()
|
||||
*/
|
||||
bool QUdpSocket::bind(const QHostAddress &address, quint16 port)
|
||||
{
|
||||
Q_D(QUdpSocket);
|
||||
if (!d->ensureInitialized(address, port))
|
||||
return false;
|
||||
|
||||
bool result = d_func()->socketEngine->bind(address, port);
|
||||
d->cachedSocketDescriptor = d->socketEngine->socketDescriptor();
|
||||
|
||||
if (!result) {
|
||||
d->socketError = d_func()->socketEngine->error();
|
||||
setErrorString(d_func()->socketEngine->errorString());
|
||||
emit error(d_func()->socketError);
|
||||
return false;
|
||||
}
|
||||
|
||||
d->state = BoundState;
|
||||
d->localAddress = d->socketEngine->localAddress();
|
||||
d->localPort = d->socketEngine->localPort();
|
||||
|
||||
emit stateChanged(d_func()->state);
|
||||
d_func()->socketEngine->setReadNotificationEnabled(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 4.1
|
||||
\overload
|
||||
|
||||
Binds to \a address on port \a port, using the BindMode \a mode.
|
||||
*/
|
||||
bool QUdpSocket::bind(const QHostAddress &address, quint16 port, BindMode mode)
|
||||
{
|
||||
Q_D(QUdpSocket);
|
||||
if (!d->ensureInitialized(address, port))
|
||||
return false;
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
if ((mode & ShareAddress) || (mode & ReuseAddressHint))
|
||||
d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1);
|
||||
else
|
||||
d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 0);
|
||||
#endif
|
||||
#ifdef Q_OS_WIN
|
||||
if (mode & ReuseAddressHint)
|
||||
d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 1);
|
||||
else
|
||||
d->socketEngine->setOption(QAbstractSocketEngine::AddressReusable, 0);
|
||||
if (mode & DontShareAddress)
|
||||
d->socketEngine->setOption(QAbstractSocketEngine::BindExclusively, 1);
|
||||
else
|
||||
d->socketEngine->setOption(QAbstractSocketEngine::BindExclusively, 0);
|
||||
#endif
|
||||
bool result = d_func()->socketEngine->bind(address, port);
|
||||
d->cachedSocketDescriptor = d->socketEngine->socketDescriptor();
|
||||
|
||||
if (!result) {
|
||||
d->socketError = d_func()->socketEngine->error();
|
||||
setErrorString(d_func()->socketEngine->errorString());
|
||||
emit error(d_func()->socketError);
|
||||
return false;
|
||||
}
|
||||
|
||||
d->state = BoundState;
|
||||
d->localAddress = d->socketEngine->localAddress();
|
||||
d->localPort = d->socketEngine->localPort();
|
||||
|
||||
emit stateChanged(d_func()->state);
|
||||
d_func()->socketEngine->setReadNotificationEnabled(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*! \overload
|
||||
|
||||
Binds to QHostAddress:Any on port \a port.
|
||||
*/
|
||||
bool QUdpSocket::bind(quint16 port)
|
||||
{
|
||||
return bind(QHostAddress::Any, port);
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 4.1
|
||||
\overload
|
||||
|
||||
Binds to QHostAddress:Any on port \a port, using the BindMode \a mode.
|
||||
*/
|
||||
bool QUdpSocket::bind(quint16 port, BindMode mode)
|
||||
{
|
||||
return bind(QHostAddress::Any, port, mode);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_NETWORKINTERFACE
|
||||
|
||||
/*!
|
||||
|
@ -60,23 +60,9 @@ class Q_NETWORK_EXPORT QUdpSocket : public QAbstractSocket
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum BindFlag {
|
||||
DefaultForPlatform = 0x0,
|
||||
ShareAddress = 0x1,
|
||||
DontShareAddress = 0x2,
|
||||
ReuseAddressHint = 0x4
|
||||
};
|
||||
Q_DECLARE_FLAGS(BindMode, BindFlag)
|
||||
|
||||
explicit QUdpSocket(QObject *parent = 0);
|
||||
virtual ~QUdpSocket();
|
||||
|
||||
bool bind(const QHostAddress &address, quint16 port);
|
||||
bool bind(quint16 port = 0);
|
||||
bool bind(const QHostAddress &address, quint16 port, BindMode mode);
|
||||
bool bind(quint16 port, BindMode mode);
|
||||
// ### Qt 5: Merge the bind functions
|
||||
|
||||
#ifndef QT_NO_NETWORKINTERFACE
|
||||
bool joinMulticastGroup(const QHostAddress &groupAddress);
|
||||
bool joinMulticastGroup(const QHostAddress &groupAddress,
|
||||
@ -101,8 +87,6 @@ private:
|
||||
Q_DECLARE_PRIVATE(QUdpSocket)
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QUdpSocket::BindMode)
|
||||
|
||||
#endif // QT_NO_UDPSOCKET
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -138,6 +138,8 @@ public slots:
|
||||
private slots:
|
||||
void socketsConstructedBeforeEventLoop();
|
||||
void constructing();
|
||||
void bind_data();
|
||||
void bind();
|
||||
void setInvalidSocketDescriptor();
|
||||
void setSocketDescriptor();
|
||||
void socketDescriptor();
|
||||
@ -479,6 +481,65 @@ void tst_QTcpSocket::constructing()
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
void tst_QTcpSocket::bind_data()
|
||||
{
|
||||
QTest::addColumn<QString>("stringAddr");
|
||||
QTest::addColumn<bool>("successExpected");
|
||||
QTest::addColumn<QString>("stringExpectedLocalAddress");
|
||||
|
||||
// iterate all interfaces, add all addresses on them as test data
|
||||
QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
|
||||
foreach (const QNetworkInterface &interface, interfaces) {
|
||||
if (!interface.isValid())
|
||||
continue;
|
||||
|
||||
foreach (const QNetworkAddressEntry &entry, interface.addressEntries()) {
|
||||
if (entry.ip().isInSubnet(QHostAddress::parseSubnet("fe80::/10")))
|
||||
continue; // link-local bind will fail, at least on Linux, so skip it.
|
||||
|
||||
QString ip(entry.ip().toString());
|
||||
QTest::newRow(ip.toLatin1().constData()) << ip << true << ip;
|
||||
}
|
||||
}
|
||||
|
||||
// additionally, try bind to known-bad addresses, and make sure this doesn't work
|
||||
// these ranges are guarenteed to be reserved for 'documentation purposes',
|
||||
// and thus, should be unused in the real world. Not that I'm assuming the
|
||||
// world is full of competent administrators, or anything.
|
||||
QStringList knownBad;
|
||||
knownBad << "198.51.100.1";
|
||||
knownBad << "2001:0DB8::1";
|
||||
foreach (const QString &badAddress, knownBad) {
|
||||
QTest::newRow(badAddress.toLatin1().constData()) << badAddress << false << QString();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QTcpSocket::bind()
|
||||
{
|
||||
QFETCH(QString, stringAddr);
|
||||
QFETCH(bool, successExpected);
|
||||
QFETCH(QString, stringExpectedLocalAddress);
|
||||
|
||||
QHostAddress addr(stringAddr);
|
||||
QHostAddress expectedLocalAddress(stringExpectedLocalAddress);
|
||||
|
||||
QTcpSocket *socket = newSocket();
|
||||
qDebug() << "Binding " << addr;
|
||||
|
||||
if (successExpected) {
|
||||
QVERIFY2(socket->bind(addr), qPrintable(socket->errorString()));
|
||||
} else {
|
||||
QVERIFY(!socket->bind(addr));
|
||||
}
|
||||
|
||||
QCOMPARE(socket->localAddress(), expectedLocalAddress);
|
||||
|
||||
delete socket;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
void tst_QTcpSocket::setInvalidSocketDescriptor()
|
||||
{
|
||||
QTcpSocket *socket = newSocket();
|
||||
|
Loading…
Reference in New Issue
Block a user