Merge pull request #1313 from ctrlaltdavid/dev/webrtc-assignment-clients

Assignment client WebRTC connections
This commit is contained in:
Kalila 2021-08-31 19:17:17 -04:00 committed by GitHub
commit 211fd474fe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 281 additions and 55 deletions

View file

@ -14,6 +14,7 @@
#include <assert.h>
#include <QJsonDocument>
#include <QProcess>
#include <QSharedMemory>
#include <QThread>
@ -125,6 +126,18 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri
PacketReceiver::makeUnsourcedListenerReference<AssignmentClient>(this, &AssignmentClient::handleCreateAssignmentPacket));
packetReceiver.registerListener(PacketType::StopNode,
PacketReceiver::makeUnsourcedListenerReference<AssignmentClient>(this, &AssignmentClient::handleStopNodePacket));
#if defined(WEBRTC_DATA_CHANNELS)
auto webrtcSocket = nodeList->getWebRTCSocket();
// Route inbound WebRTC signaling messages from the Domain Server.
packetReceiver.registerListener(PacketType::WebRTCSignaling,
PacketReceiver::makeUnsourcedListenerReference<AssignmentClient>(this, &AssignmentClient::handleWebRTCSignalingPacket));
connect(this, &AssignmentClient::webrtcSignalingMessageFromUserClient, webrtcSocket, &WebRTCSocket::onSignalingMessage);
// Route outbound WebRTC signaling messages via the Domain Server to the user client.
connect(webrtcSocket, &WebRTCSocket::sendSignalingMessage, this, &AssignmentClient::sendSignalingMessageToUserClient);
#endif
}
void AssignmentClient::stopAssignmentClient() {
@ -333,3 +346,45 @@ void AssignmentClient::assignmentCompleted() {
_isAssigned = false;
}
#if defined(WEBRTC_DATA_CHANNELS)
void AssignmentClient::handleWebRTCSignalingPacket(QSharedPointer<ReceivedMessage> message) {
auto messageString = message->readString();
auto json = QJsonDocument::fromJson(messageString.toUtf8()).object();
if (json.keys().contains("echo")) {
// Echo message back to sender.
if (!json.keys().contains("to") || !json.keys().contains("from")) {
qCDebug(assignment_client) << "Invalid WebRTC signaling echo message received.";
return;
}
// Swap to/from.
auto to = json.value("to");
json.insert("to", json.value("from"));
json.insert("from", to);
// Send back to sender via the Domain Server.
auto packetList = NLPacketList::create(PacketType::WebRTCSignaling, QByteArray(), true, true);
packetList->writeString(QJsonDocument(json).toJson(QJsonDocument::Compact));
auto nodeList = DependencyManager::get<NodeList>();
auto domainServerAddress = nodeList->getDomainHandler().getSockAddr();
nodeList->sendPacketList(std::move(packetList), domainServerAddress);
} else {
// WebRTC signaling message.
emit webrtcSignalingMessageFromUserClient(json);
}
}
// Sends a signaling message from the assignment client to the user client via the Domain Server.
void AssignmentClient::sendSignalingMessageToUserClient(const QJsonObject& json) {
auto packetList = NLPacketList::create(PacketType::WebRTCSignaling, QByteArray(), true, true);
packetList->writeString(QJsonDocument(json).toJson(QJsonDocument::Compact));
auto nodeList = DependencyManager::get<NodeList>();
auto domainServerAddress = nodeList->getDomainHandler().getSockAddr();
nodeList->sendPacketList(std::move(packetList), domainServerAddress);
}
#endif

View file

@ -16,6 +16,8 @@
#include <QtCore/QCoreApplication>
#include <QtCore/QPointer>
#include <shared/WebRTC.h>
#include "ThreadedAssignment.h"
class QSharedMemory;
@ -29,19 +31,26 @@ public:
quint16 assignmentMonitorPort);
~AssignmentClient();
public slots:
void aboutToQuit();
private slots:
void sendAssignmentRequest();
void assignmentCompleted();
void handleAuthenticationRequest();
void sendStatusPacketToACM();
void stopAssignmentClient();
public slots:
void aboutToQuit();
private slots:
void handleCreateAssignmentPacket(QSharedPointer<ReceivedMessage> message);
void handleStopNodePacket(QSharedPointer<ReceivedMessage> message);
#if defined(WEBRTC_DATA_CHANNELS)
void handleWebRTCSignalingPacket(QSharedPointer<ReceivedMessage> message);
void sendSignalingMessageToUserClient(const QJsonObject& json);
#endif
signals:
#if defined(WEBRTC_DATA_CHANNELS)
void webrtcSignalingMessageFromUserClient(const QJsonObject& json);
#endif
private:
void setUpStatusToMonitor();

