Long live QNetworkDatagram!

This commit adds a new class called QNetworkDatagram that encapsulates
the IP packet header and UDP/IP stack metadata along with the actual
payload data. It can be used for both receiving as well as sending data.

It's called QNetworkDatagram so it can be used by QSctpSocket too, when
that lands.

[ChangeLog][QtNetwork] Added QNetworkDatagram class, along with new
function receiveDatagram in QUdpSocket that returns it and an overload
to writeDatagram that can accept it.

Change-Id: Iee8cbc07c4434ce9b560ffff13ca467f425ddc3d
Reviewed-by: Alex Trotsenko <alex1973tr@gmail.com>
Reviewed-by: Richard J. Moore <rich@kde.org>
bb10
Thiago Macieira 2015-03-10 16:22:24 -07:00
parent 3aaa5d6b32
commit 4da2dda2aa
13 changed files with 1006 additions and 50 deletions

View File

@ -61,14 +61,7 @@ void Server::initSocket()
void Server::readPendingDatagrams()
{
while (udpSocket->hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
udpSocket->readDatagram(datagram.data(), datagram.size(),
&sender, &senderPort);
QNetworkDatagram datagram = udpSocket->receiveDatagram();
processTheDatagram(datagram);
}
}

View File

@ -11,6 +11,7 @@ HEADERS += kernel/qauthenticator.h \
kernel/qhostaddress_p.h \
kernel/qhostinfo.h \
kernel/qhostinfo_p.h \
kernel/qnetworkdatagram.h \
kernel/qnetworkdatagram_p.h \
kernel/qnetworkinterface.h \
kernel/qnetworkinterface_p.h \
@ -22,9 +23,10 @@ SOURCES += kernel/qauthenticator.cpp \
kernel/qdnslookup.cpp \
kernel/qhostaddress.cpp \
kernel/qhostinfo.cpp \
kernel/qurlinfo.cpp \
kernel/qnetworkdatagram.cpp \
kernel/qnetworkinterface.cpp \
kernel/qnetworkproxy.cpp \
kernel/qnetworkinterface.cpp
kernel/qurlinfo.cpp
unix {
!integrity: SOURCES += kernel/qdnslookup_unix.cpp

View File

@ -0,0 +1,535 @@
/****************************************************************************
**
** Copyright (C) 2016 Intel Corporation.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtNetwork module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qnetworkdatagram.h"
#include "qnetworkdatagram_p.h"
#ifndef QT_NO_UDPSOCKET
QT_BEGIN_NAMESPACE
/*!
\class QNetworkDatagram
\brief The QNetworkDatagram class provides the data and matadata of a UDP datagram.
\since 5.7
\ingroup network
\inmodule QtNetwork
\reentrant
QNetworkDatagram can be used with the \l QUdpSocket class to represent the full
information contained in a UDP (User Datagram Protocol) datagram.
QNetworkDatagram encapsulates the following information of a datagram:
\list
\li the payload data;
\li the sender address and port number;
\li the destination address and port number;
\li the remaining hop count limit (on IPv4, this field is usually called "time to live" - TTL);
\li the network interface index the datagram was received on or to be sent on.
\endlist
QUdpSocket will try to match a common behavior as much as possible on all
operating systems, but not all of the metadata above can be obtained in
some operating systems. Metadata that cannot be set on the datagram when
sending with QUdpSocket::writeDatagram() will be silently discarded.
Upon reception, the senderAddress() and senderPort() properties contain the
address and port of the peer that sent the datagram, while
destinationAddress() and destinationPort() contain the target that was
contained in the datagram. That is usually an address local to the current
machine, but it can also be an IPv4 broadcast address (such as
"255.255.255.255") or an IPv4 or IPv6 multicast address. Applications may
find it useful to determine if the datagram was sent specifically to this
machine via unicast addressing or whether it was sent to multiple destinations.
When sending, the senderAddress() and senderPort() should contain the local
address to be used when sending. The sender address must be an address that
is assigned to this machine, which can be obtained using
\l{QNetworkInterface}, and the port number must be the port number that the
socket is bound to. Either field can be left unset and will be filled in by
the operating system with default values. The destinationAddress() and
destinationPort() fields may be set to a target address different from the
one the UDP socket is currently associated with.
Usually, when sending a datagram in reply to a datagram previously
received, one will set the destinationAddress() to be the senderAddress()
of the incoming datagram and similarly for the port numbers. To facilitate
this common process, QNetworkDatagram provides the function makeReply().
The hopCount() function contains, for a received datagram, the remaining
hop count limit for the packet. When sending, it contains the hop count
limit to be set. Most protocols will leave this value set to the default
and let the operating system decide on the best value to be used.
Multicasting over IPv4 often uses this field to indicate the scope of the
multicast group (link-local, local to an organization or global).
The interfaceIndex() function contains the index of the operating system's
interface that received the packet. This value is the same one that can be
set on a QHostAddress::scopeId() property and matches the
QNetworkInterface::index() property. When sending packets to global
addresses, it is not necessary to set the interface index as the operating
system will choose the correct one using the system routing table. This
property is important when sending datagrams to link-local destinations,
whether unicast or multicast.
\section1 Feature support
Some features of QNetworkDatagram are not supported in all operating systems.
Only the address and ports of the remote host (sender in received packets
and destination for outgoing packets) are supported in all systems. On most
operating systems, the other features are supported only for IPv6. Software
should check at runtime whether the rest could be determined for IPv4
addresses.
The current feature support is as follows:
\table
\header \li Operating system \li Local address \li Hop count \li Interface index
\row \li FreeBSD \li Supported \li Supported \li Only for IPv6
\row \li Linux \li Supported \li Supported \li Supported
\row \li OS X \li Supported \li Supported \li Only for IPv6
\row \li Other Unix supporting RFC 3542 \li Only for IPv6 \li Only for IPv6 \li Only for IPv6
\row \li Windows XP and older \li Not supported \li Not supported \li Not supported
\row \li Windows Vista & up \li Supported \li Supported \li Supported
\row \li Windows CE \li Not supported \li Not supported \li Not supported
\row \li Windows RT \li Not supported \li Not supported \li Not supported
\endtable
\sa QUdpSocket, QNetworkInterface
*/
/*!
Creates a QNetworkDatagram object with no payload data and undefined destination address.
The payload can be modified by using setData() and the destination address
can be set with setDestination().
If the destination address is left undefined, QUdpSocket::writeDatagram()
will attempt to send the datagram to the address last associated with, by
using QUdpSocket::connectToHost().
*/
QNetworkDatagram::QNetworkDatagram()
: d(new QNetworkDatagramPrivate)
{
}
/*!
Creates a QNetworkDatagram object and sets \a data as the payload data, along with
\a destinationAddress and \a port as the destination address of the datagram.
*/
QNetworkDatagram::QNetworkDatagram(const QByteArray &data, const QHostAddress &destinationAddress, quint16 port)
: d(new QNetworkDatagramPrivate(data, destinationAddress, port))
{
}
/*!
Creates a copy of the \a other datagram, including the payload and metadata.
To create a datagram suitable for sending in a reply, use QNetworkDatagram::makeReply();
*/
QNetworkDatagram::QNetworkDatagram(const QNetworkDatagram &other)
: d(new QNetworkDatagramPrivate(*other.d))
{
}
/*! \internal */
QNetworkDatagram::QNetworkDatagram(QNetworkDatagramPrivate &dd)
: d(&dd)
{
}
/*!
Copies the \a other datagram, including the payload and metadata.
To create a datagram suitable for sending in a reply, use QNetworkDatagram::makeReply();
*/
QNetworkDatagram &QNetworkDatagram::operator=(const QNetworkDatagram &other)
{
*d = *other.d;
return *this;
}
/*!
Clears the payload data and metadata in this QNetworkDatagram object, resetting
them to their default values.
*/
void QNetworkDatagram::clear()
{
d->data.clear();
d->header.senderAddress.clear();
d->header.destinationAddress.clear();
d->header.hopLimit = -1;
d->header.ifindex = 0;
}
/*!
\fn QNetworkDatagram::isNull() const
Returns true if this QNetworkDatagram object is null. This function is the
opposite of isValid().
*/
/*!
Returns true if this QNetworkDatagram object is valid. A valid QNetworkDatagram
object contains at least one sender or receiver address. Valid datagrams
can contain empty payloads.
*/
bool QNetworkDatagram::isValid() const
{
return d->header.senderAddress.protocol() != QAbstractSocket::UnknownNetworkLayerProtocol ||
d->header.destinationAddress.protocol() != QAbstractSocket::UnknownNetworkLayerProtocol;
}
/*!
Returns the sender address associated with this datagram. For a datagram
received from the network, it is the address of the peer node that sent the
datagram. For an outgoing datagrams, it is the local address to be used
when sending.
If no sender address was set on this datagram, the returned object will
report true to QHostAddress::isNull().
\sa destinationAddress(), senderPort(), setSender()
*/
QHostAddress QNetworkDatagram::senderAddress() const
{
return d->header.senderAddress;
}
/*!
Returns the destination address associated with this datagram. For a
datagram received from the network, it is the address the peer node sent
the datagram to, which can either be a local address of this machine or a
multicast or broadcast address. For an outgoing datagrams, it is the
address the datagram should be sent to.
If no destination address was set on this datagram, the returned object
will report true to QHostAddress::isNull().
\sa senderAddress(), destinationPort(), setDestination()
*/
QHostAddress QNetworkDatagram::destinationAddress() const
{
return d->header.destinationAddress;
}
/*!
Returns the port number of the sender associated with this datagram. For a
datagram received from the network, it is the port number that the peer
node sent the datagram from. For an outgoing datagram, it is the local port
the datagram should be sent from.
If no sender address was associated with this datagram, this function
returns -1.
\sa senderAddress(), destinationPort(), setSender()
*/
int QNetworkDatagram::senderPort() const
{
return d->header.senderAddress.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol
? -1 : d->header.senderPort;
}
/*!
Returns the port number of the destination associated with this datagram.
For a datagram received from the network, it is the local port number that
the peer node sent the datagram to. For an outgoing datagram, it is the
peer port the datagram should be sent to.
If no destination address was associated with this datagram, this function
returns -1.
\sa destinationAddress(), senderPort(), setDestination()
*/
int QNetworkDatagram::destinationPort() const
{
return d->header.destinationAddress.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol
? -1 : d->header.destinationPort;
}
/*!
Sets the sender address associated with this datagram to be the address \a
address and port number \a port. The sender address and port numbers are
usually set by \l QUdpSocket upon reception, so there's no need to call
this function on a received datagram.
For outgoing datagrams, this function can be used to set the address the
datagram should carry. The address \a address must usually be one of the
local addresses assigned to this machine, which can be obtained using \l
QNetworkInterface. If left unset, the operating system will choose the most
appropriate address to use given the destination in question.
The port number \a port must be the port number associated with the socket,
if there is one. The value of 0 can be used to indicate that the operating
system should choose the port number.
\sa QUdpSocket::writeDatagram(), senderAddress(), senderPort(), setDestination()
*/
void QNetworkDatagram::setSender(const QHostAddress &address, quint16 port)
{
d->header.senderAddress = address;
d->header.senderPort = port;
}
/*!
Sets the destination address associated with this datagram to be the
address \a address and port number \a port. The destination address and
port numbers are usually set by \l QUdpSocket upon reception, so there's no
need to call this function on a received datagram.
For outgoing datagrams, this function can be used to set the address the
datagram should be sent to. It can be the unicast address used to
communicate with the peer or a broadcast or multicast address to send to a
group of devices.
\sa QUdpSocket::writeDatagram(), destinationAddress(), destinationPort(), setSender()
*/
void QNetworkDatagram::setDestination(const QHostAddress &address, quint16 port)
{
d->header.destinationAddress = address;
d->header.destinationPort = port;
}
/*!
Returns the hop count limit associated with this datagram. The hop count
limit is the number of nodes that are allowed to forward the IP packet
before it expires and an error is sent back to the sender of the datagram.
In IPv4, this value is usually known as "time to live" (TTL).
If this datagram was received from the network, this is the remaining hop
count of the datagram after reception and was decremented by 1 by each node
that forwarded the packet. A value of -1 indicates that the hop limit count
not be obtained.
If this is an outgoing datagram, this is the value to be set in the IP header
upon sending. A value of -1 indicates the operating system should choose
the value.
\sa setHopLimit()
*/
int QNetworkDatagram::hopLimit() const
{
return d->header.hopLimit;
}
/*!
Sets the hop count limit associated with this datagram to \a count. The hop
count limit is the number of nodes that are allowed to forward the IP
packet before it expires and an error is sent back to the sender of the
datagram. In IPv4, this value is usually known as "time to live" (TTL).
It is usually not necessary to call this function on datagrams received
from the network.
If this is an outgoing packet, this is the value to be set in the IP header
upon sending. The valid range for the value is 1 to 255. This function also
accepts a value of -1 to indicate that the operating system should choose
the value.
\sa hopLimit()
*/
void QNetworkDatagram::setHopLimit(int count)
{
d->header.hopLimit = count;
}
/*!
Returns the interface index this datagram is associated with. The interface
index is a positive number that uniquely identifies the network interface
in the operating system. This number matches the value returned by
QNetworkInterface::index() for the interface.
If this datagram was received from the network, this is the index of the
interface that the packet was received from. If this is an outgoing
datagram, this is the index of the interface that the datagram should be
sent on.
A value of 0 indicates that the interface index is unknown.
\sa setInterfaceIndex()
*/
uint QNetworkDatagram::interfaceIndex() const
{
return d->header.ifindex;
}
/*!
Sets the interface index this datagram is associated with to \a index. The
interface index is a positive number that uniquely identifies the network
interface in the operating system. This number matches the value returned
by QNetworkInterface::index() for the interface.
It is usually not necessary to call this function on datagrams received
from the network.
If this is an outgoing packet, this is the index of the interface the
datagram should be sent on. A value of 0 indicates that the operating
system should choose the interface based on other factors.
Note that the interface index can also be set with
QHostAddress::setScopeId() for IPv6 destination addresses and then with
setDestination(). If the scope ID set in the destination address and \a
index are different and neither is zero, it is undefined which interface
the operating system will send the datagram on.
\sa setInterfaceIndex()
*/
void QNetworkDatagram::setInterfaceIndex(uint index)
{
d->header.ifindex = index;
}
/*!
Returns the data payload of this datagram. For a datagram received from the
network, it contains the payload of the datagram. For an outgoing datagram,
it is the datagram to be sent.
Note that datagrams can be transmitted with no data, so the returned
QByteArray may be empty.
\sa setData()
*/
QByteArray QNetworkDatagram::data() const
{
return d->data;
}
/*!
Sets the data payload of this datagram to \a data. It is usually not
necessary to call this function on received datagrams. For outgoing
datagrams, this function sets the data to be sent on the network.
Since datagrams can empty, an empty QByteArray is a valid value for \a
data.
\sa data()
*/
void QNetworkDatagram::setData(const QByteArray &data)
{
d->data = data;
}
/*!
\fn QNetworkDatagram QNetworkDatagram::makeReply(const QByteArray &data) const
Creates a new QNetworkDatagram representing a reply to this incoming datagram
and sets the payload data to \a data. This function is a very convenient
way of responding to a datagram back to the original sender.
Example:
\code
void Server::readPendingDatagrams()
{
while (udpSocket->hasPendingDatagrams()) {
QNetworkDatagram datagram = udpSocket->receiveDatagram();
QByteArray replyData = processThePayload(datagram.data());
udpSocket->writeDatagram(datagram.makeReply(replyData));
}
}
\endcode
This function is especially convenient since it will automatically copy
parameters from this datagram to the new datagram as appropriate:
\list
\li this datagram's sender address and port are copied to the new
datagram's destination address and port;
\li this datagram's interface index, if any, is copied to the new
datagram's interface index;
\li this datagram's destination address and port are copied to the new
datagram's sender address and port only if the address is IPv6
global (non-multicast) address;
\li the hop count limit on the new datagram is reset to the default (-1);
\endlist
If QNetworkDatagram is modified in a future version of Qt to carry further
metadata, this function will copy that metadata as appropriate.
This datagram's destination address is not copied if it is an IPv4 address
because it is not possible to tell an IPv4 broadcast address apart from a
regular IPv4 address without an exhaustive search of all addresses assigned
to this machine. Attempting to send a datagram with the sender address
equal to the broadcast address is likely to fail. However, this should not
affect the communication as network interfaces with multiple IPv4 addresses
are uncommon, so the address the operating system will select will likely
be one the peer will understand.
\note This function comes with both rvalue- and lvalue-reference qualifier
overloads, so it is a good idea to make sure this object is rvalue, if
possible, before calling makeReply, so as to make better use of move
semantics. To achieve that, the example above would use:
\code
udpSocket->writeDatagram(std::move(datagram).makeReply(replyData));
\endcode
*/
static bool isNonMulticast(const QHostAddress &addr)
{
// is it a multicast address?
return !addr.isMulticast();
}
QNetworkDatagram QNetworkDatagram::makeReply_helper(const QByteArray &data) const
{
QNetworkDatagramPrivate *x = new QNetworkDatagramPrivate(data, d->header.senderAddress, d->header.senderPort);
x->header.ifindex = d->header.ifindex;
if (isNonMulticast(d->header.destinationAddress)) {
x->header.senderAddress = d->header.destinationAddress;
x->header.senderPort = d->header.destinationPort;
}
return QNetworkDatagram(*x);
}
void QNetworkDatagram::makeReply_helper_inplace(const QByteArray &data)
{
d->data = data;
d->header.hopLimit = -1;
qSwap(d->header.destinationPort, d->header.senderPort);
qSwap(d->header.destinationAddress, d->header.senderAddress);
if (!isNonMulticast(d->header.senderAddress))
d->header.senderAddress.clear();
}
void QNetworkDatagram::destroy(QNetworkDatagramPrivate *d)
{
Q_ASSUME(d);
delete d;
}
QT_END_NAMESPACE
#endif // QT_NO_UDPSOCKET

View File

@ -0,0 +1,122 @@
/****************************************************************************
**
** Copyright (C) 2016 Intel Corporation.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtNetwork module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QNETWORKDATAGRAM_H
#define QNETWORKDATAGRAM_H
#include <QtCore/qbytearray.h>
#include <QtNetwork/qhostaddress.h>
#ifndef QT_NO_UDPSOCKET
QT_BEGIN_NAMESPACE
class QNetworkDatagramPrivate;
class QUdpSocketPrivate;
class Q_NETWORK_EXPORT QNetworkDatagram
{
public:
QNetworkDatagram();
QNetworkDatagram(const QByteArray &data, const QHostAddress &destinationAddress = QHostAddress(),
quint16 port = 0); // implicit
QNetworkDatagram(const QNetworkDatagram &other);
QNetworkDatagram &operator=(const QNetworkDatagram &other);
~QNetworkDatagram()
{ if (d) destroy(d); }
QNetworkDatagram(QNetworkDatagram &&other) Q_DECL_NOTHROW
: d(other.d)
{ other.d = Q_NULLPTR; }
QNetworkDatagram &operator=(QNetworkDatagram &&other) Q_DECL_NOTHROW
{ swap(other); return *this; }
void swap(QNetworkDatagram &other) Q_DECL_NOTHROW
{ qSwap(d, other.d); }
void clear();
bool isValid() const;
bool isNull() const
{ return !isValid(); }
uint interfaceIndex() const;
void setInterfaceIndex(uint index);
QHostAddress senderAddress() const;
QHostAddress destinationAddress() const;
int senderPort() const;
int destinationPort() const;
void setSender(const QHostAddress &address, quint16 port = 0);
void setDestination(const QHostAddress &address, quint16 port);
int hopLimit() const;
void setHopLimit(int count);
QByteArray data() const;
void setData(const QByteArray &data);
#ifdef Q_COMPILER_REF_QUALIFIERS
QNetworkDatagram makeReply(const QByteArray &payload) const &
{ return makeReply_helper(payload); }
QNetworkDatagram makeReply(const QByteArray &payload) &&
{ makeReply_helper_inplace(payload); return *this; }
#else
QNetworkDatagram makeReply(const QByteArray &paylaod) const
{ return makeReply_helper(paylaod); }
#endif
private:
QNetworkDatagramPrivate *d;
friend class QUdpSocket;
explicit QNetworkDatagram(QNetworkDatagramPrivate &dd);
QNetworkDatagram makeReply_helper(const QByteArray &data) const;
void makeReply_helper_inplace(const QByteArray &data);
static void destroy(QNetworkDatagramPrivate *d);
};
Q_DECLARE_SHARED(QNetworkDatagram)
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QNetworkDatagram)
#endif // QT_NO_UDPSOCKET
#endif // QNETWORKDATAGRAM_H

View File

@ -73,6 +73,18 @@ public:
quint16 destinationPort;
};
class QNetworkDatagramPrivate
{
public:
QNetworkDatagramPrivate(const QByteArray &data = QByteArray(),
const QHostAddress &dstAddr = QHostAddress(), quint16 port = 0)
: data(data), header(dstAddr, port)
{}
QByteArray data;
QIpPacketHeader header;
};
QT_END_NAMESPACE
#endif // QNETWORKDATAGRAM_P_H

View File

@ -536,7 +536,7 @@ QList<QNetworkAddressEntry> QNetworkInterface::addressEntries() const
QNetworkInterface::interfaceFromName(name).index()
\endcode
\sa interfaceFromName(), interfaceNameFromIndex()
\sa interfaceFromName(), interfaceNameFromIndex(), QNetworkDatagram::interfaceIndex()
*/
int QNetworkInterface::interfaceIndexFromName(const QString &name)
{
@ -596,7 +596,7 @@ QNetworkInterface QNetworkInterface::interfaceFromIndex(int index)
QNetworkInterface::interfaceFromIndex(index).name()
\endcode
\sa interfaceFromIndex(), interfaceIndexFromName()
\sa interfaceFromIndex(), interfaceIndexFromName(), QNetworkDatagram::interfaceIndex()
*/
QString QNetworkInterface::interfaceNameFromIndex(int index)
{

View File

@ -111,7 +111,7 @@
\value WantAll this is a catch-all value to indicate the caller is
interested in all the available information
\sa readDatagram()
\sa readDatagram(), QNetworkDatagram
*/
#include "qnativesocketengine_p.h"

View File

@ -55,10 +55,10 @@
datagrams.
The most common way to use this class is to bind to an address and port
using bind(), then call writeDatagram() and readDatagram() to transfer
data. If you want to use the standard QIODevice functions read(),
readLine(), write(), etc., you must first connect the socket directly to a
peer by calling connectToHost().
using bind(), then call writeDatagram() and readDatagram() /
receiveDatagram() to transfer data. If you want to use the standard
QIODevice functions read(), readLine(), write(), etc., you must first
connect the socket directly to a peer by calling connectToHost().
The socket emits the bytesWritten() signal every time a datagram
is written to the network. If you just want to send datagrams,
@ -67,7 +67,7 @@
The readyRead() signal is emitted whenever datagrams arrive. In
that case, hasPendingDatagrams() returns \c true. Call
pendingDatagramSize() to obtain the size of the first pending
datagram, and readDatagram() to read it.
datagram, and readDatagram() or receiveDatagram() to read it.
\note An incoming datagram should be read when you receive the readyRead()
signal, otherwise this signal will not be emitted for the next datagram.
@ -94,11 +94,12 @@
\l{multicastreceiver}{Multicast Receiver} examples illustrate how
to use QUdpSocket in applications.
\sa QTcpSocket
\sa QTcpSocket, QNetworkDatagram
*/
#include "qudpsocket.h"
#include "qhostaddress.h"
#include "qnetworkdatagram.h"
#include "qnetworkinterface.h"
#include "qabstractsocket_p.h"
@ -358,8 +359,102 @@ qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddre
Sends the datagram \a datagram to the host address \a host and at
port \a port.
The function returns the number of bytes sent if it succeeded or -1 if it
encountered an error.
*/
/*!
\overload
Sends the datagram \a datagram to the host address and port numbers
contained in \a datagram, using the network interface and hop count limits
also set there. If the destination address and port numbers are unset, this
function will send to the address that was passed to connectToHost().
If the destination address is IPv6 with a non-empty
\l{QHostAddress::scopeId()}{scope id} but differs from the interface index
in \a datagram, it is undefined which interface the operating system will
choose to send on.
The function returns the number of bytes sent if it succeeded or -1 if it
encountered an error.
\warning Calling this function on a connected UDP socket may
result in an error and no packet being sent. If you are using a
connected socket, use write() to send datagrams.
\sa QNetworkDatagram::setDestination(), QNetworkDatagram::setHopLimit(), QNetworkDatagram::setInterfaceIndex()
*/
qint64 QUdpSocket::writeDatagram(const QNetworkDatagram &datagram)
{
Q_D(QUdpSocket);
#if defined QUDPSOCKET_DEBUG
qDebug("QUdpSocket::writeDatagram(%p, %i, \"%s\", %i)",
datagram.d->data.constData(),
datagram.d->data.size(),
datagram.destinationAddress().toString().toLatin1().constData(),
datagram.destinationPort());
#endif
if (!d->doEnsureInitialized(QHostAddress::Any, 0, datagram.destinationAddress()))
return -1;
if (state() == UnconnectedState)
bind();
qint64 sent = d->socketEngine->writeDatagram(datagram.d->data,
datagram.d->data.size(),
datagram.d->header);
d->cachedSocketDescriptor = d->socketEngine->socketDescriptor();
if (sent >= 0) {
emit bytesWritten(sent);
} else {
d->setErrorAndEmit(d->socketEngine->error(), d->socketEngine->errorString());
}
return sent;
}
/*!
Receives a datagram no larger than \a maxSize bytes and returns it in the
QNetworkDatagram object, along with the sender's host address and port. If
possible, this function will also try to determine the datagram's
destination address, port, and the number of hop counts at reception time.
On failure, returns a QNetworkDatagram that reports \l
{QNetworkDatagram::isValid()}{not valid}.
If \a maxSize is too small, the rest of the datagram will be lost. If \a
maxSize is 0, the datagram will be discarded. If \a maxSize is -1 (the
default), this function will attempt to read the entire datagram.
\sa writeDatagram(), hasPendingDatagrams(), pendingDatagramSize()
*/
QNetworkDatagram QUdpSocket::receiveDatagram(qint64 maxSize)
{
Q_D(QUdpSocket);
#if defined QUDPSOCKET_DEBUG
qDebug("QUdpSocket::receiveDatagram(%lld)", maxSize);
#endif
QT_CHECK_BOUND("QUdpSocket::receiveDatagram()", QNetworkDatagram());
if (maxSize < 0)
maxSize = d->socketEngine->pendingDatagramSize();
if (maxSize < 0)
return QNetworkDatagram();
QNetworkDatagram result(QByteArray(maxSize, Qt::Uninitialized));
qint64 readBytes = d->socketEngine->readDatagram(result.d->data.data(), maxSize, &result.d->header,
QAbstractSocketEngine::WantAll);
d->hasPendingData = false;
d->socketEngine->setReadNotificationEnabled(true);
if (readBytes < 0)
d->setErrorAndEmit(d->socketEngine->error(), d->socketEngine->errorString());
else if (readBytes != result.d->data.size())
result.d->data.truncate(readBytes);
return result;
}
/*!
Receives a datagram no larger than \a maxSize bytes and stores
it in \a data. The sender's host address and port is stored in
@ -404,6 +499,7 @@ qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *addres
d->setErrorAndEmit(d->socketEngine->error(), d->socketEngine->errorString());
return readBytes;
}
#endif // QT_NO_UDPSOCKET
QT_END_NAMESPACE

View File

@ -48,6 +48,7 @@ QT_BEGIN_NAMESPACE
#ifndef QT_NO_UDPSOCKET
class QNetworkDatagram;
class QNetworkInterface;
class QUdpSocketPrivate;
@ -72,7 +73,10 @@ public:
bool hasPendingDatagrams() const;
qint64 pendingDatagramSize() const;
QNetworkDatagram receiveDatagram(qint64 maxSize = -1);
qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *host = Q_NULLPTR, quint16 *port = Q_NULLPTR);
qint64 writeDatagram(const QNetworkDatagram &datagram);
qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &host, quint16 port);
inline qint64 writeDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port)
{ return writeDatagram(datagram.constData(), datagram.size(), host, port); }

View File

@ -7,6 +7,7 @@ SUBDIRS=\
qauthenticator \
qnetworkproxy \
qnetworkinterface \
qnetworkdatagram \
qnetworkaddressentry \
qhostaddress \

View File

@ -0,0 +1,5 @@
CONFIG += testcase console
CONFIG -= app_bundle
TARGET = tst_qnetworkdatagram
SOURCES += tst_qnetworkdatagram.cpp
QT = core network testlib

View File

@ -0,0 +1,146 @@
/****************************************************************************
**
** Copyright (C) 2016 Intel Corporation.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtNetwork module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QNetworkDatagram>
#include <QtTest>
#include <QCoreApplication>
class tst_QNetworkDatagram : public QObject
{
Q_OBJECT
public:
tst_QNetworkDatagram();
private Q_SLOTS:
void getSetCheck();
void makeReply_data();
void makeReply();
};
tst_QNetworkDatagram::tst_QNetworkDatagram()
{
}
void tst_QNetworkDatagram::getSetCheck()
{
QNetworkDatagram dg;
QVERIFY(dg.isNull());
QVERIFY(!dg.isValid());
QCOMPARE(dg.senderAddress(), QHostAddress());
QCOMPARE(dg.destinationAddress(), QHostAddress());
QCOMPARE(dg.senderPort(), -1);
QCOMPARE(dg.destinationPort(), -1);
QCOMPARE(dg.hopLimit(), -1);
QCOMPARE(dg.interfaceIndex(), 0U);
dg.setHopLimit(1);
QCOMPARE(dg.hopLimit(), 1);
dg.setHopLimit(255);
QCOMPARE(dg.hopLimit(), 255);
dg.setInterfaceIndex(1);
QCOMPARE(dg.interfaceIndex(), 1U);
dg.setInterfaceIndex(1234567U);
QCOMPARE(dg.interfaceIndex(), 1234567U);
dg.setSender(QHostAddress::Any, 12345);
QCOMPARE(dg.senderAddress(), QHostAddress(QHostAddress::Any));
QCOMPARE(dg.senderPort(), 12345);
dg.setSender(QHostAddress::LocalHost);
QCOMPARE(dg.senderAddress(), QHostAddress(QHostAddress::LocalHost));
QCOMPARE(dg.senderPort(), 0);
dg.setDestination(QHostAddress::LocalHostIPv6, 12345);
QCOMPARE(dg.destinationAddress(), QHostAddress(QHostAddress::LocalHostIPv6));
QCOMPARE(dg.destinationPort(), 12345);
dg.setDestination(QHostAddress::Broadcast, 137);
QCOMPARE(dg.destinationAddress(), QHostAddress(QHostAddress::Broadcast));
QCOMPARE(dg.destinationPort(), 137);
}
void tst_QNetworkDatagram::makeReply_data()
{
qRegisterMetaType<QNetworkDatagram>();
QTest::addColumn<QNetworkDatagram>("dgram");
QTest::addColumn<QString>("localAddress");
QNetworkDatagram dgram("some data", QHostAddress("192.0.2.1"), 10001);
dgram.setHopLimit(64);
dgram.setSender(QHostAddress::LocalHost, 12345);
QTest::newRow("ipv4") << dgram << "192.0.2.1";
dgram.setDestination(QHostAddress("224.0.0.1"), 10002);
QTest::newRow("ipv4-multicast") << dgram << QString();
dgram.setSender(QHostAddress::LocalHostIPv6, 12346);
dgram.setDestination(QHostAddress("2001:db8::1"), 12347);
QTest::newRow("ipv6") << dgram << "2001:db8::1";
dgram.setSender(QHostAddress("fe80::1%1"), 10003);
dgram.setDestination(QHostAddress("fe80::2%1"), 10004);
dgram.setInterfaceIndex(1);
QTest::newRow("ipv6-linklocal") << dgram << "fe80::2%1";
dgram.setDestination(QHostAddress("ff02::1%1"), 10005);
QTest::newRow("ipv6-multicast") << dgram << QString();
}
void tst_QNetworkDatagram::makeReply()
{
QFETCH(QNetworkDatagram, dgram);
QFETCH(QString, localAddress);
{
QNetworkDatagram reply = dgram.makeReply("World");
QCOMPARE(reply.data(), QByteArray("World"));
QCOMPARE(reply.senderAddress(), QHostAddress(localAddress));
QCOMPARE(reply.senderPort(), localAddress.isEmpty() ? -1 : dgram.destinationPort());
QCOMPARE(reply.destinationAddress(), dgram.senderAddress());
QCOMPARE(reply.destinationPort(), dgram.senderPort());
QCOMPARE(reply.interfaceIndex(), dgram.interfaceIndex());
QCOMPARE(reply.hopLimit(), -1);
}
QNetworkDatagram copy = dgram;
copy.setData(copy.data());
{
QNetworkDatagram reply = qMove(copy).makeReply("World");
QCOMPARE(reply.data(), QByteArray("World"));
QCOMPARE(reply.senderAddress(), QHostAddress(localAddress));
QCOMPARE(reply.senderPort(), localAddress.isEmpty() ? -1 : dgram.destinationPort());
QCOMPARE(reply.destinationAddress(), dgram.senderAddress());
QCOMPARE(reply.destinationPort(), dgram.senderPort());
QCOMPARE(reply.interfaceIndex(), dgram.interfaceIndex());
QCOMPARE(reply.hopLimit(), -1);
}
}
QTEST_MAIN(tst_QNetworkDatagram)
#include "tst_qnetworkdatagram.moc"

View File

@ -39,6 +39,7 @@
#include <qhostinfo.h>
#include <qtcpsocket.h>
#include <qmap.h>
#include <qnetworkdatagram.h>
#include <QNetworkProxy>
#include <QNetworkInterface>
@ -114,6 +115,7 @@ protected slots:
void async_readDatagramSlot();
private:
QList<QHostAddress> allAddresses;
#ifndef QT_NO_BEARERMANAGEMENT
QNetworkConfigurationManager *netConfMan;
QNetworkConfiguration networkConfiguration;
@ -173,6 +175,7 @@ void tst_QUdpSocket::initTestCase()
{
if (!QtNetworkSettings::verifyTestNetworkSettings())
QSKIP("No network test server available");
allAddresses = QNetworkInterface::allAddresses();
}
void tst_QUdpSocket::init()
@ -252,6 +255,11 @@ void tst_QUdpSocket::unconnectedServerAndClientTest()
int(strlen(message[i])));
buf[strlen(message[i])] = '\0';
QCOMPARE(QByteArray(buf), QByteArray(message[i]));
QCOMPARE(port, clientSocket.localPort());
if (host.toIPv4Address()) // in case the sender is IPv4 mapped in IPv6
QCOMPARE(host.toIPv4Address(), makeNonAny(clientSocket.localAddress()).toIPv4Address());
else
QCOMPARE(host, makeNonAny(clientSocket.localAddress()));
}
}
@ -325,14 +333,32 @@ void tst_QUdpSocket::broadcasting()
QVERIFY(serverSocket.hasPendingDatagrams());
do {
QByteArray arr; arr.resize(serverSocket.pendingDatagramSize() + 1);
QHostAddress host;
quint16 port;
const int messageLength = int(strlen(message[i]));
QCOMPARE((int) serverSocket.readDatagram(arr.data(), arr.size() - 1, &host, &port),
messageLength);
QNetworkDatagram dgram = serverSocket.receiveDatagram();
QVERIFY(dgram.isValid());
QByteArray arr = dgram.data();
QCOMPARE(arr.length(), messageLength);
arr.resize(messageLength);
QCOMPARE(arr, QByteArray(message[i]));
if (dgram.senderAddress().toIPv4Address()) // in case it's a v6-mapped address
QVERIFY2(allAddresses.contains(QHostAddress(dgram.senderAddress().toIPv4Address())),
dgram.senderAddress().toString().toLatin1());
else if (!dgram.senderAddress().isNull())
QVERIFY2(allAddresses.contains(dgram.senderAddress()),
dgram.senderAddress().toString().toLatin1());
QCOMPARE(dgram.senderPort(), int(broadcastSocket.localPort()));
if (!dgram.destinationAddress().isNull()) {
QVERIFY2(dgram.destinationAddress() == QHostAddress::Broadcast
|| broadcastAddresses.contains(dgram.destinationAddress()),
dgram.destinationAddress().toString().toLatin1());
QCOMPARE(dgram.destinationPort(), int(serverSocket.localPort()));
}
int ttl = dgram.hopLimit();
if (ttl != -1)
QVERIFY(ttl != 0);
} while (serverSocket.hasPendingDatagrams());
}
}
@ -1070,7 +1096,7 @@ void tst_QUdpSocket::zeroLengthDatagram()
#ifdef FORCE_SESSION
sender.setProperty("_q_networksession", QVariant::fromValue(networkSession));
#endif
QCOMPARE(sender.writeDatagram(QByteArray(), QHostAddress::LocalHost, receiver.localPort()), qint64(0));
QCOMPARE(sender.writeDatagram(QNetworkDatagram(QByteArray(), QHostAddress::LocalHost, receiver.localPort())), qint64(0));
QVERIFY2(receiver.waitForReadyRead(1000), QtNetworkSettings::msgSocketError(receiver).constData());
QVERIFY(receiver.hasPendingDatagrams());
@ -1355,10 +1381,20 @@ void tst_QUdpSocket::multicast()
QVERIFY(receiver.hasPendingDatagrams());
QList<QByteArray> receivedDatagrams;
while (receiver.hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(receiver.pendingDatagramSize());
receiver.readDatagram(datagram.data(), datagram.size(), 0, 0);
receivedDatagrams << datagram;
QNetworkDatagram dgram = receiver.receiveDatagram();
receivedDatagrams << dgram.data();
QVERIFY2(allAddresses.contains(dgram.senderAddress()),
dgram.senderAddress().toString().toLatin1());
QCOMPARE(dgram.senderPort(), int(sender.localPort()));
if (!dgram.destinationAddress().isNull()) {
QCOMPARE(dgram.destinationAddress(), groupAddress);
QCOMPARE(dgram.destinationPort(), int(receiver.localPort()));
}
int ttl = dgram.hopLimit();
if (ttl != -1)
QVERIFY(ttl != 0);
}
QCOMPARE(receivedDatagrams, datagrams);
@ -1453,7 +1489,8 @@ void tst_QUdpSocket::linkLocalIPv6()
quint16 port = 0;
foreach (const QHostAddress& addr, addresses) {
QUdpSocket *s = new QUdpSocket;
QVERIFY2(s->bind(addr, port), qPrintable(s->errorString()));
QVERIFY2(s->bind(addr, port), addr.toString().toLatin1()
+ '/' + QByteArray::number(port) + ": " + qPrintable(s->errorString()));
port = s->localPort(); //bind same port, different networks
sockets << s;
}
@ -1463,24 +1500,25 @@ void tst_QUdpSocket::linkLocalIPv6()
QSignalSpy neutralReadSpy(&neutral, SIGNAL(readyRead()));
QByteArray testData("hello");
QByteArray receiveBuffer("xxxxx");
foreach (QUdpSocket *s, sockets) {
QSignalSpy spy(s, SIGNAL(readyRead()));
neutralReadSpy.clear();
QVERIFY(s->writeDatagram(testData, s->localAddress(), neutral.localPort()));
QTRY_VERIFY(neutralReadSpy.count() > 0); //note may need to accept a firewall prompt
QHostAddress from;
quint16 fromPort;
QCOMPARE((int)neutral.readDatagram(receiveBuffer.data(), receiveBuffer.length(), &from, &fromPort), testData.length());
QCOMPARE(from, s->localAddress());
QCOMPARE(fromPort, s->localPort());
QCOMPARE(receiveBuffer, testData);
QVERIFY(neutral.writeDatagram(testData, s->localAddress(), s->localPort()));
QNetworkDatagram dgram = neutral.receiveDatagram(testData.length() * 2);
QVERIFY(dgram.isValid());
QCOMPARE(dgram.senderAddress(), s->localAddress());
QCOMPARE(dgram.senderPort(), int(s->localPort()));
QCOMPARE(dgram.data().length(), testData.length());
QCOMPARE(dgram.data(), testData);
QVERIFY(neutral.writeDatagram(dgram.makeReply(testData)));
QTRY_VERIFY(spy.count() > 0); //note may need to accept a firewall prompt
QCOMPARE((int)s->readDatagram(receiveBuffer.data(), receiveBuffer.length(), &from, &fromPort), testData.length());
QCOMPARE(receiveBuffer, testData);
dgram = s->receiveDatagram(testData.length() * 2);
QCOMPARE(dgram.data(), testData);
//sockets bound to other interfaces shouldn't have received anything
foreach (QUdpSocket *s2, sockets) {
@ -1535,21 +1573,23 @@ void tst_QUdpSocket::linkLocalIPv4()
QVERIFY(neutral.bind(QHostAddress(QHostAddress::AnyIPv4)));
QByteArray testData("hello");
QByteArray receiveBuffer("xxxxx");
foreach (QUdpSocket *s, sockets) {
QVERIFY(s->writeDatagram(testData, s->localAddress(), neutral.localPort()));
QVERIFY2(neutral.waitForReadyRead(10000), QtNetworkSettings::msgSocketError(neutral).constData());
QHostAddress from;
quint16 fromPort;
QCOMPARE((int)neutral.readDatagram(receiveBuffer.data(), receiveBuffer.length(), &from, &fromPort), testData.length());
QCOMPARE(from, s->localAddress());
QCOMPARE(fromPort, s->localPort());
QCOMPARE(receiveBuffer, testData);
QVERIFY(neutral.writeDatagram(testData, s->localAddress(), s->localPort()));
QVERIFY2(s->waitForReadyRead(10000), QtNetworkSettings::msgSocketError(*s).constData());
QCOMPARE((int)s->readDatagram(receiveBuffer.data(), receiveBuffer.length(), &from, &fromPort), testData.length());
QCOMPARE(receiveBuffer, testData);
QNetworkDatagram dgram = neutral.receiveDatagram(testData.length() * 2);
QVERIFY(dgram.isValid());
QCOMPARE(dgram.senderAddress(), s->localAddress());
QCOMPARE(dgram.senderPort(), int(s->localPort()));
QCOMPARE(dgram.data().length(), testData.length());
QCOMPARE(dgram.data(), testData);
QVERIFY(neutral.writeDatagram(dgram.makeReply(testData)));
dgram = s->receiveDatagram(testData.length() * 2);
QVERIFY(dgram.isValid());
QCOMPARE(dgram.data(), testData);
//sockets bound to other interfaces shouldn't have received anything
foreach (QUdpSocket *s2, sockets) {