Merge pull request #1259 from ctrlaltdavid/dev/webrtc-packet

First exchange of Vircadia protocol packets with the Vircadia Web SDK
This commit is contained in:
Kalila 2021-07-15 22:34:13 -04:00 committed by GitHub
commit dfd71ab18b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 1070 additions and 222 deletions

View file

@ -82,7 +82,7 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
_assignmentServerHostname = assignmentServerHostname;
}
_assignmentServerSocket = HifiSockAddr(_assignmentServerHostname, assignmentServerPort, true);
_assignmentServerSocket = HifiSockAddr(SocketType::UDP, _assignmentServerHostname, assignmentServerPort, true);
if (_assignmentServerSocket.isNull()) {
qCCritical(assignment_client) << "PAGE: Couldn't resolve domain server address" << _assignmentServerHostname;
}
@ -110,7 +110,8 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
// did we get an assignment-client monitor port?
if (assignmentMonitorPort > 0) {
_assignmentClientMonitorSocket = HifiSockAddr(DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME, assignmentMonitorPort);
_assignmentClientMonitorSocket = HifiSockAddr(SocketType::UDP, DEFAULT_ASSIGNMENT_CLIENT_MONITOR_HOSTNAME,
assignmentMonitorPort);
_assignmentClientMonitorSocket.setObjectName("AssignmentClientMonitor");
qCDebug(assignment_client) << "Assignment-client monitor socket is" << _assignmentClientMonitorSocket;

View file

@ -69,7 +69,7 @@ AssignmentClientMonitor::AssignmentClientMonitor(const unsigned int numAssignmen
// create a NodeList so we can receive stats from children
DependencyManager::registerInheritance<LimitedNodeList, NodeList>();
auto addressManager = DependencyManager::set<AddressManager>();
auto nodeList = DependencyManager::set<LimitedNodeList>(listenPort);
auto nodeList = DependencyManager::set<LimitedNodeList>(NodeType::Unassigned, listenPort);
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::AssignmentClientStatus,

View file

@ -165,10 +165,6 @@ bool DomainServer::forwardMetaverseAPIRequest(HTTPConnection* connection,
DomainServer::DomainServer(int argc, char* argv[]) :
QCoreApplication(argc, argv),
_gatekeeper(this),
#ifdef WEBRTC_DATA_CHANNELS
_webrtcSignalingServer(QHostAddress::AnyIPv4, DEFAULT_DOMAIN_SERVER_WS_PORT, this),
_webrtcDataChannels(NodeType::DomainServer, this),
#endif
_httpManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTP_PORT,
QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this)
{
@ -252,8 +248,6 @@ DomainServer::DomainServer(int argc, char* argv[]) :
updateDownstreamNodes();
updateUpstreamNodes();
setUpWebRTC();
if (_type != NonMetaverse) {
// if we have a metaverse domain, we'll use an access token for API calls
resetAccountManagerAccessToken();
@ -737,10 +731,11 @@ void DomainServer::setupNodeListAndAssignments() {
// check for scripts the user wants to persist from their domain-server config
populateStaticScriptedAssignmentsFromSettings();
auto nodeList = DependencyManager::set<LimitedNodeList>(domainServerPort, domainServerDTLSPort);
auto nodeList = DependencyManager::set<LimitedNodeList>(NodeType::DomainServer, domainServerPort, domainServerDTLSPort);
// no matter the local port, save it to shared mem so that local assignment clients can ask what it is
nodeList->putLocalPortIntoSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, this, nodeList->getSocketLocalPort());
nodeList->putLocalPortIntoSharedMemory(DOMAIN_SERVER_LOCAL_PORT_SMEM_KEY, this,
nodeList->getSocketLocalPort(SocketType::UDP));
// store our local http ports in shared memory
quint16 localHttpPort = DOMAIN_SERVER_HTTP_PORT;
@ -3047,6 +3042,7 @@ ReplicationServerInfo serverInformationFromSettings(QVariantMap serverMap, Repli
// read the address and port and construct a HifiSockAddr from them
serverInfo.sockAddr = {
SocketType::UDP,
serverMap[REPLICATION_SERVER_ADDRESS].toString(),
(quint16) serverMap[REPLICATION_SERVER_PORT].toString().toInt()
};
@ -3139,20 +3135,6 @@ void DomainServer::updateUpstreamNodes() {
updateReplicationNodes(Upstream);
}
void DomainServer::setUpWebRTC() {
#ifdef WEBRTC_DATA_CHANNELS
// Inbound WebRTC signaling messages received from a client.
connect(&_webrtcSignalingServer, &WebRTCSignalingServer::messageReceived,
&_webrtcDataChannels, &WebRTCDataChannels::onSignalingMessage);
// Outbound WebRTC signaling messages being sent to a client.
connect(&_webrtcDataChannels, &WebRTCDataChannels::signalingMessage,
&_webrtcSignalingServer, &WebRTCSignalingServer::sendMessage);
#endif
}
void DomainServer::initializeExporter() {
static const QString ENABLE_EXPORTER = "monitoring.enable_prometheus_exporter";
static const QString EXPORTER_PORT = "monitoring.prometheus_exporter_port";
@ -3641,7 +3623,7 @@ void DomainServer::randomizeICEServerAddress(bool shouldTriggerHostLookup) {
indexToTry = distribution(generator);
}
_iceServerSocket = HifiSockAddr { candidateICEAddresses[indexToTry], ICE_SERVER_DEFAULT_PORT };
_iceServerSocket = HifiSockAddr { SocketType::UDP, candidateICEAddresses[indexToTry], ICE_SERVER_DEFAULT_PORT };
qCInfo(domain_server_ice) << "Set candidate ice-server socket to" << _iceServerSocket;
// clear our number of hearbeat denials, this should be re-set on ice-server change

View file

@ -27,11 +27,6 @@
#include <Assignment.h>
#include <HTTPSConnection.h>
#include <LimitedNodeList.h>
#include <shared/WebRTC.h>
#if defined(WEBRTC_DATA_CHANNELS)
#include <webrtc/WebRTCDataChannels.h>
#include <webrtc/WebRTCSignalingServer.h>
#endif
#include "AssetsBackupHandler.h"
#include "DomainGatekeeper.h"
@ -147,8 +142,6 @@ private slots:
void updateDownstreamNodes();
void updateUpstreamNodes();
void setUpWebRTC();
void initializeExporter();
void initializeMetadataExporter();
@ -319,11 +312,6 @@ private:
std::unordered_map<int, std::unique_ptr<QTemporaryFile>> _pendingContentFiles;
QThread _assetClientThread;
#ifdef WEBRTC_DATA_CHANNELS
WebRTCSignalingServer _webrtcSignalingServer;
WebRTCDataChannels _webrtcDataChannels;
#endif
};

View file

@ -37,7 +37,7 @@ IceServer::IceServer(int argc, char* argv[]) :
{
// start the ice-server socket
qDebug() << "ice-server socket is listening on" << ICE_SERVER_DEFAULT_PORT;
_serverSocket.bind(QHostAddress::AnyIPv4, ICE_SERVER_DEFAULT_PORT);
_serverSocket.bind(SocketType::UDP, QHostAddress::AnyIPv4, ICE_SERVER_DEFAULT_PORT);
// set processPacket as the verified packet callback for the udt::Socket
_serverSocket.setPacketHandler([this](std::unique_ptr<udt::Packet> packet) { processPacket(std::move(packet)); });

View file

@ -551,7 +551,7 @@ void setupPreferences() {
auto getter = [nodeListWeak] {
auto nodeList = nodeListWeak.lock();
if (nodeList) {
return static_cast<int>(nodeList->getSocketLocalPort());
return static_cast<int>(nodeList->getSocketLocalPort(SocketType::UDP));
} else {
return -1;
}
@ -559,7 +559,7 @@ void setupPreferences() {
auto setter = [nodeListWeak](int preset) {
auto nodeList = nodeListWeak.lock();
if (nodeList) {
nodeList->setSocketLocalPort(static_cast<quint16>(preset));
nodeList->setSocketLocalPort(SocketType::UDP, static_cast<quint16>(preset));
}
};
auto preference = new IntSpinnerPreference(NETWORKING, "Listening Port", getter, setter);

View file

@ -37,7 +37,7 @@
DomainHandler::DomainHandler(QObject* parent) :
QObject(parent),
_sockAddr(HifiSockAddr(QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)),
_sockAddr(HifiSockAddr(SocketType::UDP, QHostAddress::Null, DEFAULT_DOMAIN_SERVER_PORT)),
_icePeer(this),
_settingsTimer(this),
_apiRefreshTimer(this)
@ -282,7 +282,8 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname,
HifiSockAddr* replaceableSockAddr = &_iceServerSockAddr;
replaceableSockAddr->~HifiSockAddr();
replaceableSockAddr = new (replaceableSockAddr) HifiSockAddr(iceServerHostname, ICE_SERVER_DEFAULT_PORT);
replaceableSockAddr = new (replaceableSockAddr) HifiSockAddr(SocketType::UDP, iceServerHostname,
ICE_SERVER_DEFAULT_PORT);
_iceServerSockAddr.setObjectName("IceServer");
auto nodeList = DependencyManager::get<NodeList>();
@ -367,7 +368,7 @@ void DomainHandler::setIsConnected(bool isConnected) {
emit connectedToDomain(_domainURL);
// FIXME: Reinstate the requestDomainSettings() call here in version 2021.2.0 instead of having it in
// NodeList::processDomainServerList().
// NodeList::processDomainList().
/*
if (_domainURL.scheme() == URL_SCHEME_HIFI && !_domainURL.host().isEmpty()) {
// we've connected to new domain - time to ask it for global settings

View file

@ -27,21 +27,22 @@
int hifiSockAddrMetaTypeId = qRegisterMetaType<HifiSockAddr>();
HifiSockAddr::HifiSockAddr() :
_socketType(SocketType::Unknown),
_address(),
_port(0)
{
}
HifiSockAddr::HifiSockAddr(const QHostAddress& address, quint16 port) :
HifiSockAddr::HifiSockAddr(SocketType socketType, const QHostAddress& address, quint16 port) :
_socketType(socketType),
_address(address),
_port(port)
{
}
HifiSockAddr::HifiSockAddr(const HifiSockAddr& otherSockAddr) :
QObject(),
_socketType(otherSockAddr._socketType),
_address(otherSockAddr._address),
_port(otherSockAddr._port)
{
@ -50,12 +51,14 @@ HifiSockAddr::HifiSockAddr(const HifiSockAddr& otherSockAddr) :
HifiSockAddr& HifiSockAddr::operator=(const HifiSockAddr& rhsSockAddr) {
setObjectName(rhsSockAddr.objectName());
_socketType = rhsSockAddr._socketType;
_address = rhsSockAddr._address;
_port = rhsSockAddr._port;
return *this;
}
HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort, bool shouldBlockForLookup) :
HifiSockAddr::HifiSockAddr(SocketType socketType, const QString& hostname, quint16 hostOrderPort, bool shouldBlockForLookup) :
_socketType(socketType),
_address(hostname),
_port(hostOrderPort)
{
@ -73,19 +76,10 @@ HifiSockAddr::HifiSockAddr(const QString& hostname, quint16 hostOrderPort, bool
}
}
HifiSockAddr::HifiSockAddr(const sockaddr* sockaddr) {
_address = QHostAddress(sockaddr);
if (sockaddr->sa_family == AF_INET) {
_port = ntohs(reinterpret_cast<const sockaddr_in*>(sockaddr)->sin_port);
} else {
_port = ntohs(reinterpret_cast<const sockaddr_in6*>(sockaddr)->sin6_port);
}
}
void HifiSockAddr::swap(HifiSockAddr& otherSockAddr) {
using std::swap;
swap(_socketType, otherSockAddr._socketType);
swap(_address, otherSockAddr._address);
swap(_port, otherSockAddr._port);
@ -96,7 +90,7 @@ void HifiSockAddr::swap(HifiSockAddr& otherSockAddr) {
}
bool HifiSockAddr::operator==(const HifiSockAddr& rhsSockAddr) const {
return _address == rhsSockAddr._address && _port == rhsSockAddr._port;
return _socketType == rhsSockAddr._socketType && _address == rhsSockAddr._address && _port == rhsSockAddr._port;
}
void HifiSockAddr::handleLookupResult(const QHostInfo& hostInfo) {
@ -118,7 +112,7 @@ void HifiSockAddr::handleLookupResult(const QHostInfo& hostInfo) {
}
QString HifiSockAddr::toString() const {
return _address.toString() + ":" + QString::number(_port);
return socketTypeToString(_socketType) + " " + _address.toString() + ":" + QString::number(_port);
}
bool HifiSockAddr::hasPrivateAddress() const {
@ -134,17 +128,27 @@ bool HifiSockAddr::hasPrivateAddress() const {
}
QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr) {
debug.nospace() << sockAddr._address.toString().toLocal8Bit().constData() << ":" << sockAddr._port;
debug.nospace() << socketTypeToString(sockAddr._socketType).toLocal8Bit().constData() << " "
<< sockAddr._address.toString().toLocal8Bit().constData() << ":" << sockAddr._port;
return debug.space();
}
QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr) {
// Don't include socketType because it can be implied from the type of connection used.
// WEBRTC TODO: Reconsider this.
dataStream << sockAddr._address << sockAddr._port;
return dataStream;
}
QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr) {
// Don't include socketType because it can be implied from the type of connection used.
// WEBRTC TODO: Reconsider this.
dataStream >> sockAddr._address >> sockAddr._port;
// Set default for non-WebRTC code.
// WEBRTC TODO: Reconsider this.
sockAddr.setSocketType(SocketType::UDP);
return dataStream;
}

View file

@ -19,14 +19,16 @@ struct sockaddr;
#include <QtNetwork/QHostInfo>
#include "SocketType.h"
class HifiSockAddr : public QObject {
Q_OBJECT
public:
HifiSockAddr();
HifiSockAddr(const QHostAddress& address, quint16 port);
HifiSockAddr(SocketType socketType, const QHostAddress& address, quint16 port);
HifiSockAddr(const HifiSockAddr& otherSockAddr);
HifiSockAddr(const QString& hostname, quint16 hostOrderPort, bool shouldBlockForLookup = false);
HifiSockAddr(const sockaddr* sockaddr);
HifiSockAddr(SocketType socketType, const QString& hostname, quint16 hostOrderPort, bool shouldBlockForLookup = false);
bool isNull() const { return _address.isNull() && _port == 0; }
void clear() { _address.clear(); _port = 0;}
@ -37,6 +39,10 @@ public:
bool operator==(const HifiSockAddr& rhsSockAddr) const;
bool operator!=(const HifiSockAddr& rhsSockAddr) const { return !(*this == rhsSockAddr); }
SocketType getSocketType() const { return _socketType; }
SocketType* getSocketTypePointer() { return &_socketType; }
void setSocketType(const SocketType socketType) { _socketType = socketType; }
const QHostAddress& getAddress() const { return _address; }
QHostAddress* getAddressPointer() { return &_address; }
void setAddress(const QHostAddress& address) { _address = address; }
@ -53,6 +59,7 @@ public:
bool hasPrivateAddress() const; // checks if the address behind this sock addr is private per RFC 1918
friend QDebug operator<<(QDebug debug, const HifiSockAddr& sockAddr);
friend QDataStream& operator<<(QDataStream& dataStream, const HifiSockAddr& sockAddr);
friend QDataStream& operator>>(QDataStream& dataStream, HifiSockAddr& sockAddr);
@ -62,6 +69,7 @@ signals:
void lookupCompleted();
void lookupFailed();
private:
SocketType _socketType { SocketType::Unknown };
QHostAddress _address;
quint16 _port;
};

View file

@ -49,18 +49,18 @@ static Setting::Handle<quint16> LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.Loc
using namespace std::chrono_literals;
static const std::chrono::milliseconds CONNECTION_RATE_INTERVAL_MS = 1s;
LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) :
_nodeSocket(this),
LimitedNodeList::LimitedNodeList(char ownerType, int socketListenPort, int dtlsListenPort) :
_nodeSocket(this, true, ownerType),
_packetReceiver(new PacketReceiver(this))
{
qRegisterMetaType<ConnectionStep>("ConnectionStep");
auto port = (socketListenPort != INVALID_PORT) ? socketListenPort : LIMITED_NODELIST_LOCAL_PORT.get();
_nodeSocket.bind(QHostAddress::AnyIPv4, port);
quint16 assignedPort = _nodeSocket.localPort();
_nodeSocket.bind(SocketType::UDP, QHostAddress::AnyIPv4, port);
quint16 assignedPort = _nodeSocket.localPort(SocketType::UDP);
if (socketListenPort != INVALID_PORT && socketListenPort != 0 && socketListenPort != assignedPort) {
qCCritical(networking) << "PAGE: NodeList is unable to assign requested port of" << socketListenPort;
qCCritical(networking) << "PAGE: NodeList is unable to assign requested UDP port of" << socketListenPort;
}
qCDebug(networking) << "NodeList socket is listening on" << assignedPort;
qCDebug(networking) << "NodeList UDP socket is listening on" << assignedPort;
if (dtlsListenPort != INVALID_PORT) {
// only create the DTLS socket during constructor if a custom port is passed
@ -73,6 +73,8 @@ LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) :
qCDebug(networking) << "NodeList DTLS socket is listening on" << _dtlsSocket->localPort();
}
_nodeSocket.bind(SocketType::WebRTC, QHostAddress::AnyIPv4, DEFAULT_DOMAIN_SERVER_WS_PORT);
// check for local socket updates every so often
const int LOCAL_SOCKET_UPDATE_INTERVAL_MSECS = 5 * 1000;
QTimer* localSocketUpdate = new QTimer(this);
@ -204,15 +206,20 @@ void LimitedNodeList::setPermissions(const NodePermissions& newPermissions) {
}
}
void LimitedNodeList::setSocketLocalPort(quint16 socketLocalPort) {
void LimitedNodeList::setSocketLocalPort(SocketType socketType, quint16 socketLocalPort) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "setSocketLocalPort", Qt::QueuedConnection,
Q_ARG(quint16, socketLocalPort));
return;
}
if (_nodeSocket.localPort() != socketLocalPort) {
_nodeSocket.rebind(socketLocalPort);
LIMITED_NODELIST_LOCAL_PORT.set(socketLocalPort);
if (_nodeSocket.localPort(socketType) != socketLocalPort) {
_nodeSocket.rebind(socketType, socketLocalPort);
if (socketType == SocketType::UDP) {
LIMITED_NODELIST_LOCAL_PORT.set(socketLocalPort);
} else {
// WEBRTC TODO: Add WebRTC equivalent?
qCWarning(networking_webrtc) << "LIMITED_NODELIST_LOCAL_PORT not set for WebRTC socket";
}
}
}
@ -1105,7 +1112,7 @@ void LimitedNodeList::processSTUNResponse(std::unique_ptr<udt::BasePacket> packe
_publicSockAddr.getAddress().toString().toLocal8Bit().constData(),
_publicSockAddr.getPort());
_publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort);
_publicSockAddr = HifiSockAddr(SocketType::UDP, newPublicAddress, newPublicPort);
if (!_hasCompletedInitialSTUN) {
// if we're here we have definitely completed our initial STUN sequence
@ -1186,7 +1193,7 @@ void LimitedNodeList::stopInitialSTUNUpdate(bool success) {
qCDebug(networking) << "LimitedNodeList public socket will be set with local port and null QHostAddress.";
// reset the public address and port to a null address
_publicSockAddr = HifiSockAddr(QHostAddress(), _nodeSocket.localPort());
_publicSockAddr = HifiSockAddr(SocketType::UDP, QHostAddress(), _nodeSocket.localPort(SocketType::UDP));
// we have changed the publicSockAddr, so emit our signal
emit publicSockAddrChanged(_publicSockAddr);
@ -1213,7 +1220,7 @@ void LimitedNodeList::stopInitialSTUNUpdate(bool success) {
void LimitedNodeList::updateLocalSocket() {
// when update is called, if the local socket is empty then start with the guessed local socket
if (_localSockAddr.isNull()) {
setLocalSocket(HifiSockAddr { getGuessedLocalAddress(), _nodeSocket.localPort() });
setLocalSocket(HifiSockAddr { SocketType::UDP, getGuessedLocalAddress(), _nodeSocket.localPort(SocketType::UDP) });
}
// attempt to use Google's DNS to confirm that local IP
@ -1237,7 +1244,7 @@ void LimitedNodeList::connectedForLocalSocketTest() {
auto localHostAddress = localIPTestSocket->localAddress();
if (localHostAddress.protocol() == QAbstractSocket::IPv4Protocol) {
setLocalSocket(HifiSockAddr { localHostAddress, _nodeSocket.localPort() });
setLocalSocket(HifiSockAddr { SocketType::UDP, localHostAddress, _nodeSocket.localPort(SocketType::UDP) });
_hasTCPCheckedLocalSocket = true;
}
@ -1253,7 +1260,7 @@ void LimitedNodeList::errorTestingLocalSocket() {
// error connecting to the test socket - if we've never set our local socket using this test socket
// then use our possibly updated guessed local address as fallback
if (!_hasTCPCheckedLocalSocket) {
setLocalSocket(HifiSockAddr { getGuessedLocalAddress(), _nodeSocket.localPort() });
setLocalSocket(HifiSockAddr { SocketType::UDP, getGuessedLocalAddress(), _nodeSocket.localPort(SocketType::UDP) });
qCCritical(networking) << "PAGE: Can't connect to Google DNS service via TCP, falling back to guessed local address"
<< getLocalSockAddr();
}
@ -1273,8 +1280,8 @@ void LimitedNodeList::setLocalSocket(const HifiSockAddr& sockAddr) {
_localSockAddr = sockAddr;
if (_hasTCPCheckedLocalSocket) { // Force a port change for NAT:
reset("local socket change");
_nodeSocket.rebind(0);
_localSockAddr.setPort(_nodeSocket.localPort());
_nodeSocket.rebind(SocketType::UDP, 0);
_localSockAddr.setPort(_nodeSocket.localPort(SocketType::UDP));
qCInfo(networking) << "Local port changed to" << _localSockAddr.getPort();
}
}

View file

@ -135,8 +135,8 @@ public:
bool getThisNodeCanGetAndSetPrivateUserData() const { return _permissions.can(NodePermissions::Permission::canGetAndSetPrivateUserData); }
bool getThisNodeCanRezAvatarEntities() const { return _permissions.can(NodePermissions::Permission::canRezAvatarEntities); }
quint16 getSocketLocalPort() const { return _nodeSocket.localPort(); }
Q_INVOKABLE void setSocketLocalPort(quint16 socketLocalPort);
quint16 getSocketLocalPort(SocketType socketType) const { return _nodeSocket.localPort(socketType); }
Q_INVOKABLE void setSocketLocalPort(SocketType socketType, quint16 socketLocalPort);
QUdpSocket& getDTLSSocket();
@ -413,7 +413,8 @@ protected:
QUuid connectionSecretUUID;
};
LimitedNodeList(int socketListenPort = INVALID_PORT, int dtlsListenPort = INVALID_PORT);
LimitedNodeList(char ownerType = NodeType::DomainServer, int socketListenPort = INVALID_PORT,
int dtlsListenPort = INVALID_PORT);
LimitedNodeList(LimitedNodeList const&) = delete; // Don't implement, needed to avoid copies of singleton
void operator=(LimitedNodeList const&) = delete; // Don't implement, needed to avoid copies of singleton
@ -446,7 +447,7 @@ protected:
QUdpSocket* _dtlsSocket { nullptr };
HifiSockAddr _localSockAddr;
HifiSockAddr _publicSockAddr;
HifiSockAddr _stunSockAddr { STUN_SERVER_HOSTNAME, STUN_SERVER_PORT };
HifiSockAddr _stunSockAddr { SocketType::UDP, STUN_SERVER_HOSTNAME, STUN_SERVER_PORT };
bool _hasTCPCheckedLocalSocket { false };
bool _useAuthentication { true };

View file

@ -50,7 +50,7 @@ const int KEEPALIVE_PING_INTERVAL_MS = 1000;
const int MAX_SYSTEM_INFO_SIZE = 1000;
NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort) :
LimitedNodeList(socketListenPort, dtlsListenPort),
LimitedNodeList(newOwnerType, socketListenPort, dtlsListenPort),
_ownerType(newOwnerType),
_nodeTypesOfInterest(),
_domainHandler(this),
@ -147,7 +147,7 @@ NodeList::NodeList(char newOwnerType, int socketListenPort, int dtlsListenPort)
auto& packetReceiver = getPacketReceiver();
packetReceiver.registerListener(PacketType::DomainList,
PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processDomainServerList));
PacketReceiver::makeUnsourcedListenerReference<NodeList>(this, &NodeList::processDomainList));
packetReceiver.registerListener(PacketType::Ping,
PacketReceiver::makeSourcedListenerReference<NodeList>(this, &NodeList::processPingPacket));
packetReceiver.registerListener(PacketType::PingReply,
@ -357,7 +357,7 @@ void NodeList::sendDomainServerCheckIn() {
if (publicSockAddr.isNull()) {
// we don't know our public socket and we need to send it to the domain server
qCDebug(networking_ice) << "Waiting for inital public socket from STUN. Will not send domain-server check in.";
qCDebug(networking_ice) << "Waiting for initial public socket from STUN. Will not send domain-server check in.";
} else if (domainHandlerIp.isNull() && _domainHandler.requiresICE()) {
qCDebug(networking_ice) << "Waiting for ICE discovered domain-server socket. Will not send domain-server check in.";
handleICEConnectionToDomainServer();
@ -401,6 +401,8 @@ void NodeList::sendDomainServerCheckIn() {
return;
}
// WEBRTC TODO: Move code into packet library. And update reference in DomainConnectRequest.js.
auto domainPacket = NLPacket::create(domainPacketType);
QDataStream packetStream(domainPacket.get());
@ -452,7 +454,6 @@ void NodeList::sendDomainServerCheckIn() {
packetStream << hardwareAddress;
// now add the machine fingerprint
auto accountManager = DependencyManager::get<AccountManager>();
packetStream << FingerprintUtils::getMachineFingerprint();
platform::json all = platform::getAll();
@ -710,7 +711,9 @@ void NodeList::processDomainServerConnectionTokenPacket(QSharedPointer<ReceivedM
sendDomainServerCheckIn();
}
void NodeList::processDomainServerList(QSharedPointer<ReceivedMessage> message) {
void NodeList::processDomainList(QSharedPointer<ReceivedMessage> message) {
// WEBRTC TODO: Move code into packet library. And update reference in DomainServerList.js.
// parse header information
QDataStream packetStream(message->getMessage());
@ -891,6 +894,10 @@ void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) {
info.publicSocket.setAddress(_domainHandler.getIP());
}
// WEBRTC TODO: Handle WebRTC-connected nodes. Probably need to include SocketType in HifiSockAddr << and >>
info.publicSocket.setSocketType(SocketType::UDP);
info.localSocket.setSocketType(SocketType::UDP);
addNewNode(info);
}

View file

@ -109,7 +109,7 @@ public slots:
void sendDomainServerCheckIn();
void handleDSPathQuery(const QString& newPath);
void processDomainServerList(QSharedPointer<ReceivedMessage> message);
void processDomainList(QSharedPointer<ReceivedMessage> message);
void processDomainServerAddedNode(QSharedPointer<ReceivedMessage> message);
void processDomainServerRemovedNode(QSharedPointer<ReceivedMessage> message);
void processDomainServerPathResponse(QSharedPointer<ReceivedMessage> message);
@ -153,7 +153,9 @@ private slots:
void maybeSendIgnoreSetToNode(SharedNodePointer node);
private:
NodeList() : LimitedNodeList(INVALID_PORT, INVALID_PORT) { assert(false); } // Not implemented, needed for DependencyManager templates compile
NodeList() : LimitedNodeList(NodeType::Unassigned, INVALID_PORT, INVALID_PORT) {
assert(false); // Not implemented, needed for DependencyManager templates compile
}
NodeList(char ownerType, int socketListenPort = INVALID_PORT, int dtlsListenPort = INVALID_PORT);
NodeList(NodeList const&) = delete; // Don't implement, needed to avoid copies of singleton
void operator=(NodeList const&) = delete; // Don't implement, needed to avoid copies of singleton

View file

@ -139,9 +139,7 @@ void PacketReceiver::handleVerifiedPacket(std::unique_ptr<udt::Packet> packet) {
if (_shouldDropPackets) {
return;
}
auto nodeList = DependencyManager::get<LimitedNodeList>();
// setup an NLPacket from the packet we were passed
auto nlPacket = NLPacket::fromBase(std::move(packet));
auto receivedMessage = QSharedPointer<ReceivedMessage>::create(*nlPacket);

View file

@ -0,0 +1,39 @@
//
// SocketType.h
// libraries/networking/src
//
// Created by David Rowe on 17 May 2021.
// Copyright 2021 Vircadia contributors.
//
// Handles UDP and WebRTC sockets in parallel.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef vircadia_SocketType_h
#define vircadia_SocketType_h
/// @addtogroup Networking
/// @{
/// @brief The types of network socket.
enum class SocketType {
Unknown, ///< Unknown socket type.
UDP, ///< UDP socket.
WebRTC ///< WebRTC socket. A WebRTC data channel presented as a UDP-style socket.
};
/// @brief Returns the name of a SocketType value, e.g., <code>"WebRTC"</code>.
/// @param socketType The SocketType value.
/// @return The name of the SocketType value.
static QString socketTypeToString(SocketType socketType) {
static QStringList SOCKET_TYPE_STRINGS { "Unknown", "UDP", "WebRTC" };
return SOCKET_TYPE_STRINGS[(int)socketType];
}
/// @}
#endif // vircadia_SocketType_h

View file

@ -57,7 +57,7 @@ BasePacket::BasePacket(qint64 size) {
}
// Sanity check
Q_ASSERT(size >= 0 || size < maxPayload);
Q_ASSERT(size >= 0 && size <= maxPayload);
_packetSize = size;
_packet.reset(new char[_packetSize]());

View file

@ -26,6 +26,8 @@ namespace udt {
static const int CONNECTION_SEND_BUFFER_SIZE_PACKETS = 8192;
static const int UDP_SEND_BUFFER_SIZE_BYTES = 1048576;
static const int UDP_RECEIVE_BUFFER_SIZE_BYTES = 1048576;
static const int WEBRTC_SEND_BUFFER_SIZE_BYTES = 1048576;
static const int WEBRTC_RECEIVE_BUFFER_SIZE_BYTES = 1048576;
static const int DEFAULT_SYN_INTERVAL_USECS = 10 * 1000;

View file

@ -0,0 +1,284 @@
//
// NetworkSocket.cpp
// libraries/networking/src/udt
//
// Created by David Rowe on 21 Jun 2021.
// Copyright 2021 Vircadia contributors.
//
#include "NetworkSocket.h"
#include "../NetworkLogging.h"
NetworkSocket::NetworkSocket(QObject* parent, NodeType_t nodeType) :
QObject(parent),
_parent(parent),
_udpSocket(this)
#if defined(WEBRTC_DATA_CHANNELS)
,
_webrtcSocket(this, nodeType)
#endif
{
connect(&_udpSocket, &QUdpSocket::readyRead, this, &NetworkSocket::readyRead);
connect(&_udpSocket, &QAbstractSocket::stateChanged, this, &NetworkSocket::onUDPStateChanged);
// Use old SIGNAL/SLOT mechanism for Android builds.
connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(onUDPSocketError(QAbstractSocket::SocketError)));
#if defined(WEBRTC_DATA_CHANNELS)
connect(&_webrtcSocket, &WebRTCSocket::readyRead, this, &NetworkSocket::readyRead);
connect(&_webrtcSocket, &WebRTCSocket::stateChanged, this, &NetworkSocket::onWebRTCStateChanged);
// WEBRTC TODO: Add similar for errorOccurred
#endif
}
void NetworkSocket::setSocketOption(SocketType socketType, QAbstractSocket::SocketOption option, const QVariant& value) {
switch (socketType) {
case SocketType::UDP:
_udpSocket.setSocketOption(option, value);
break;
#if defined(WEBRTC_DATA_CHANNELS)
case SocketType::WebRTC:
_webrtcSocket.setSocketOption(option, value);
break;
#endif
default:
qCCritical(networking) << "Socket type not specified in setSocketOption()";
}
}
QVariant NetworkSocket::socketOption(SocketType socketType, QAbstractSocket::SocketOption option) {
switch (socketType) {
case SocketType::UDP:
return _udpSocket.socketOption(option);
#if defined(WEBRTC_DATA_CHANNELS)
case SocketType::WebRTC:
return _webrtcSocket.socketOption(option);
#endif
default:
qCCritical(networking) << "Socket type not specified in socketOption()";
return "";
}
}
void NetworkSocket::bind(SocketType socketType, const QHostAddress& address, quint16 port) {
switch (socketType) {
case SocketType::UDP:
_udpSocket.bind(address, port);
break;
#if defined(WEBRTC_DATA_CHANNELS)
case SocketType::WebRTC:
_webrtcSocket.bind(address, port);
break;
#endif
default:
qCCritical(networking) << "Socket type not specified in bind()";
}
}
void NetworkSocket::abort(SocketType socketType) {
switch (socketType) {
case SocketType::UDP:
_udpSocket.abort();
break;
#if defined(WEBRTC_DATA_CHANNELS)
case SocketType::WebRTC:
_webrtcSocket.abort();
break;
#endif
default:
qCCritical(networking) << "Socket type not specified in abort()";
}
}
quint16 NetworkSocket::localPort(SocketType socketType) const {
switch (socketType) {
case SocketType::UDP:
return _udpSocket.localPort();
#if defined(WEBRTC_DATA_CHANNELS)
case SocketType::WebRTC:
return _webrtcSocket.localPort();
#endif
default:
qCCritical(networking) << "Socket type not specified in localPort()";
return 0;
}
}
qintptr NetworkSocket::socketDescriptor(SocketType socketType) const {
switch (socketType) {
case SocketType::UDP:
return _udpSocket.socketDescriptor();
#if defined(WEBRTC_DATA_CHANNELS)
case SocketType::WebRTC:
return _webrtcSocket.socketDescriptor();
return 0;
#endif
default:
qCCritical(networking) << "Socket type not specified in socketDescriptor()";
return 0;
}
}
qint64 NetworkSocket::writeDatagram(const QByteArray& datagram, const HifiSockAddr& sockAddr) {
switch (sockAddr.getSocketType()) {
case SocketType::UDP:
// WEBRTC TODO: The Qt documentation says that the following call shouldn't be used if the UDP socket is connected!!!
// https://doc.qt.io/qt-5/qudpsocket.html#writeDatagram
return _udpSocket.writeDatagram(datagram, sockAddr.getAddress(), sockAddr.getPort());
#if defined(WEBRTC_DATA_CHANNELS)
case SocketType::WebRTC:
return _webrtcSocket.writeDatagram(datagram, sockAddr.getPort());
#endif
default:
qCCritical(networking) << "Socket type not specified in writeDatagram() address";
return 0;
}
}
qint64 NetworkSocket::bytesToWrite(SocketType socketType, quint16 port) const {
switch (socketType) {
case SocketType::UDP:
return _udpSocket.bytesToWrite();
#if defined(WEBRTC_DATA_CHANNELS)
case SocketType::WebRTC:
return _webrtcSocket.bytesToWrite(port);
#endif
default:
qCCritical(networking) << "Socket type not specified in bytesToWrite()";
return 0;
}
}
bool NetworkSocket::hasPendingDatagrams() const {
return
#if defined(WEBRTC_DATA_CHANNELS)
_webrtcSocket.hasPendingDatagrams() ||
#endif
_udpSocket.hasPendingDatagrams();
}
qint64 NetworkSocket::pendingDatagramSize() {
#if defined(WEBRTC_DATA_CHANNELS)
// Alternate socket types, remembering the socket type used so that the same socket type is used next readDatagram().
if (_lastSocketTypeRead == SocketType::UDP) {
if (_webrtcSocket.hasPendingDatagrams()) {
_pendingDatagramSizeSocketType = SocketType::WebRTC;
return _webrtcSocket.pendingDatagramSize();
} else {
_pendingDatagramSizeSocketType = SocketType::UDP;
return _udpSocket.pendingDatagramSize();
}
} else {
if (_udpSocket.hasPendingDatagrams()) {
_pendingDatagramSizeSocketType = SocketType::UDP;
return _udpSocket.pendingDatagramSize();
} else {
_pendingDatagramSizeSocketType = SocketType::WebRTC;
return _webrtcSocket.pendingDatagramSize();
}
}
#else
return _udpSocket.pendingDatagramSize();
#endif
}
qint64 NetworkSocket::readDatagram(char* data, qint64 maxSize, HifiSockAddr* sockAddr) {
#if defined(WEBRTC_DATA_CHANNELS)
// Read per preceding pendingDatagramSize() if any, otherwise alternate socket types.
if (_pendingDatagramSizeSocketType == SocketType::UDP
|| _pendingDatagramSizeSocketType == SocketType::Unknown && _lastSocketTypeRead == SocketType::WebRTC) {
_lastSocketTypeRead = SocketType::UDP;
_pendingDatagramSizeSocketType = SocketType::Unknown;
if (sockAddr) {
sockAddr->setSocketType(SocketType::UDP);
return _udpSocket.readDatagram(data, maxSize, sockAddr->getAddressPointer(), sockAddr->getPortPointer());
} else {
return _udpSocket.readDatagram(data, maxSize);
}
} else {
_lastSocketTypeRead = SocketType::WebRTC;
_pendingDatagramSizeSocketType = SocketType::Unknown;
if (sockAddr) {
sockAddr->setSocketType(SocketType::WebRTC);
return _webrtcSocket.readDatagram(data, maxSize, sockAddr->getAddressPointer(), sockAddr->getPortPointer());
} else {
return _webrtcSocket.readDatagram(data, maxSize);
}
}
#else
if (sockAddr) {
sockAddr->setSocketType(SocketType::UDP);
return _udpSocket.readDatagram(data, maxSize, sockAddr->getAddressPointer(), sockAddr->getPortPointer());
} else {
return _udpSocket.readDatagram(data, maxSize);
}
#endif
}
QAbstractSocket::SocketState NetworkSocket::state(SocketType socketType) const {
switch (socketType) {
case SocketType::UDP:
return _udpSocket.state();
#if defined(WEBRTC_DATA_CHANNELS)
case SocketType::WebRTC:
return _webrtcSocket.state();
#endif
default:
qCCritical(networking) << "Socket type not specified in state()";
return QAbstractSocket::SocketState::UnconnectedState;
}
}
QAbstractSocket::SocketError NetworkSocket::error(SocketType socketType) const {
switch (socketType) {
case SocketType::UDP:
return _udpSocket.error();
#if defined(WEBRTC_DATA_CHANNELS)
case SocketType::WebRTC:
return _webrtcSocket.error();
#endif
default:
qCCritical(networking) << "Socket type not specified in error()";
return QAbstractSocket::SocketError::UnknownSocketError;
}
}
QString NetworkSocket::errorString(SocketType socketType) const {
switch (socketType) {
case SocketType::UDP:
return _udpSocket.errorString();
#if defined(WEBRTC_DATA_CHANNELS)
case SocketType::WebRTC:
return _webrtcSocket.errorString();
#endif
default:
qCCritical(networking) << "Socket type not specified in errorString()";
return "";
}
}
void NetworkSocket::onUDPStateChanged(QAbstractSocket::SocketState socketState) {
emit stateChanged(SocketType::UDP, socketState);
}
void NetworkSocket::onWebRTCStateChanged(QAbstractSocket::SocketState socketState) {
emit stateChanged(SocketType::WebRTC, socketState);
}
void NetworkSocket::onUDPSocketError(QAbstractSocket::SocketError socketError) {
emit NetworkSocket::socketError(SocketType::UDP, socketError);
}
void NetworkSocket::onWebRTCSocketError(QAbstractSocket::SocketError socketError) {
emit NetworkSocket::socketError(SocketType::WebRTC, socketError);
}

View file

@ -0,0 +1,163 @@
//
// NetworkSocket.h
// libraries/networking/src/udt
//
// Created by David Rowe on 21 Jun 2021.
// Copyright 2021 Vircadia contributors.
//
#ifndef vircadia_NetworkSocket_h
#define vircadia_NetworkSocket_h
#include <QObject>
#include <QUdpSocket>
#include <shared/WebRTC.h>
#include "../HifiSockAddr.h"
#include "../NodeType.h"
#include "../SocketType.h"
#if defined(WEBRTC_DATA_CHANNELS)
#include "../webrtc/WebRTCSocket.h"
#endif
/// @addtogroup Networking
/// @{
/// @brief Multiplexes a QUdpSocket and a WebRTCSocket so that they appear as a single QUdpSocket-style socket.
class NetworkSocket : public QObject {
Q_OBJECT
public:
/// @brief Constructs a new NetworkSocket object.
/// @param parent Qt parent object.
/// @param nodeType The type of node that the NetworkSocket object is being used in.
NetworkSocket(QObject* parent, NodeType_t nodeType);
/// @brief Set the value of a UDP or WebRTC socket option.
/// @param socketType The type of socket for which to set the option value.
/// @param option The option to set the value of.
/// @param value The option value.
void setSocketOption(SocketType socketType, QAbstractSocket::SocketOption option, const QVariant& value);
/// @brief Gets the value of a UDP or WebRTC socket option.
/// @param socketType The type of socket for which to get the option value.
/// @param option The option to get the value of.
/// @return The option value.
QVariant socketOption(SocketType socketType, QAbstractSocket::SocketOption option);
/// @brief Binds the UDP or WebRTC socket to an address and port.
/// @param socketType The type of socket to bind.
/// @param address The address to bind to.
/// @param port The port to bind to.
void bind(SocketType socketType, const QHostAddress& address, quint16 port = 0);
/// @brief Immediately closes and resets the socket.
/// @param socketType The type of socket to close and reset.
void abort(SocketType socketType);
/// @brief Gets the UDP or WebRTC local port number.
/// @param socketType The type of socket for which to the get local port number.
/// @return The UDP or WebRTC local port number if available, otherwise <code>0</code>.
quint16 localPort(SocketType socketType) const;
/// @brief Returns the native socket descriptor of the UDP or WebRTC socket.
/// @param socketType The type of socket to get the socket descriptor for.
/// @return The native socket descriptor if available, otherwise <code>-1</code>.
qintptr socketDescriptor(SocketType socketType) const;
/// @brief Sends a datagram to a network address.
/// @param datagram The datagram to send.
/// @param sockAddr The address to send to.
/// @return The number of bytes if successfully sent, otherwise <code>-1</code>.
qint64 writeDatagram(const QByteArray& datagram, const HifiSockAddr& sockAddr);
/// @brief Gets the number of bytes waiting to be written.
/// @details For UDP, there's a single buffer used for all destinations. For WebRTC, each destination has its own buffer.
/// @param socketType The type of socket for which to get the number of bytes waiting to be written.
/// @param port If a WebRTC socket, the data channel for which to get the number of bytes waiting.
/// @return The number of bytes waiting to be written.
qint64 bytesToWrite(SocketType socketType, quint16 port = 0) const;
/// @brief Gets whether there is a pending datagram waiting to be read.
/// @return <code>true</code> if there is a datagram waiting to be read, <code>false</code> if there isn't.
bool hasPendingDatagrams() const;
/// @brief Gets the size of the next pending datagram, alternating between socket types if both have datagrams to read.
/// @return The size of the next pendign datagram.
qint64 pendingDatagramSize();
/// @brief Reads the next datagram per the most recent pendingDatagramSize call if made, otherwise alternating between
/// socket types if both have datagrams to read.
/// @param data The destination to write the data into.
/// @param maxSize The maximum number of bytes to read.
/// @param sockAddr The destination to write the source network address into.
/// @return The number of bytes if successfully read, otherwise <code>-1</code>.
qint64 readDatagram(char* data, qint64 maxSize, HifiSockAddr* sockAddr = nullptr);
/// @brief Gets the state of the UDP or WebRTC socket.
/// @param socketType The type of socket for which to get the state.
/// @return The socket state.
QAbstractSocket::SocketState state(SocketType socketType) const;
/// @brief Gets the type of error that last occurred.
/// @param socketType The type of socket for which to get the last error.
/// @return The type of error that last occurred
QAbstractSocket::SocketError error(SocketType socketType) const;
/// @brief Gets the description of the error that last occurred.
/// @param socketType The type of socket for which to get the last error's description.
/// @return The description of the error that last occurred.
QString errorString(SocketType socketType) const;
signals:
/// @brief Emitted each time new data becomes available for reading.
void readyRead();
/// @brief Emitted when the state of the underlying UDP or WebRTC socket changes.
/// @param socketType The type of socket that changed state.
/// @param socketState The socket's new state.
void stateChanged(SocketType socketType, QAbstractSocket::SocketState socketState);
/// @brief
/// @param socketType
/// @param socketError
void socketError(SocketType socketType, QAbstractSocket::SocketError socketError);
private slots:
void onUDPStateChanged(QAbstractSocket::SocketState socketState);
void onWebRTCStateChanged(QAbstractSocket::SocketState socketState);
void onUDPSocketError(QAbstractSocket::SocketError socketError);
void onWebRTCSocketError(QAbstractSocket::SocketError socketError);
private:
QObject* _parent;
QUdpSocket _udpSocket;
#if defined(WEBRTC_DATA_CHANNELS)
WebRTCSocket _webrtcSocket;
#endif
#if defined(WEBRTC_DATA_CHANNELS)
SocketType _pendingDatagramSizeSocketType { SocketType::Unknown };
SocketType _lastSocketTypeRead { SocketType::Unknown };
#endif
};
/// @}
#endif // vircadia_NetworkSocket_h

View file

@ -10,6 +10,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// WEBRTC TODO: Rename / split up into files with better names.
#ifndef hifi_PacketHeaders_h
#define hifi_PacketHeaders_h

View file

@ -39,18 +39,17 @@ using namespace udt;
#endif
Socket::Socket(QObject* parent, bool shouldChangeSocketOptions) :
Socket::Socket(QObject* parent, bool shouldChangeSocketOptions, NodeType_t nodeType) :
QObject(parent),
_udpSocket(parent),
_networkSocket(parent, nodeType),
_readyReadBackupTimer(new QTimer(this)),
_shouldChangeSocketOptions(shouldChangeSocketOptions)
{
connect(&_udpSocket, &QUdpSocket::readyRead, this, &Socket::readPendingDatagrams);
connect(&_networkSocket, &NetworkSocket::readyRead, this, &Socket::readPendingDatagrams);
// make sure we hear about errors and state changes from the underlying socket
connect(&_udpSocket, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(handleSocketError(QAbstractSocket::SocketError)));
connect(&_udpSocket, &QAbstractSocket::stateChanged, this, &Socket::handleStateChanged);
connect(&_networkSocket, &NetworkSocket::socketError, this, &Socket::handleSocketError);
connect(&_networkSocket, &NetworkSocket::stateChanged, this, &Socket::handleStateChanged);
// in order to help track down the zombie server bug, add a timer to check if we missed a readyRead
const int READY_READ_BACKUP_CHECK_MSECS = 2 * 1000;
@ -58,19 +57,21 @@ Socket::Socket(QObject* parent, bool shouldChangeSocketOptions) :
_readyReadBackupTimer->start(READY_READ_BACKUP_CHECK_MSECS);
}
void Socket::bind(const QHostAddress& address, quint16 port) {
_udpSocket.bind(address, port);
void Socket::bind(SocketType socketType, const QHostAddress& address, quint16 port) {
_networkSocket.bind(socketType, address, port);
if (_shouldChangeSocketOptions) {
setSystemBufferSizes();
setSystemBufferSizes(socketType);
if (socketType == SocketType::WebRTC) {
return;
}
#if defined(Q_OS_LINUX)
auto sd = _udpSocket.socketDescriptor();
auto sd = _networkSocket.socketDescriptor(socketType);
int val = IP_PMTUDISC_DONT;
setsockopt(sd, IPPROTO_IP, IP_MTU_DISCOVER, &val, sizeof(val));
#elif defined(Q_OS_WIN)
auto sd = _udpSocket.socketDescriptor();
auto sd = _networkSocket.socketDescriptor(socketType);
int val = 0; // false
if (setsockopt(sd, IPPROTO_IP, IP_DONTFRAGMENT, (const char *)&val, sizeof(val))) {
auto wsaErr = WSAGetLastError();
@ -80,16 +81,16 @@ void Socket::bind(const QHostAddress& address, quint16 port) {
}
}
void Socket::rebind() {
rebind(_udpSocket.localPort());
void Socket::rebind(SocketType socketType) {
rebind(socketType, _networkSocket.localPort(socketType));
}
void Socket::rebind(quint16 localPort) {
_udpSocket.abort();
bind(QHostAddress::AnyIPv4, localPort);
void Socket::rebind(SocketType socketType, quint16 localPort) {
_networkSocket.abort(socketType);
bind(socketType, QHostAddress::AnyIPv4, localPort);
}
void Socket::setSystemBufferSizes() {
void Socket::setSystemBufferSizes(SocketType socketType) {
for (int i = 0; i < 2; i++) {
QAbstractSocket::SocketOption bufferOpt;
QString bufferTypeString;
@ -98,20 +99,22 @@ void Socket::setSystemBufferSizes() {
if (i == 0) {
bufferOpt = QAbstractSocket::SendBufferSizeSocketOption;
numBytes = udt::UDP_SEND_BUFFER_SIZE_BYTES;
numBytes = socketType == SocketType::UDP
? udt::UDP_SEND_BUFFER_SIZE_BYTES : udt::WEBRTC_SEND_BUFFER_SIZE_BYTES;
bufferTypeString = "send";
} else {
bufferOpt = QAbstractSocket::ReceiveBufferSizeSocketOption;
numBytes = udt::UDP_RECEIVE_BUFFER_SIZE_BYTES;
numBytes = socketType == SocketType::UDP
? udt::UDP_RECEIVE_BUFFER_SIZE_BYTES : udt::WEBRTC_RECEIVE_BUFFER_SIZE_BYTES;
bufferTypeString = "receive";
}
int oldBufferSize = _udpSocket.socketOption(bufferOpt).toInt();
int oldBufferSize = _networkSocket.socketOption(socketType, bufferOpt).toInt();
if (oldBufferSize < numBytes) {
_udpSocket.setSocketOption(bufferOpt, QVariant(numBytes));
int newBufferSize = _udpSocket.socketOption(bufferOpt).toInt();
_networkSocket.setSocketOption(socketType, bufferOpt, QVariant(numBytes));
int newBufferSize = _networkSocket.socketOption(socketType, bufferOpt).toInt();
qCDebug(networking) << "Changed socket" << bufferTypeString << "buffer size from" << oldBufferSize << "to"
<< newBufferSize << "bytes";
@ -235,16 +238,18 @@ qint64 Socket::writeDatagram(const char* data, qint64 size, const HifiSockAddr&
}
qint64 Socket::writeDatagram(const QByteArray& datagram, const HifiSockAddr& sockAddr) {
auto socketType = sockAddr.getSocketType();
// don't attempt to write the datagram if we're unbound. Just drop it.
// _udpSocket.writeDatagram will return an error anyway, but there are
// _networkSocket.writeDatagram will return an error anyway, but there are
// potential crashes in Qt when that happens.
if (_udpSocket.state() != QAbstractSocket::BoundState) {
if (_networkSocket.state(socketType) != QAbstractSocket::BoundState) {
qCDebug(networking) << "Attempt to writeDatagram when in unbound state to" << sockAddr;
return -1;
}
qint64 bytesWritten = _udpSocket.writeDatagram(datagram, sockAddr.getAddress(), sockAddr.getPort());
int pending = _udpSocket.bytesToWrite();
qint64 bytesWritten = _networkSocket.writeDatagram(datagram, sockAddr);
int pending = _networkSocket.bytesToWrite(socketType, sockAddr.getPort());
if (bytesWritten < 0 || pending) {
int wsaError = 0;
static std::atomic<int> previousWsaError (0);
@ -252,8 +257,8 @@ qint64 Socket::writeDatagram(const QByteArray& datagram, const HifiSockAddr& soc
wsaError = WSAGetLastError();
#endif
QString errorString;
QDebug(&errorString) << "udt::writeDatagram (" << _udpSocket.state() << sockAddr << ") error - "
<< wsaError << _udpSocket.error() << "(" << _udpSocket.errorString() << ")"
QDebug(&errorString) << "udt::writeDatagram (" << _networkSocket.state(socketType) << sockAddr << ") error - "
<< wsaError << _networkSocket.error(socketType) << "(" << _networkSocket.errorString(socketType) << ")"
<< (pending ? "pending bytes:" : "pending:") << pending;
if (previousWsaError.exchange(wsaError) != wsaError) {
@ -343,7 +348,7 @@ void Socket::messageFailed(Connection* connection, Packet::MessageNumber message
}
void Socket::checkForReadyReadBackup() {
if (_udpSocket.hasPendingDatagrams()) {
if (_networkSocket.hasPendingDatagrams()) {
qCDebug(networking) << "Socket::checkForReadyReadBackup() detected blocked readyRead signal. Flushing pending datagrams.";
// so that birarda can possibly figure out how the heck we get into this state in the first place
@ -357,8 +362,8 @@ void Socket::checkForReadyReadBackup() {
// drop all of the pending datagrams on the floor
int droppedCount = 0;
while (_udpSocket.hasPendingDatagrams()) {
_udpSocket.readDatagram(nullptr, 0);
while (_networkSocket.hasPendingDatagrams()) {
_networkSocket.readDatagram(nullptr, 0);
++droppedCount;
}
qCDebug(networking) << "Flushed" << droppedCount << "Packets";
@ -371,8 +376,8 @@ void Socket::readPendingDatagrams() {
const auto abortTime = system_clock::now() + MAX_PROCESS_TIME;
int packetSizeWithHeader = -1;
while (_udpSocket.hasPendingDatagrams() &&
(packetSizeWithHeader = _udpSocket.pendingDatagramSize()) != -1) {
while (_networkSocket.hasPendingDatagrams() &&
(packetSizeWithHeader = _networkSocket.pendingDatagramSize()) != -1) {
if (system_clock::now() > abortTime) {
// We've been running for too long, stop processing packets for now
// Once we've processed the event queue, we'll come back to packet processing
@ -397,8 +402,7 @@ void Socket::readPendingDatagrams() {
auto buffer = std::unique_ptr<char[]>(new char[packetSizeWithHeader]);
// pull the datagram
auto sizeRead = _udpSocket.readDatagram(buffer.get(), packetSizeWithHeader,
senderSockAddr.getAddressPointer(), senderSockAddr.getPortPointer());
auto sizeRead = _networkSocket.readDatagram(buffer.get(), packetSizeWithHeader, &senderSockAddr);
// save information for this packet, in case it is the one that sticks readyRead
_lastPacketSizeRead = sizeRead;
@ -540,17 +544,17 @@ std::vector<HifiSockAddr> Socket::getConnectionSockAddrs() {
return addr;
}
void Socket::handleSocketError(QAbstractSocket::SocketError socketError) {
void Socket::handleSocketError(SocketType socketType, QAbstractSocket::SocketError socketError) {
int wsaError = 0;
static std::atomic<int> previousWsaError(0);
#ifdef WIN32
wsaError = WSAGetLastError();
#endif
int pending = _udpSocket.bytesToWrite();
int pending = _networkSocket.bytesToWrite(socketType);
QString errorString;
QDebug(&errorString) << "udt::Socket (" << _udpSocket.state() << ") error - " << wsaError << socketError <<
"(" << _udpSocket.errorString() << ")" << (pending ? "pending bytes:" : "pending:")
<< pending;
QDebug(&errorString) << "udt::Socket (" << socketTypeToString(socketType) << _networkSocket.state(socketType)
<< ") error - " << wsaError << socketError << "(" << _networkSocket.errorString(socketType) << ")"
<< (pending ? "pending bytes:" : "pending:") << pending;
if (previousWsaError.exchange(wsaError) != wsaError) {
qCDebug(networking).noquote() << errorString;
@ -563,9 +567,9 @@ void Socket::handleSocketError(QAbstractSocket::SocketError socketError) {
}
}
void Socket::handleStateChanged(QAbstractSocket::SocketState socketState) {
void Socket::handleStateChanged(SocketType socketType, QAbstractSocket::SocketState socketState) {
if (socketState != QAbstractSocket::BoundState) {
qCDebug(networking) << "udt::Socket state changed - state is now" << socketState;
qCDebug(networking) << socketTypeToString(socketType) << "socket state changed - state is now" << socketState;
}
}

View file

@ -21,11 +21,11 @@
#include <QtCore/QObject>
#include <QtCore/QTimer>
#include <QtNetwork/QUdpSocket>
#include "../HifiSockAddr.h"
#include "TCPVegasCC.h"
#include "Connection.h"
#include "NetworkSocket.h"
//#define UDT_CONNECTION_DEBUG
@ -55,9 +55,9 @@ class Socket : public QObject {
public:
using StatsVector = std::vector<std::pair<HifiSockAddr, ConnectionStats::Stats>>;
Socket(QObject* object = 0, bool shouldChangeSocketOptions = true);
Socket(QObject* object = 0, bool shouldChangeSocketOptions = true, NodeType_t nodeType = NodeType::Unassigned);
quint16 localPort() const { return _udpSocket.localPort(); }
quint16 localPort(SocketType socketType) const { return _networkSocket.localPort(socketType); }
// Simple functions writing to the socket with no processing
qint64 writeBasePacket(const BasePacket& packet, const HifiSockAddr& sockAddr);
@ -67,9 +67,9 @@ public:
qint64 writeDatagram(const char* data, qint64 size, const HifiSockAddr& sockAddr);
qint64 writeDatagram(const QByteArray& datagram, const HifiSockAddr& sockAddr);
void bind(const QHostAddress& address, quint16 port = 0);
void rebind(quint16 port);
void rebind();
void bind(SocketType socketType, const QHostAddress& address, quint16 port = 0);
void rebind(SocketType socketType, quint16 port);
void rebind(SocketType socketType);
void setPacketFilterOperator(PacketFilterOperator filterOperator) { _packetFilterOperator = filterOperator; }
void setPacketHandler(PacketHandler handler) { _packetHandler = handler; }
@ -105,11 +105,11 @@ private slots:
void readPendingDatagrams();
void checkForReadyReadBackup();
void handleSocketError(QAbstractSocket::SocketError socketError);
void handleStateChanged(QAbstractSocket::SocketState socketState);
void handleSocketError(SocketType socketType, QAbstractSocket::SocketError socketError);
void handleStateChanged(SocketType socketType, QAbstractSocket::SocketState socketState);
private:
void setSystemBufferSizes();
void setSystemBufferSizes(SocketType socketType);
Connection* findOrCreateConnection(const HifiSockAddr& sockAddr, bool filterCreation = false);
// privatized methods used by UDTTest - they are private since they must be called on the Socket thread
@ -121,7 +121,7 @@ private:
Q_INVOKABLE void writeReliablePacket(Packet* packet, const HifiSockAddr& sockAddr);
Q_INVOKABLE void writeReliablePacketList(PacketList* packetList, const HifiSockAddr& sockAddr);
QUdpSocket _udpSocket { this };
NetworkSocket _networkSocket;
PacketFilterOperator _packetFilterOperator;
PacketHandler _packetHandler;
MessageHandler _messageHandler;

View file

@ -25,6 +25,8 @@ const int MAX_WEBRTC_BUFFER_SIZE = 16777216; // 16MB
// #define WEBRTC_DEBUG
using namespace webrtc;
void WDCSetSessionDescriptionObserver::OnSuccess() {
#ifdef WEBRTC_DEBUG
@ -330,6 +332,13 @@ void WDCConnection::onDataChannelMessageReceived(const DataBuffer& buffer) {
_parent->emitDataMessage(_dataChannelID, byteArray);
}
qint64 WDCConnection::getBufferedAmount() const {
#ifdef WEBRTC_DEBUG
qCDebug(networking_webrtc) << "WDCConnection::getBufferedAmount()";
#endif
return _dataChannel->buffered_amount();
}
bool WDCConnection::sendDataMessage(const DataBuffer& buffer) {
#ifdef WEBRTC_DEBUG
qCDebug(networking_webrtc) << "WDCConnection::sendDataMessage()";
@ -356,12 +365,13 @@ void WDCConnection::closePeerConnection() {
}
WebRTCDataChannels::WebRTCDataChannels(NodeType_t nodeType, QObject* parent) :
_nodeType(nodeType),
_parent(parent)
WebRTCDataChannels::WebRTCDataChannels(QObject* parent, NodeType_t nodeType) :
QObject(parent),
_parent(parent),
_nodeType(nodeType)
{
#ifdef WEBRTC_DEBUG
qCDebug(networking_webrtc) << "WebRTCDataChannels::WebRTCDataChannels()";
qCDebug(networking_webrtc) << "WebRTCDataChannels::WebRTCDataChannels()" << nodeType << NodeType::getNodeTypeName(nodeType);
#endif
// Create a peer connection factory.
@ -392,14 +402,7 @@ WebRTCDataChannels::~WebRTCDataChannels() {
#ifdef WEBRTC_DEBUG
qCDebug(networking_webrtc) << "WebRTCDataChannels::~WebRTCDataChannels()";
#endif
QHashIterator<quint16, WDCConnection*> i(_connectionsByDataChannel);
while (i.hasNext()) {
i.next();
delete i.value();
}
_connectionsByWebSocket.clear();
_connectionsByDataChannel.clear();
reset();
_peerConnectionFactory = nullptr;
_rtcSignalingThread->Stop();
_rtcSignalingThread = nullptr;
@ -409,6 +412,16 @@ WebRTCDataChannels::~WebRTCDataChannels() {
_rtcNetworkThread = nullptr;
}
void WebRTCDataChannels::reset() {
QHashIterator<quint16, WDCConnection*> i(_connectionsByDataChannel);
while (i.hasNext()) {
i.next();
delete i.value();
}
_connectionsByWebSocket.clear();
_connectionsByDataChannel.clear();
}
quint16 WebRTCDataChannels::getNewDataChannelID() {
static const int QUINT16_LIMIT = std::numeric_limits<uint16_t>::max() + 1;
_lastDataChannelID = std::max((_lastDataChannelID + 1) % QUINT16_LIMIT, 1);
@ -474,7 +487,7 @@ void WebRTCDataChannels::sendSignalingMessage(const QJsonObject& message) {
void WebRTCDataChannels::emitDataMessage(int dataChannelID, const QByteArray& byteArray) {
#ifdef WEBRTC_DEBUG
qCDebug(networking_webrtc) << "WebRTCDataChannels::emitDataMessage() :" << dataChannelID;
qCDebug(networking_webrtc) << "WebRTCDataChannels::emitDataMessage() :" << dataChannelID << byteArray;
#endif
emit dataMessage(dataChannelID, byteArray);
}
@ -495,6 +508,14 @@ bool WebRTCDataChannels::sendDataMessage(int dataChannelID, const QByteArray& by
return connection->sendDataMessage(buffer);
}
/// @brief Gets the number of bytes waiting to be written on a data channel.
/// @param port The data channel ID.
/// @return The number of bytes waiting to be written on the data channel.
qint64 WebRTCDataChannels::getBufferedAmount(int dataChannelID) const {
auto connection = _connectionsByDataChannel.value(dataChannelID);
return connection->getBufferedAmount();
}
rtc::scoped_refptr<PeerConnectionInterface> WebRTCDataChannels::createPeerConnection(
const std::shared_ptr<WDCPeerConnectionObserver> peerConnectionObserver) {
#ifdef WEBRTC_DEBUG

View file

@ -23,8 +23,6 @@
#include "../NodeType.h"
using namespace webrtc;
class WebRTCDataChannels;
class WDCConnection;
@ -33,7 +31,7 @@ class WDCConnection;
/// @{
/// @brief A WebRTC session description observer.
class WDCSetSessionDescriptionObserver : public SetSessionDescriptionObserver {
class WDCSetSessionDescriptionObserver : public webrtc::SetSessionDescriptionObserver {
public:
/// @brief The call to SetLocalDescription or SetRemoteDescription succeeded.
@ -41,12 +39,12 @@ public:
/// @brief The call to SetLocalDescription or SetRemoteDescription failed.
/// @param error Error information.
void OnFailure(RTCError error) override;
void OnFailure(webrtc::RTCError error) override;
};
/// @brief A WebRTC create session description observer.
class WDCCreateSessionDescriptionObserver : public CreateSessionDescriptionObserver {
class WDCCreateSessionDescriptionObserver : public webrtc::CreateSessionDescriptionObserver {
public:
/// @brief Constructs a session description observer.
@ -55,11 +53,11 @@ public:
/// @brief The call to CreateAnswer succeeded.
/// @param The session description.
void OnSuccess(SessionDescriptionInterface* desc) override;
void OnSuccess(webrtc::SessionDescriptionInterface* desc) override;
//@ @brief The call to CreateAnswer failed.
/// @param error Error information.
void OnFailure(RTCError error) override;
void OnFailure(webrtc::RTCError error) override;
private:
WDCConnection* _parent;
@ -67,7 +65,7 @@ private:
/// @brief A WebRTC peer connection observer.
class WDCPeerConnectionObserver : public PeerConnectionObserver {
class WDCPeerConnectionObserver : public webrtc::PeerConnectionObserver {
public:
/// @brief Constructs a peer connection observer.
@ -76,26 +74,26 @@ public:
/// @brief Called when the SignalingState changes.
/// @param newState The new signaling state.
void OnSignalingChange(PeerConnectionInterface::SignalingState newState) override;
void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState newState) override;
/// @brief Called when renegotiation is needed. For example, an ICE restart has begun.
void OnRenegotiationNeeded() override;
/// @brief Called when the ICE gather state changes.
/// @param newState The new ICE gathering state.
void OnIceGatheringChange(PeerConnectionInterface::IceGatheringState newState) override;
void OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState newState) override;
/// @brief Called when a new ICE candidate has been gathered.
/// @param candidate The new ICE candidate.
void OnIceCandidate(const IceCandidateInterface* candidate) override;
void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) override;
/// @brief Called when a remote peer opens a data channel.
/// @param dataChannel The data channel.
void OnDataChannel(rtc::scoped_refptr<DataChannelInterface> dataChannel) override;
void OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface> dataChannel) override;
/// @brief Called when the peer connection state changes.
/// @param newState The new peer connection state.
void OnConnectionChange(PeerConnectionInterface::PeerConnectionState newState) override;
void OnConnectionChange(webrtc::PeerConnectionInterface::PeerConnectionState newState) override;
private:
WDCConnection* _parent;
@ -103,7 +101,7 @@ private:
/// @brief A WebRTC data channel observer.
class WDCDataChannelObserver : public DataChannelObserver {
class WDCDataChannelObserver : public webrtc::DataChannelObserver {
public:
/// @brief Constructs a data channel observer.
@ -115,7 +113,7 @@ public:
/// @brief A data channel message was received.
/// @param The message received.
void OnMessage(const DataBuffer& buffer) override;
void OnMessage(const webrtc::DataBuffer& buffer) override;
private:
WDCConnection* _parent;
@ -135,11 +133,11 @@ public:
/// @brief Gets the WebSocket ID.
/// @return The ID of the WebSocket.
quint16 getWebSocketID() { return _webSocketID; }
quint16 getWebSocketID() const { return _webSocketID; }
/// @brief Gets the WebRTC data channel ID.
/// @return The WebRTC data channel ID. `-1` if not open yet.
int getDataChannelID() { return _dataChannelID; }
int getDataChannelID() const { return _dataChannelID; }
/// @brief Sets the remote session description received from the remote client via the signaling channel.
@ -151,11 +149,11 @@ public:
/// @brief Sends an answer to the remote client via the signaling channel.
/// @param description The answer.
void sendAnswer(SessionDescriptionInterface* description);
void sendAnswer(webrtc::SessionDescriptionInterface* description);
/// @brief Sets the local session description on the WebRTC data channel being connected.
/// @param description The local session description.
void setLocalDescription(SessionDescriptionInterface* description);
void setLocalDescription(webrtc::SessionDescriptionInterface* description);
/// @brief Adds an ICE candidate received from the remote client via the signaling channel.
/// @param data The ICE candidate.
@ -163,15 +161,15 @@ public:
/// @brief Sends an ICE candidate to the remote vlient via the signaling channel.
/// @param candidate The ICE candidate.
void sendIceCandidate(const IceCandidateInterface* candidate);
void sendIceCandidate(const webrtc::IceCandidateInterface* candidate);
/// @brief Monitors the peer connection state.
/// @param state The new peer connection state.
void onPeerConnectionStateChanged(PeerConnectionInterface::PeerConnectionState state);
void onPeerConnectionStateChanged(webrtc::PeerConnectionInterface::PeerConnectionState state);
/// @brief Handles the WebRTC data channel being opened.
/// @param dataChannel The WebRTC data channel.
void onDataChannelOpened(rtc::scoped_refptr<DataChannelInterface> dataChannel);
void onDataChannelOpened(rtc::scoped_refptr<webrtc::DataChannelInterface> dataChannel);
/// @brief Handles a change in the state of the WebRTC data channel.
void onDataChannelStateChanged();
@ -179,12 +177,17 @@ public:
/// @brief Handles a message being received on the WebRTC data channel.
/// @param buffer The message received.
void onDataChannelMessageReceived(const DataBuffer& buffer);
void onDataChannelMessageReceived(const webrtc::DataBuffer& buffer);
/// @brief Gets the number of bytes waiting to be sent on the WebRTC data channel.
/// @return The number of bytes waiting to be sent on the WebRTC data channel.
qint64 getBufferedAmount() const;
/// @brief Sends a message on the WebRTC data channel.
/// @param buffer The message to send.
/// @return `true` if the message was sent, otherwise `false`.
bool sendDataMessage(const DataBuffer& buffer);
bool sendDataMessage(const webrtc::DataBuffer& buffer);
/// @brief Closes the WebRTC peer connection.
void closePeerConnection();
@ -198,10 +201,10 @@ private:
rtc::scoped_refptr<WDCCreateSessionDescriptionObserver> _createSessionDescriptionObserver { nullptr };
std::shared_ptr<WDCDataChannelObserver> _dataChannelObserver { nullptr };
rtc::scoped_refptr<DataChannelInterface> _dataChannel { nullptr };
rtc::scoped_refptr<webrtc::DataChannelInterface> _dataChannel { nullptr };
std::shared_ptr<WDCPeerConnectionObserver> _peerConnectionObserver { nullptr };
rtc::scoped_refptr<PeerConnectionInterface> _peerConnection { nullptr };
rtc::scoped_refptr<webrtc::PeerConnectionInterface> _peerConnection { nullptr };
};
@ -223,20 +226,25 @@ class WebRTCDataChannels : public QObject {
public:
/// @brief Constructs a new WebRTCDataChannels object.
/// @param nodeType The type of node that the WebRTCDataChannels object is being used in.
/// @param parent The parent Qt object.
WebRTCDataChannels(NodeType_t nodeType, QObject* parent);
/// @param nodeType The type of node that the WebRTCDataChannels object is being used in.
WebRTCDataChannels(QObject* parent, NodeType_t nodeType);
/// @brief Destroys a WebRTCDataChannels object.
~WebRTCDataChannels();
/// @brief Returns the type of node that the WebRTCDataChannels object is being used in.
/// @brief Gets the type of node that the WebRTCDataChannels object is being used in.
/// @return The type of node.
NodeType_t getNodeType() {
return _nodeType;
}
/// @brief Immediately closes all connections and resets the socket.
void reset();
/// @brief Get a new data channel ID to uniquely identify a WDCConnection.
/// @details This ID is assigned by WebRTCDataChannels; it is <em>not</em> the WebRTC data channel ID because that is only
/// unique within a peer connection.
/// @return A new data channel ID.
quint16 getNewDataChannelID();
@ -260,10 +268,15 @@ public:
/// @return `true` if the data message was sent, otherwise `false`.
bool sendDataMessage(int dataChannelID, const QByteArray& message);
/// @brief Gets the number of bytes waiting to be sent on a data channel.
/// @param dataChannelID The data channel ID.
/// @return The number of bytes waiting to be sent on the data channel.
qint64 getBufferedAmount(int dataChannelID) const;
/// @brief Creates a new WebRTC peer connection for connecting to an Interface client.
/// @param peerConnectionObserver An observer to monitor the WebRTC peer connection.
/// @return The new WebRTC peer connection.
rtc::scoped_refptr<PeerConnectionInterface> createPeerConnection(
rtc::scoped_refptr<webrtc::PeerConnectionInterface> createPeerConnection(
const std::shared_ptr<WDCPeerConnectionObserver> peerConnectionObserver);
/// @brief Initiates closing the peer connection for a WebRTC data channel.
@ -312,9 +325,9 @@ private:
std::unique_ptr<rtc::Thread> _rtcWorkerThread { nullptr };
std::unique_ptr<rtc::Thread> _rtcSignalingThread { nullptr };
rtc::scoped_refptr<PeerConnectionFactoryInterface> _peerConnectionFactory { nullptr };
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> _peerConnectionFactory { nullptr };
quint16 _lastDataChannelID { 0 };
quint16 _lastDataChannelID { 0 }; // First data channel ID is 1.
QHash<quint16, WDCConnection*> _connectionsByWebSocket;
QHash<quint16, WDCConnection*> _connectionsByDataChannel;

View file

@ -19,33 +19,33 @@
const int WEBRTC_SOCKET_CHECK_INTERVAL_IN_MS = 30000;
WebRTCSignalingServer::WebRTCSignalingServer(const QHostAddress& address, quint16 port, QObject* parent) :
WebRTCSignalingServer::WebRTCSignalingServer(QObject* parent) :
QObject(parent),
_address(address),
_port(port),
_webSocketServer(new QWebSocketServer(QStringLiteral("WebRTC Signaling Server"), QWebSocketServer::NonSecureMode, this))
{
connect(_webSocketServer, &QWebSocketServer::newConnection, this, &WebRTCSignalingServer::newWebSocketConnection);
bindSocket();
// Automatically recover from network interruptions.
_isWebSocketServerListeningTimer = new QTimer(this);
connect(_isWebSocketServerListeningTimer, &QTimer::timeout, this, &WebRTCSignalingServer::checkWebSocketServerIsListening);
_isWebSocketServerListeningTimer->start(WEBRTC_SOCKET_CHECK_INTERVAL_IN_MS);
}
bool WebRTCSignalingServer::bind(const QHostAddress& address, quint16 port) {
_address = address;
_port = port;
auto success = _webSocketServer->listen(_address, _port);
if (!success) {
qCWarning(networking_webrtc) << "Failed to open WebSocket for WebRTC signaling.";
}
return success;
}
void WebRTCSignalingServer::checkWebSocketServerIsListening() {
if (!_webSocketServer->isListening()) {
qCWarning(networking_webrtc) << "WebSocket on port " << QString::number(_port) << " is no longer listening";
_webSockets.clear();
bindSocket();
}
}
void WebRTCSignalingServer::bindSocket() {
if (!_webSocketServer->listen(_address, _port)) {
qCWarning(networking_webrtc) << "Failed to open WebSocket for WebRTC signaling.";
_webSocketServer->listen(_address, _port);
}
}

View file

@ -59,10 +59,14 @@ class WebRTCSignalingServer : public QObject {
public:
/// @brief Constructs a new WebRTCSignalingServer object.
/// @param address The IP address to use for the WebSocket.
/// @param port The port to use for the WebSocket.
/// @param parent Qt parent object.
WebRTCSignalingServer(const QHostAddress& address, quint16 port, QObject* parent = nullptr);
WebRTCSignalingServer(QObject* parent);
/// @brief Binds the WebRTC signaling server's WebSocket to an address and port.
/// @param address The address to use for the WebSocket.
/// @param port The port to use for the WebSocket.
/// @return <code>true</code> if the WebSocket was successfully bound, <code>false</code> if it wasn't.
bool bind(const QHostAddress& address, quint16 port);
public slots:
@ -88,11 +92,10 @@ private slots:
private:
void checkWebSocketServerIsListening();
void bindSocket();
QWebSocketServer* _webSocketServer;
QHostAddress _address;
const quint16 _port;
quint16 _port { 0 };
QHash<quint16, QWebSocket*> _webSockets; // client WebSocket port, client WebSocket object

View file

@ -0,0 +1,157 @@
//
// WebRTCSocket.cpp
// libraries/networking/src/webrtc
//
// Created by David Rowe on 21 Jun 2021.
// Copyright 2021 Vircadia contributors.
//
#include "WebRTCSocket.h"
#if defined(WEBRTC_DATA_CHANNELS)
#include "../NetworkLogging.h"
#include "../udt/Constants.h"
WebRTCSocket::WebRTCSocket(QObject* parent, NodeType_t nodeType) :
QObject(parent),
_signalingServer(this /*, QHostAddress::AnyIPv4, DEFAULT_DOMAIN_SERVER_WS_PORT*/),
_dataChannels(this, nodeType)
{
// Connect WebRTC signaling server and data channels.
connect(&_signalingServer, &WebRTCSignalingServer::messageReceived,
&_dataChannels, &WebRTCDataChannels::onSignalingMessage);
connect(&_dataChannels, &WebRTCDataChannels::signalingMessage,
&_signalingServer, &WebRTCSignalingServer::sendMessage);
// Route received data channel messages.
connect(&_dataChannels, &WebRTCDataChannels::dataMessage, this, &WebRTCSocket::onDataChannelReceivedMessage);
}
void WebRTCSocket::setSocketOption(QAbstractSocket::SocketOption option, const QVariant& value) {
clearError();
switch (option) {
case QAbstractSocket::SocketOption::ReceiveBufferSizeSocketOption:
case QAbstractSocket::SocketOption::SendBufferSizeSocketOption:
// WebRTC doesn't provide access to setting these buffer sizes.
break;
default:
setError(QAbstractSocket::SocketError::UnsupportedSocketOperationError, "Failed to set socket option");
qCCritical(networking_webrtc) << "WebRTCSocket::setSocketOption() not implemented for option:" << option;
}
}
QVariant WebRTCSocket::socketOption(QAbstractSocket::SocketOption option) {
clearError();
switch (option) {
case QAbstractSocket::SocketOption::ReceiveBufferSizeSocketOption:
// WebRTC doesn't provide access to the receive buffer size. Just use the default buffer size.
return udt::WEBRTC_RECEIVE_BUFFER_SIZE_BYTES;
case QAbstractSocket::SocketOption::SendBufferSizeSocketOption:
// WebRTC doesn't provide access to the send buffer size though it's probably 16MB. Just use the default buffer size.
return udt::WEBRTC_SEND_BUFFER_SIZE_BYTES;
default:
setError(QAbstractSocket::SocketError::UnsupportedSocketOperationError, "Failed to get socket option");
qCCritical(networking_webrtc) << "WebRTCSocket::getSocketOption() not implemented for option:" << option;
}
return QVariant();
}
bool WebRTCSocket::bind(const QHostAddress& address, quint16 port, QAbstractSocket::BindMode mode) {
// WebRTC data channels aren't bound to ports so just treat this as a successful operation.
auto wasBound = _isBound;
_isBound = _signalingServer.bind(address, port);
if (_isBound != wasBound) {
emit stateChanged(_isBound ? QAbstractSocket::BoundState : QAbstractSocket::UnconnectedState);
}
return _isBound;
}
QAbstractSocket::SocketState WebRTCSocket::state() const {
return _isBound ? QAbstractSocket::BoundState : QAbstractSocket::UnconnectedState;
}
void WebRTCSocket::abort() {
_dataChannels.reset();
}
qint64 WebRTCSocket::writeDatagram(const QByteArray& datagram, quint16 port) {
clearError();
if (_dataChannels.sendDataMessage(port, datagram)) {
return datagram.length();
}
setError(QAbstractSocket::SocketError::UnknownSocketError, "Failed to write datagram");
return -1;
}
qint64 WebRTCSocket::bytesToWrite(quint16 port) const {
return _dataChannels.getBufferedAmount(port);
}
bool WebRTCSocket::hasPendingDatagrams() const {
return _receivedQueue.length() > 0;
}
qint64 WebRTCSocket::pendingDatagramSize() const {
if (_receivedQueue.length() > 0) {
return _receivedQueue.head().second.length();
}
return -1;
}
qint64 WebRTCSocket::readDatagram(char* data, qint64 maxSize, QHostAddress* address, quint16* port) {
clearError();
if (_receivedQueue.length() > 0) {
auto datagram = _receivedQueue.dequeue();
auto length = std::min((qint64)datagram.second.length(), maxSize);
if (data) {
memcpy(data, datagram.second.constData(), length);
}
if (address) {
// WEBRTC TODO: Use signaling channel's remote WebSocket address? Or remote data channel address?
*address = QHostAddress::AnyIPv4;
}
if (port) {
*port = datagram.first;
}
return length;
}
setError(QAbstractSocket::SocketError::UnknownSocketError, "Failed to read datagram");
return -1;
}
QAbstractSocket::SocketError WebRTCSocket::error() const {
return _lastErrorType;
}
QString WebRTCSocket::errorString() const {
return _lastErrorString;
}
void WebRTCSocket::setError(QAbstractSocket::SocketError errorType, QString errorString) {
_lastErrorType = errorType;
}
void WebRTCSocket::clearError() {
_lastErrorType = QAbstractSocket::SocketError();
_lastErrorString = QString();
}
void WebRTCSocket::onDataChannelReceivedMessage(int dataChannelID, const QByteArray& message) {
_receivedQueue.enqueue(QPair<int, QByteArray>(dataChannelID, message));
emit readyRead();
}
#endif // WEBRTC_DATA_CHANNELS

View file

@ -0,0 +1,161 @@
//
// WebRTCSocket.h
// libraries/networking/src/webrtc
//
// Created by David Rowe on 21 Jun 2021.
// Copyright 2021 Vircadia contributors.
//
#ifndef vircadia_WebRTCSocket_h
#define vircadia_WebRTCSocket_h
#include <shared/WebRTC.h>
#if defined(WEBRTC_DATA_CHANNELS)
#include <QAbstractSocket>
#include <QObject>
#include <QQueue>
#include "WebRTCDataChannels.h"
#include "WebRTCSignalingServer.h"
/// @addtogroup Networking
/// @{
/// @brief Provides a QUdpSocket-style interface for using WebRTCDataChannels.
class WebRTCSocket : public QObject {
Q_OBJECT
public:
/// @brief Constructs a new WebRTCSocket object.
/// @param parent Qt parent object.
/// @param nodeType The type of node that the WebRTCsocket object is being used in.
WebRTCSocket(QObject* parent, NodeType_t nodeType);
/// @brief Nominally sets the value of a socket option.
/// @details Only <code>SendBufferSizeSocketOption</code> and <code>ReceiveBufferSizeSocketOption</code> options are handled
/// and for these no action is taken because these buffer sizes are not configurable in WebRTC.
/// Included for compatibility with the QUdpSocket interface.
/// @param option The socket option.
/// @param value The value of the socket option.
void setSocketOption(QAbstractSocket::SocketOption option, const QVariant& value);
/// @brief Nominally gets the value of a socket option.
/// @details Only <code>SendBufferSizeSocketOption</code> and <code>ReceiveBufferSizeSocketOption</code> options are handled
/// and for these only default values are returned because these buffer sizes are not configurable in WebRTC.
/// Included for compatibility with the QUdpSocket interface.
/// @param option The socket option.
/// @return The value of the socket option.
QVariant socketOption(QAbstractSocket::SocketOption option);
/// @brief Binds the WebRTC socket's signaling server to an address and port.
/// @details Note: WebRTC data connections aren't bound to an address or port. Their ports are negotiated as part of the
/// WebRTC peer connection process.
/// @param address The address to use for the signaling server.
/// @param port The port to use for the signaling server.
/// @param mode The bind mode. (Not used: included for compatibility with the QUdpSocket interface.)
/// @return <code>true</code> if the signaling server was successfully bound, <code>false</code> if it wasn't.
bool bind(const QHostAddress& address, quint16 port = 0, QAbstractSocket::BindMode mode
= QAbstractSocket::DefaultForPlatform);
/// @brief Gets the state of the socket.
/// @details In particular, QAbstractSocket::BoundState is returned if the socket is bound,
/// QAbstractSocket::UnconnectedState if it isn't.
/// @return The state of the socket.
QAbstractSocket::SocketState state() const;
/// @brief Immediately closes all connections and resets the socket.
void abort();
/// @brief Nominally gets the host port number.
/// @details
/// Included for compatibility with the QUdpSocket interface.
/// @return <code>0</code>
quint16 localPort() const { return 0; }
/// @brief Nominally gets the socket descriptor.
/// @details
/// Included for compatibility with the QUdpSocket interface.
/// @return <code>-1</code>
qintptr socketDescriptor() const { return -1; }
/// @brief Sends a datagram to the host on a data channel.
/// @param datagram The datagram to send.
/// @param port The data channel ID.
/// @return The number of bytes if successfully sent, otherwise <code>-1</code>.
qint64 writeDatagram(const QByteArray& datagram, quint16 port);
/// @brief Gets the number of bytes waiting to be written.
/// @param port The data channel ID.
/// @return The number of bytes waiting to be written.
qint64 bytesToWrite(quint16 port) const;
/// @brief Gets whether there's a datagram waiting to be read.
/// @return <code>true</code> if there's a datagram waiting to be read, <code>false</code> if there isn't.
bool hasPendingDatagrams() const;
/// @brief Gets the size of the first pending datagram.
/// @return the size of the first pending datagram; <code>-1</code> if there is no pending datagram.
qint64 pendingDatagramSize() const;
/// @brief Reads the next datagram, up to a maximum number of bytes.
/// @details Any remaining data in the datagram is lost.
/// @param data The destination to read the datagram into.
/// @param maxSize The maximum number of bytes to read.
/// @param address The destination to put the IP address that the datagram was read from. (Not currently set.)
/// @param port The destination to put the data channel ID that the datagram was read from.
/// @return The number of bytes read on success; <code>-1</code> if reading unsuccessful.
qint64 readDatagram(char* data, qint64 maxSize, QHostAddress* address = nullptr, quint16* port = nullptr);
/// @brief Gets the type of error that last occurred.
/// @return The type of error that last occurred.
QAbstractSocket::SocketError error() const;
/// @brief Gets the description of the error that last occurred.
/// @return The description of the error that last occurred.
QString errorString() const;
public slots:
/// @brief Handles the WebRTC data channel receiving a message.
/// @details Queues the message to be read via readDatagram.
/// @param dataChannelID The data channel that the message was received on.
/// @param message The message that was received.
void onDataChannelReceivedMessage(int dataChannelID, const QByteArray& message);
signals:
/// @brief Emitted when the state of the socket changes.
void stateChanged(QAbstractSocket::SocketState socketState);
/// @brief Emitted each time new data becomes available for reading.
void readyRead();
private:
void setError(QAbstractSocket::SocketError errorType, QString errorString);
void clearError();
WebRTCSignalingServer _signalingServer;
WebRTCDataChannels _dataChannels;
bool _isBound { false };
QQueue<QPair<int, QByteArray>> _receivedQueue; // Messages received are queued for reading from the "socket".
QAbstractSocket::SocketError _lastErrorType { QAbstractSocket::UnknownSocketError };
QString _lastErrorString;
};
/// @}
#endif // WEBRTC_DATA_CHANNELS
#endif // vircadia_WebRTCSocket_h

View file

@ -62,7 +62,7 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) :
const_cast<QLoggingCategory*>(&networking())->setEnabled(QtWarningMsg, false);
}
_stunSockAddr = HifiSockAddr(STUN_SERVER_HOSTNAME, STUN_SERVER_PORT, true);
_stunSockAddr = HifiSockAddr(SocketType::UDP, STUN_SERVER_HOSTNAME, STUN_SERVER_PORT, true);
_cacheSTUNResult = parser.isSet(cacheSTUNOption);
@ -79,7 +79,7 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) :
}
}
_iceServerAddr = HifiSockAddr("127.0.0.1", ICE_SERVER_DEFAULT_PORT);
_iceServerAddr = HifiSockAddr(SocketType::UDP, "127.0.0.1", ICE_SERVER_DEFAULT_PORT);
if (parser.isSet(iceServerAddressOption)) {
// parse the IP and port combination for this target
QString hostnamePortString = parser.value(iceServerAddressOption);
@ -96,7 +96,7 @@ ICEClientApp::ICEClientApp(int argc, char* argv[]) :
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
} else {
_iceServerAddr = HifiSockAddr(address, port);
_iceServerAddr = HifiSockAddr(SocketType::UDP, address, port);
}
}
@ -132,7 +132,7 @@ void ICEClientApp::openSocket() {
_socket = new udt::Socket();
unsigned int localPort = 0;
_socket->bind(QHostAddress::AnyIPv4, localPort);
_socket->bind(SocketType::UDP, QHostAddress::AnyIPv4, localPort);
_socket->setPacketHandler([this](std::unique_ptr<udt::Packet> packet) { processPacket(std::move(packet)); });
_socket->addUnfilteredHandler(_stunSockAddr,
[this](std::unique_ptr<udt::BasePacket> packet) {
@ -140,10 +140,10 @@ void ICEClientApp::openSocket() {
});
if (_verbose) {
qDebug() << "local port is" << _socket->localPort();
qDebug() << "local port is" << _socket->localPort(SocketType::UDP);
}
_localSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort());
_publicSockAddr = HifiSockAddr("127.0.0.1", _socket->localPort());
_localSockAddr = HifiSockAddr(SocketType::UDP, "127.0.0.1", _socket->localPort(SocketType::UDP));
_publicSockAddr = HifiSockAddr(SocketType::UDP, "127.0.0.1", _socket->localPort(SocketType::UDP));
_domainPingCount = 0;
}
@ -188,7 +188,7 @@ void ICEClientApp::doSomething() {
if (_verbose) {
qDebug() << "using cached STUN response";
}
_publicSockAddr.setPort(_socket->localPort());
_publicSockAddr.setPort(_socket->localPort(SocketType::UDP));
setState(talkToIceServer);
}
@ -303,7 +303,7 @@ void ICEClientApp::processSTUNResponse(std::unique_ptr<udt::BasePacket> packet)
uint16_t newPublicPort;
QHostAddress newPublicAddress;
if (LimitedNodeList::parseSTUNResponse(packet.get(), newPublicAddress, newPublicPort)) {
_publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort);
_publicSockAddr = HifiSockAddr(SocketType::UDP, newPublicAddress, newPublicPort);
if (_verbose) {
qDebug() << "My public address is" << _publicSockAddr;
}

@ -1 +1 @@
Subproject commit ab6c8b1a54aec359b1894f70722f69cfec7f04f1
Subproject commit 0afaa769d46683d461c9288aa31468f64cba0233