View file

@ -70,7 +70,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>(NodeType::Unassigned, listenPort);
auto nodeList = DependencyManager::set<LimitedNodeList>(listenPort);
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
packetReceiver.registerListener(PacketType::AssignmentClientStatus,

View file

@ -167,6 +167,10 @@ DomainServer::DomainServer(int argc, char* argv[]) :
_gatekeeper(this),
_httpManager(QHostAddress::AnyIPv4, DOMAIN_SERVER_HTTP_PORT,
QString("%1/resources/web/").arg(QCoreApplication::applicationDirPath()), this)
#if defined(WEBRTC_DATA_CHANNELS)
,
_webrtcSignalingServer(this)
#endif
{
if (_parentPID != -1) {
watchParentProcess(_parentPID);
@ -248,6 +252,10 @@ DomainServer::DomainServer(int argc, char* argv[]) :
updateDownstreamNodes();
updateUpstreamNodes();
#if defined(WEBRTC_DATA_CHANNELS)
setUpWebRTCSignalingServer();
#endif
if (_type != NonMetaverse) {
// if we have a metaverse domain, we'll use an access token for API calls
resetAccountManagerAccessToken();
@ -731,7 +739,7 @@ void DomainServer::setupNodeListAndAssignments() {
// check for scripts the user wants to persist from their domain-server config
populateStaticScriptedAssignmentsFromSettings();
auto nodeList = DependencyManager::set<LimitedNodeList>(NodeType::DomainServer, domainServerPort, domainServerDTLSPort);
auto nodeList = DependencyManager::set<LimitedNodeList>(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,
@ -846,6 +854,72 @@ void DomainServer::setupNodeListAndAssignments() {
addStaticAssignmentsToQueue();
}
#if defined(WEBRTC_DATA_CHANNELS)
// Sets up the WebRTC signaling server that's hosted by the domain server.
void DomainServer::setUpWebRTCSignalingServer() {
// Bind the WebRTC signaling server's WebSocket to its port.
bool isBound = _webrtcSignalingServer.bind(QHostAddress::AnyIPv4, DEFAULT_DOMAIN_SERVER_WS_PORT);
if (!isBound) {
qWarning() << "WebRTC signaling server not bound to port. WebRTC connections are not supported.";
return;
}
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
// Route inbound WebRTC signaling messages received from user clients.
connect(&_webrtcSignalingServer, &WebRTCSignalingServer::messageReceived,
this, &DomainServer::routeWebRTCSignalingMessage);
// Route domain server signaling messages.
auto webrtcSocket = limitedNodeList->getWebRTCSocket();
connect(this, &DomainServer::webrtcSignalingMessageForDomainServer, webrtcSocket, &WebRTCSocket::onSignalingMessage);
connect(webrtcSocket, &WebRTCSocket::sendSignalingMessage, &_webrtcSignalingServer, &WebRTCSignalingServer::sendMessage);
// Forward signaling messages received from assignment clients to user client.
PacketReceiver& packetReceiver = limitedNodeList->getPacketReceiver();
packetReceiver.registerListener(PacketType::WebRTCSignaling,
PacketReceiver::makeUnsourcedListenerReference<DomainServer>(this,
&DomainServer::forwardAssignmentClientSignalingMessageToUserClient));
connect(this, &DomainServer::webrtcSignalingMessageForUserClient,
&_webrtcSignalingServer, &WebRTCSignalingServer::sendMessage);
}
// Routes an inbound WebRTC signaling message received from a client app to the appropriate recipient.
void DomainServer::routeWebRTCSignalingMessage(const QJsonObject& json) {
if (json.value("to").toString() == NodeType::DomainServer) {
emit webrtcSignalingMessageForDomainServer(json);
} else {
sendWebRTCSignalingMessageToAssignmentClient(json);
}
}
// Sends a WebRTC signaling message to the target AC contained in the message.
void DomainServer::sendWebRTCSignalingMessageToAssignmentClient(const QJsonObject& json) {
NodeType_t destinationNodeType = NodeType::fromChar(json.value("to").toString().at(0));
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
auto destinationNode = limitedNodeList->soloNodeOfType(destinationNodeType);
if (!destinationNode) {
qWarning() << NodeType::getNodeTypeName(destinationNodeType) << "not found for WebRTC signaling message.";
return;
}
// Use an NLPacketList because the signaling message is not necessarily small.
auto packetList = NLPacketList::create(PacketType::WebRTCSignaling, QByteArray(), true, true);
packetList->writeString(QJsonDocument(json).toJson(QJsonDocument::Compact));
limitedNodeList->sendPacketList(std::move(packetList), *destinationNode);
}
// Forwards a WebRTC signaling message received from an assignment client to the relevant user client.
void DomainServer::forwardAssignmentClientSignalingMessageToUserClient(QSharedPointer<ReceivedMessage> message) {
auto messageString = message->readString();
auto json = QJsonDocument::fromJson(messageString.toUtf8()).object();
emit webrtcSignalingMessageForUserClient(json);
}
#endif
bool DomainServer::resetAccountManagerAccessToken() {
if (!_oauthProviderURL.isEmpty()) {
// check for an access-token in our settings, can optionally be overidden by env value

View file

@ -27,6 +27,8 @@
#include <Assignment.h>
#include <HTTPSConnection.h>
#include <LimitedNodeList.h>
#include <shared/WebRTC.h>
#include <webrtc/WebRTCSignalingServer.h>
#include "AssetsBackupHandler.h"
#include "DomainGatekeeper.h"
@ -148,6 +150,10 @@ private slots:
void tokenGrantFinished();
void profileRequestFinished();
#if defined(WEBRTC_DATA_CHANNELS)
void forwardAssignmentClientSignalingMessageToUserClient(QSharedPointer<ReceivedMessage> message);
#endif
void aboutToQuit();
signals:
@ -155,6 +161,12 @@ signals:
void userConnected();
void userDisconnected();
#if defined(WEBRTC_DATA_CHANNELS)
void webrtcSignalingMessageForDomainServer(const QJsonObject& json);
void webrtcSignalingMessageForUserClient(const QJsonObject& json);
#endif
private:
QUuid getID();
@ -235,6 +247,12 @@ private:
std::initializer_list<QString> optionalData = { },
bool requireAccessToken = true);
#if defined(WEBRTC_DATA_CHANNELS)
void setUpWebRTCSignalingServer();
void routeWebRTCSignalingMessage(const QJsonObject& json);
void sendWebRTCSignalingMessageToAssignmentClient(const QJsonObject& json);
#endif
QString operationToString(const QNetworkAccessManager::Operation &op);
SubnetList _acSubnetWhitelist;
@ -312,6 +330,10 @@ private:
std::unordered_map<int, std::unique_ptr<QTemporaryFile>> _pendingContentFiles;
QThread _assetClientThread;
#if defined(WEBRTC_DATA_CHANNELS)
WebRTCSignalingServer _webrtcSignalingServer;
#endif
};

View file

@ -16,6 +16,7 @@
#include <QtCore/QDataStream>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QTimer>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QNetworkRequest>

View file

@ -3,14 +3,16 @@
// libraries/networking/src
//
// Copyright 2017 High Fidelity, Inc.
// Copyright 2021 Vircadia contributors.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#include "BaseAssetScriptingInterface.h"
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QMimeDatabase>
#include <QThread>

View file

@ -50,8 +50,8 @@ 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(char ownerType, int socketListenPort, int dtlsListenPort) :
_nodeSocket(this, true, ownerType),
LimitedNodeList::LimitedNodeList(int socketListenPort, int dtlsListenPort) :
_nodeSocket(this, true),
_packetReceiver(new PacketReceiver(this))
{
qRegisterMetaType<ConnectionStep>("ConnectionStep");
@ -74,7 +74,7 @@ LimitedNodeList::LimitedNodeList(char ownerType, int socketListenPort, int dtlsL
qCDebug(networking) << "NodeList DTLS socket is listening on" << _dtlsSocket->localPort();
}
_nodeSocket.bind(SocketType::WebRTC, QHostAddress::AnyIPv4, DEFAULT_DOMAIN_SERVER_WS_PORT);
_nodeSocket.bind(SocketType::WebRTC, QHostAddress::AnyIPv4);
// check for local socket updates every so often
const int LOCAL_SOCKET_UPDATE_INTERVAL_MSECS = 5 * 1000;
@ -241,6 +241,12 @@ QUdpSocket& LimitedNodeList::getDTLSSocket() {
return *_dtlsSocket;
}
#if defined(WEBRTC_DATA_CHANNELS)
const WebRTCSocket* LimitedNodeList::getWebRTCSocket() {
return _nodeSocket.getWebRTCSocket();
}
#endif
bool LimitedNodeList::isPacketVerifiedWithSource(const udt::Packet& packet, Node* sourceNode) {
// We track bandwidth when doing packet verification to avoid needing to do a node lookup
// later when we already do it in packetSourceAndHashMatchAndTrackBandwidth. A node lookup

View file

@ -37,7 +37,6 @@
#include <DependencyManager.h>
#include <SharedUtil.h>
#include "DomainHandler.h"
#include "NetworkingConstants.h"
#include "Node.h"
#include "NLPacket.h"
@ -139,6 +138,10 @@ public:
Q_INVOKABLE void setSocketLocalPort(SocketType socketType, quint16 socketLocalPort);
QUdpSocket& getDTLSSocket();
#if defined(WEBRTC_DATA_CHANNELS)
const WebRTCSocket* getWebRTCSocket();
#endif
PacketReceiver& getPacketReceiver() { return *_packetReceiver; }
@ -413,8 +416,7 @@ protected:
QUuid connectionSecretUUID;
};
LimitedNodeList(char ownerType = NodeType::DomainServer, int socketListenPort = INVALID_PORT,
int dtlsListenPort = INVALID_PORT);
LimitedNodeList(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

View file

@ -117,7 +117,7 @@ void NetworkPeer::setActiveSocket(SockAddr* discoveredSocket) {
// we have an active socket, stop our ping timer
stopPingTimer();
// we're now considered connected to this peer - reset the number of connection attemps
// we're now considered connected to this peer - reset the number of connection attempts
resetConnectionAttempts();
if (_activeSocket) {

View file

@ -46,6 +46,22 @@ static const QHash<NodeType_t, QString> TYPE_NAME_HASH {
{ NodeType::Unassigned, "Unassigned" }
};
static const QHash<NodeType_t, QString> TYPE_CHAR_HASH {
{ NodeType::DomainServer, "D" },
{ NodeType::EntityServer, "o" },
{ NodeType::Agent, "I" },
{ NodeType::AudioMixer, "M" },
{ NodeType::AvatarMixer, "W" },
{ NodeType::AssetServer, "A" },
{ NodeType::MessagesMixer, "m" },
{ NodeType::EntityScriptServer, "S" },
{ NodeType::UpstreamAudioMixer, "B" },
{ NodeType::UpstreamAvatarMixer, "C" },
{ NodeType::DownstreamAudioMixer, "a" },
{ NodeType::DownstreamAvatarMixer, "w" },
{ NodeType::Unassigned, QChar(1) }
};
const QString& NodeType::getNodeTypeName(NodeType_t nodeType) {
const auto matchedTypeName = TYPE_NAME_HASH.find(nodeType);
return matchedTypeName != TYPE_NAME_HASH.end() ? matchedTypeName.value() : UNKNOWN_NodeType_t_NAME;
@ -85,6 +101,9 @@ NodeType_t NodeType::fromString(QString type) {
return TYPE_NAME_HASH.key(type, NodeType::Unassigned);
}
NodeType_t NodeType::fromChar(QChar type) {
return TYPE_CHAR_HASH.key(type, NodeType::Unassigned);
}
Node::Node(const QUuid& uuid, NodeType_t type, const SockAddr& publicSocket,
const SockAddr& localSocket, QObject* parent) :

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(newOwnerType, socketListenPort, dtlsListenPort),
LimitedNodeList(socketListenPort, dtlsListenPort),
_ownerType(newOwnerType),
_nodeTypesOfInterest(),
_domainHandler(this),

View file

@ -154,7 +154,7 @@ private slots:
void maybeSendIgnoreSetToNode(SharedNodePointer node);
private:
NodeList() : LimitedNodeList(NodeType::Unassigned, INVALID_PORT, INVALID_PORT) {
NodeList() : LimitedNodeList(INVALID_PORT, INVALID_PORT) {
assert(false); // Not implemented, needed for DependencyManager templates compile
}
NodeList(char ownerType, int socketListenPort = INVALID_PORT, int dtlsListenPort = INVALID_PORT);

View file

@ -43,6 +43,7 @@ namespace NodeType {
NodeType_t downstreamType(NodeType_t primaryType);
NodeType_t fromString(QString type);
NodeType_t fromChar(QChar type);
}
typedef QSet<NodeType_t> NodeSet;

View file

@ -11,13 +11,13 @@
#include "../NetworkLogging.h"
NetworkSocket::NetworkSocket(QObject* parent, NodeType_t nodeType) :
NetworkSocket::NetworkSocket(QObject* parent) :
QObject(parent),
_parent(parent),
_udpSocket(this)
#if defined(WEBRTC_DATA_CHANNELS)
,
_webrtcSocket(this, nodeType)
_webrtcSocket(this)
#endif
{
connect(&_udpSocket, &QUdpSocket::readyRead, this, &NetworkSocket::readyRead);
@ -267,6 +267,13 @@ QString NetworkSocket::errorString(SocketType socketType) const {
}
#if defined(WEBRTC_DATA_CHANNELS)
const WebRTCSocket* NetworkSocket::getWebRTCSocket() {
return &_webrtcSocket;
}
#endif
void NetworkSocket::onUDPStateChanged(QAbstractSocket::SocketState socketState) {
emit stateChanged(SocketType::UDP, socketState);
}

View file

@ -33,8 +33,7 @@ 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);
NetworkSocket(QObject* parent);
/// @brief Set the value of a UDP or WebRTC socket option.
@ -91,7 +90,7 @@ public:
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.
/// @return The size of the next pending datagram.
qint64 pendingDatagramSize();
/// @brief Reads the next datagram per the most recent pendingDatagramSize call if made, otherwise alternating between
@ -111,7 +110,7 @@ public:
/// @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
/// @return The type of error that last occurred.
QAbstractSocket::SocketError error(SocketType socketType) const;
/// @brief Gets the description of the error that last occurred.
@ -119,6 +118,13 @@ public:
/// @return The description of the error that last occurred.
QString errorString(SocketType socketType) const;
#if defined(WEBRTC_DATA_CHANNELS)
/// @brief Gets a pointer to the WebRTC socket object.
/// @return A pointer to the WebRTC socket object.
const WebRTCSocket* getWebRTCSocket();
#endif
signals:
/// @brief Emitted each time new data becomes available for reading.

View file

@ -139,6 +139,7 @@ public:
BulkAvatarTraitsAck,
StopInjector,
AvatarZonePresence,
WebRTCSignaling,
NUM_PACKET_TYPE
};
@ -190,7 +191,7 @@ public:
<< PacketTypeEnum::Value::ReplicatedMicrophoneAudioWithEcho << PacketTypeEnum::Value::ReplicatedInjectAudio
<< PacketTypeEnum::Value::ReplicatedSilentAudioFrame << PacketTypeEnum::Value::ReplicatedAvatarIdentity
<< PacketTypeEnum::Value::ReplicatedKillAvatar << PacketTypeEnum::Value::ReplicatedBulkAvatarData
<< PacketTypeEnum::Value::AvatarZonePresence;
<< PacketTypeEnum::Value::AvatarZonePresence << PacketTypeEnum::Value::WebRTCSignaling;
return NON_SOURCED_PACKETS;
}

View file

@ -40,9 +40,9 @@ using namespace udt;
#endif
Socket::Socket(QObject* parent, bool shouldChangeSocketOptions, NodeType_t nodeType) :
Socket::Socket(QObject* parent, bool shouldChangeSocketOptions) :
QObject(parent),
_networkSocket(parent, nodeType),
_networkSocket(parent),
_readyReadBackupTimer(new QTimer(this)),
_shouldChangeSocketOptions(shouldChangeSocketOptions)
{
@ -91,6 +91,12 @@ void Socket::rebind(SocketType socketType, quint16 localPort) {
bind(socketType, QHostAddress::AnyIPv4, localPort);
}
#if defined(WEBRTC_DATA_CHANNELS)
const WebRTCSocket* Socket::getWebRTCSocket() {
return _networkSocket.getWebRTCSocket();
}
#endif
void Socket::setSystemBufferSizes(SocketType socketType) {
for (int i = 0; i < 2; i++) {
QAbstractSocket::SocketOption bufferOpt;

View file

@ -55,8 +55,8 @@ class Socket : public QObject {
public:
using StatsVector = std::vector<std::pair<SockAddr, ConnectionStats::Stats>>;
Socket(QObject* object = 0, bool shouldChangeSocketOptions = true, NodeType_t nodeType = NodeType::Unassigned);
Socket(QObject* object = 0, bool shouldChangeSocketOptions = true);
quint16 localPort(SocketType socketType) const { return _networkSocket.localPort(socketType); }
@ -90,6 +90,10 @@ public:
StatsVector sampleStatsForAllConnections();
#if defined(WEBRTC_DATA_CHANNELS)
const WebRTCSocket* getWebRTCSocket();
#endif
#if (PR_BUILD || DEV_BUILD)
void sendFakedHandshakeRequest(const SockAddr& sockAddr);
#endif

View file

@ -373,13 +373,12 @@ void WDCConnection::closePeerConnection() {
}
WebRTCDataChannels::WebRTCDataChannels(QObject* parent, NodeType_t nodeType) :
WebRTCDataChannels::WebRTCDataChannels(QObject* parent) :
QObject(parent),
_parent(parent),
_nodeType(nodeType)
_parent(parent)
{
#ifdef WEBRTC_DEBUG
qCDebug(networking_webrtc) << "WebRTCDataChannels::WebRTCDataChannels()" << nodeType << NodeType::getNodeTypeName(nodeType);
qCDebug(networking_webrtc) << "WebRTCDataChannels::WebRTCDataChannels()";
#endif
// Create a peer connection factory.
@ -452,12 +451,18 @@ void WebRTCDataChannels::onSignalingMessage(const QJsonObject& message) {
const int MAX_DEBUG_DETAIL_LENGTH = 64;
auto data = message.value("data").isObject() ? message.value("data").toObject() : QJsonObject();
int from = message.value("from").isDouble() ? (quint16)(message.value("from").toInt()) : 0;
if (from <= 0 || from > MAXUINT16 || !data.contains("description") && !data.contains("candidate")) {
auto to = NodeType::fromChar(message.value("to").toString().at(0));
if (from <= 0 || from > MAXUINT16 || to == NodeType::Unassigned
|| !data.contains("description") && !data.contains("candidate")) {
qCWarning(networking_webrtc) << "Unexpected signaling message:"
<< QJsonDocument(message).toJson(QJsonDocument::Compact).left(MAX_DEBUG_DETAIL_LENGTH);
return;
}
// Remember this node's type for the reply.
_nodeType = to;
// Find or create a connection.
WDCConnection* connection;
if (_connectionsByWebSocket.contains(from)) {

View file

@ -227,8 +227,7 @@ public:
/// @brief Constructs a new WebRTCDataChannels object.
/// @param parent The parent Qt object.
/// @param nodeType The type of node that the WebRTCDataChannels object is being used in.
WebRTCDataChannels(QObject* parent, NodeType_t nodeType);
WebRTCDataChannels(QObject* parent);
/// @brief Destroys a WebRTCDataChannels object.
~WebRTCDataChannels();
@ -319,7 +318,7 @@ private:
QObject* _parent;
NodeType_t _nodeType;
NodeType_t _nodeType { NodeType::Unassigned };
std::unique_ptr<rtc::Thread> _rtcNetworkThread { nullptr };
std::unique_ptr<rtc::Thread> _rtcWorkerThread { nullptr };

View file

@ -10,20 +10,19 @@
#if defined(WEBRTC_DATA_CHANNELS)
#include <QHostAddress>
#include "../NetworkLogging.h"
#include "../udt/Constants.h"
WebRTCSocket::WebRTCSocket(QObject* parent, NodeType_t nodeType) :
WebRTCSocket::WebRTCSocket(QObject* parent) :
QObject(parent),
_signalingServer(this /*, QHostAddress::AnyIPv4, DEFAULT_DOMAIN_SERVER_WS_PORT*/),
_dataChannels(this, nodeType)
_dataChannels(this)
{
// Connect WebRTC signaling server and data channels.
connect(&_signalingServer, &WebRTCSignalingServer::messageReceived,
&_dataChannels, &WebRTCDataChannels::onSignalingMessage);
connect(&_dataChannels, &WebRTCDataChannels::signalingMessage,
&_signalingServer, &WebRTCSignalingServer::sendMessage);
// Route signaling messages.
connect(this, &WebRTCSocket::onSignalingMessage, &_dataChannels, &WebRTCDataChannels::onSignalingMessage);
connect(&_dataChannels, &WebRTCDataChannels::signalingMessage, this, &WebRTCSocket::sendSignalingMessage);
// Route received data channel messages.
connect(&_dataChannels, &WebRTCDataChannels::dataMessage, this, &WebRTCSocket::onDataChannelReceivedMessage);
@ -63,7 +62,7 @@ QVariant WebRTCSocket::socketOption(QAbstractSocket::SocketOption option) {
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);
_isBound = true;
if (_isBound != wasBound) {
emit stateChanged(_isBound ? QAbstractSocket::BoundState : QAbstractSocket::UnconnectedState);
}

View file

@ -18,7 +18,6 @@
#include <QQueue>
#include "WebRTCDataChannels.h"
#include "WebRTCSignalingServer.h"
/// @addtogroup Networking
/// @{
@ -32,8 +31,7 @@ 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);
WebRTCSocket(QObject* parent);
/// @brief Nominally sets the value of a socket option.
@ -52,13 +50,14 @@ public:
/// @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
/// @brief Nominally binds the WebRTC socket to an address and port.
/// @details WebRTC data connections aren't actually 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.
/// Included for compatibility with the QUdpSocket interface.
/// @param address The address.
/// @param port The port.
/// @param mode The bind mode.
/// @return <code>true</code>.
bool bind(const QHostAddress& address, quint16 port = 0, QAbstractSocket::BindMode mode
= QAbstractSocket::DefaultForPlatform);
@ -132,17 +131,26 @@ public slots:
signals:
/// @brief Emitted when the state of the socket changes.
/// @param socketState The new state of the socket.
void stateChanged(QAbstractSocket::SocketState socketState);
/// @brief Emitted each time new data becomes available for reading.
void readyRead();
/// @brief Emitted when a WebRTC signaling message has been received from the signaling server for this WebRTCSocket.
/// @param json The signaling message.
void onSignalingMessage(const QJsonObject& json);
/// @brief Emitted when there's a WebRTC signaling message to send via the signaling server.
/// @param json The signaling message.
void sendSignalingMessage(const QJsonObject& message);
private:
void setError(QAbstractSocket::SocketError errorType, QString errorString);
void clearError();
WebRTCSignalingServer _signalingServer;
WebRTCDataChannels _dataChannels;
bool _isBound { false };