diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 93abd8c759..b844615c6d 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -891,16 +891,7 @@ void DomainServer::routeWebRTCSignalingMessage(const QJsonObject& json) { if (json.value("to").toString() == NodeType::DomainServer) { emit webrtcSignalingMessageForDomainServer(json); } else { - // Insert the WebRTC data channel ID for the assignment client to use. - auto webrtcSocket = DependencyManager::get()->getWebRTCSocket(); - auto channelID = webrtcSocket->getDataChannelIDForWebSocket((quint16)json.value("from").toInt()); - if (channelID == 0 && json.value("echo").isUndefined()) { // Let echo messages through without a domain connection. - qCritical() << "WebRTC data channel ID not found for assignment client signaling!"; - return; - } - QJsonObject jsonModified = json; - jsonModified.insert("channel", QJsonValue(channelID)); - sendWebRTCSignalingMessageToAssignmentClient(jsonModified); + sendWebRTCSignalingMessageToAssignmentClient(json); } } diff --git a/libraries/networking/src/SockAddr.cpp b/libraries/networking/src/SockAddr.cpp index 61be5219f2..e8eb6c4b86 100644 --- a/libraries/networking/src/SockAddr.cpp +++ b/libraries/networking/src/SockAddr.cpp @@ -116,6 +116,10 @@ QString SockAddr::toString() const { return socketTypeToString(_socketType) + " " + _address.toString() + ":" + QString::number(_port); } +QString SockAddr::toShortString() const { + return _address.toString() + ":" + QString::number(_port); +} + bool SockAddr::hasPrivateAddress() const { // an address is private if it is loopback or falls in any of the RFC1918 address spaces const QPair TWENTY_FOUR_BIT_BLOCK = { QHostAddress("10.0.0.0"), 8 }; diff --git a/libraries/networking/src/SockAddr.h b/libraries/networking/src/SockAddr.h index 4e2ec89a50..877bae4ee4 100644 --- a/libraries/networking/src/SockAddr.h +++ b/libraries/networking/src/SockAddr.h @@ -56,6 +56,7 @@ public: static int unpackSockAddr(const unsigned char* packetData, SockAddr& unpackDestSockAddr); QString toString() const; + QString toShortString() const; bool hasPrivateAddress() const; // checks if the address behind this sock addr is private per RFC 1918 diff --git a/libraries/networking/src/udt/NetworkSocket.cpp b/libraries/networking/src/udt/NetworkSocket.cpp index 639e4bbb88..cc28cbfc73 100644 --- a/libraries/networking/src/udt/NetworkSocket.cpp +++ b/libraries/networking/src/udt/NetworkSocket.cpp @@ -133,7 +133,7 @@ qint64 NetworkSocket::writeDatagram(const QByteArray& datagram, const SockAddr& return _udpSocket.writeDatagram(datagram, sockAddr.getAddress(), sockAddr.getPort()); #if defined(WEBRTC_DATA_CHANNELS) case SocketType::WebRTC: - return _webrtcSocket.writeDatagram(datagram, sockAddr.getPort()); + return _webrtcSocket.writeDatagram(datagram, sockAddr); #endif default: qCCritical(networking) << "Socket type not specified in writeDatagram() address"; @@ -141,13 +141,13 @@ qint64 NetworkSocket::writeDatagram(const QByteArray& datagram, const SockAddr& } } -qint64 NetworkSocket::bytesToWrite(SocketType socketType, quint16 port) const { +qint64 NetworkSocket::bytesToWrite(SocketType socketType, const SockAddr& address) const { switch (socketType) { case SocketType::UDP: return _udpSocket.bytesToWrite(); #if defined(WEBRTC_DATA_CHANNELS) case SocketType::WebRTC: - return _webrtcSocket.bytesToWrite(port); + return _webrtcSocket.bytesToWrite(address); #endif default: qCCritical(networking) << "Socket type not specified in bytesToWrite()"; diff --git a/libraries/networking/src/udt/NetworkSocket.h b/libraries/networking/src/udt/NetworkSocket.h index 887e73b32b..030f27e119 100644 --- a/libraries/networking/src/udt/NetworkSocket.h +++ b/libraries/networking/src/udt/NetworkSocket.h @@ -80,9 +80,10 @@ public: /// @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. + /// @param address If a WebRTCSocket, the destination address for which to get the number of bytes waiting. + /// @param port If a WebRTC socket, the destination port 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; + qint64 bytesToWrite(SocketType socketType, const SockAddr& address = SockAddr()) const; /// @brief Gets whether there is a pending datagram waiting to be read. diff --git a/libraries/networking/src/udt/Socket.cpp b/libraries/networking/src/udt/Socket.cpp index 2aa5542651..721eafd7e7 100644 --- a/libraries/networking/src/udt/Socket.cpp +++ b/libraries/networking/src/udt/Socket.cpp @@ -256,7 +256,7 @@ qint64 Socket::writeDatagram(const QByteArray& datagram, const SockAddr& sockAdd } qint64 bytesWritten = _networkSocket.writeDatagram(datagram, sockAddr); - int pending = _networkSocket.bytesToWrite(socketType, sockAddr.getPort()); + int pending = _networkSocket.bytesToWrite(socketType, sockAddr); if (bytesWritten < 0 || pending) { int wsaError = 0; static std::atomic previousWsaError (0); diff --git a/libraries/networking/src/webrtc/WebRTCDataChannels.cpp b/libraries/networking/src/webrtc/WebRTCDataChannels.cpp index d3646508c8..769b1720df 100644 --- a/libraries/networking/src/webrtc/WebRTCDataChannels.cpp +++ b/libraries/networking/src/webrtc/WebRTCDataChannels.cpp @@ -131,13 +131,12 @@ void WDCDataChannelObserver::OnMessage(const DataBuffer& buffer) { } -WDCConnection::WDCConnection(WebRTCDataChannels* parent, quint16 webSocketID, int dataChannelID) : +WDCConnection::WDCConnection(WebRTCDataChannels* parent, const QString& dataChannelID) : _parent(parent), - _webSocketID(webSocketID), _dataChannelID(dataChannelID) { #ifdef WEBRTC_DEBUG - qCDebug(networking_webrtc) << "WDCConnection::WDCConnection() :" << webSocketID << dataChannelID; + qCDebug(networking_webrtc) << "WDCConnection::WDCConnection() :" << dataChannelID; #endif // Create observers. @@ -197,7 +196,7 @@ void WDCConnection::sendAnswer(SessionDescriptionInterface* description) { QJsonObject jsonObject; jsonObject.insert("from", QString(_parent->getNodeType())); - jsonObject.insert("to", _webSocketID); + jsonObject.insert("to", _dataChannelID); jsonObject.insert("data", jsonWebRTCPayload); _parent->sendSignalingMessage(jsonObject); @@ -251,7 +250,7 @@ void WDCConnection::sendIceCandidate(const IceCandidateInterface* candidate) { QJsonObject jsonObject; jsonObject.insert("from", QString(_parent->getNodeType())); - jsonObject.insert("to", _webSocketID); + jsonObject.insert("to", _dataChannelID); jsonObject.insert("data", jsonWebRTCData); QJsonDocument jsonDocument = QJsonDocument(jsonObject); @@ -328,7 +327,9 @@ void WDCConnection::onDataChannelMessageReceived(const DataBuffer& buffer) { #ifdef WEBRTC_DEBUG qCDebug(networking_webrtc) << "Echo message back"; #endif - _parent->sendDataMessage(_dataChannelID, byteArray); // Use parent method to exercise the code stack. + auto addressParts = _dataChannelID.split(":"); + auto address = SockAddr(SocketType::WebRTC, QHostAddress(addressParts[0]), addressParts[1].toInt()); + _parent->sendDataMessage(address, byteArray); // Use parent method to exercise the code stack. return; } @@ -420,58 +421,35 @@ WebRTCDataChannels::~WebRTCDataChannels() { } void WebRTCDataChannels::reset() { - QHashIterator i(_connectionsByDataChannel); + QHashIterator i(_connectionsByID); while (i.hasNext()) { i.next(); delete i.value(); } - _connectionsByWebSocket.clear(); - _connectionsByDataChannel.clear(); + _connectionsByID.clear(); } -quint16 WebRTCDataChannels::getNewDataChannelID() { - // The first data channel ID is 1. - static const int QUINT16_LIMIT = std::numeric_limits::max() + 1; - _lastDataChannelID = std::max((_lastDataChannelID + 1) % QUINT16_LIMIT, 1); -#ifdef WEBRTC_DEBUG - qCDebug(networking_webrtc) << "WebRTCDataChannels::getNewDataChannelID() :" << _lastDataChannelID; -#endif - return _lastDataChannelID; -} - -int WebRTCDataChannels::getDataChannelIDForWebSocket(quint16 webSocketID) const { -#ifdef WEBRTC_DEBUG - qCDebug(networking_webrtc) << "WebRTCDataChannels::getDataChannelIDForWebSocket() :" << webSocketID; -#endif - auto connection = _connectionsByWebSocket.value(webSocketID); - if (!connection) { - return 0; - } - return connection->getDataChannelID(); -} - -void WebRTCDataChannels::onDataChannelOpened(WDCConnection* connection, quint16 dataChannelID) { +void WebRTCDataChannels::onDataChannelOpened(WDCConnection* connection, const QString& dataChannelID) { #ifdef WEBRTC_DEBUG qCDebug(networking_webrtc) << "WebRTCDataChannels::onDataChannelOpened() :" << dataChannelID; #endif - _connectionsByDataChannel.insert(dataChannelID, connection); + _connectionsByID.insert(dataChannelID, connection); } void WebRTCDataChannels::onSignalingMessage(const QJsonObject& message) { #ifdef WEBRTC_DEBUG - qCDebug(networking_webrtc) << "WebRTCDataChannel::onSignalingMessage()" << message << message.value("channel"); + qCDebug(networking_webrtc) << "WebRTCDataChannel::onSignalingMessage()" << message; #endif // Validate message. const int MAX_DEBUG_DETAIL_LENGTH = 64; + const QRegularExpression DATA_CHANNEL_ID_REGEX{ "^[1-9]\\d*\\.\\d+\\.\\d+\\.\\d+:\\d+$" }; auto data = message.value("data").isObject() ? message.value("data").toObject() : QJsonObject(); - int from = message.value("from").isDouble() ? (quint16)(message.value("from").toInt()) : 0; + auto from = message.value("from").toString(); auto to = NodeType::fromChar(message.value("to").toString().at(0)); - int channel = message.value("channel").isDouble() ? (int)(message.value("channel").toInt()) : 0; - - if (from <= 0 || from > MAXUINT16 || to == NodeType::Unassigned || channel < 0 || channel > MAXUINT16 + if (!DATA_CHANNEL_ID_REGEX.match(from).hasMatch() || to == NodeType::Unassigned || !data.contains("description") && !data.contains("candidate")) { - qCWarning(networking_webrtc) << "Unexpected signaling message:" + qCWarning(networking_webrtc) << "Invalid or unexpected signaling message:" << QJsonDocument(message).toJson(QJsonDocument::Compact).left(MAX_DEBUG_DETAIL_LENGTH); return; } @@ -481,16 +459,11 @@ void WebRTCDataChannels::onSignalingMessage(const QJsonObject& message) { // Find or create a connection. WDCConnection* connection; - if (_connectionsByWebSocket.contains(from)) { - connection = _connectionsByWebSocket.value(from); + if (_connectionsByID.contains(from)) { + connection = _connectionsByID.value(from); } else { - // Assignment clients use the same data channel ID as the domain server, which is provided in the "channel" property. - // The domain server creates a new data channel ID. - if (channel == 0) { - channel = getNewDataChannelID(); - } - connection = new WDCConnection(this, from, channel); - _connectionsByWebSocket.insert(from, connection); + connection = new WDCConnection(this, from); + _connectionsByID.insert(from, connection); } // Set the remote description and reply with an answer. @@ -519,41 +492,41 @@ void WebRTCDataChannels::sendSignalingMessage(const QJsonObject& message) { emit signalingMessage(message); } -void WebRTCDataChannels::emitDataMessage(int dataChannelID, const QByteArray& byteArray) { +void WebRTCDataChannels::emitDataMessage(const QString& dataChannelID, const QByteArray& byteArray) { #ifdef WEBRTC_DEBUG qCDebug(networking_webrtc) << "WebRTCDataChannels::emitDataMessage() :" << dataChannelID << byteArray.toHex() << byteArray.length(); #endif - emit dataMessage(dataChannelID, byteArray); + auto addressParts = dataChannelID.split(":"); + auto address = SockAddr(SocketType::WebRTC, QHostAddress(addressParts[0]), addressParts[1].toInt()); + emit dataMessage(address, byteArray); } -bool WebRTCDataChannels::sendDataMessage(int dataChannelID, const QByteArray& byteArray) { +bool WebRTCDataChannels::sendDataMessage(const SockAddr& destination, const QByteArray& byteArray) { + auto dataChannelID = destination.toShortString(); #ifdef WEBRTC_DEBUG qCDebug(networking_webrtc) << "WebRTCDataChannels::sendDataMessage() :" << dataChannelID; #endif - // Find connection. - if (!_connectionsByDataChannel.contains(dataChannelID)) { + if (!_connectionsByID.contains(dataChannelID)) { qCWarning(networking_webrtc) << "Could not find WebRTC data channel to send message on!"; return false; } - auto connection = _connectionsByDataChannel.value(dataChannelID); + auto connection = _connectionsByID.value(dataChannelID); DataBuffer buffer(byteArray.toStdString(), true); 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; 0 if the channel doesn't exist. -qint64 WebRTCDataChannels::getBufferedAmount(int dataChannelID) const { - if (!_connectionsByDataChannel.contains(dataChannelID)) { +qint64 WebRTCDataChannels::getBufferedAmount(const SockAddr& address) const { + auto dataChannelID = address.toShortString(); + if (!_connectionsByID.contains(dataChannelID)) { #ifdef WEBRTC_DEBUG qCDebug(networking_webrtc) << "WebRTCDataChannels::getBufferedAmount() : Channel doesn't exist:" << dataChannelID; #endif return 0; } - auto connection = _connectionsByDataChannel.value(dataChannelID); + auto connection = _connectionsByID.value(dataChannelID); return connection->getBufferedAmount(); } @@ -600,10 +573,9 @@ void WebRTCDataChannels::closePeerConnectionNow(WDCConnection* connection) { // Delete the WDCConnection. #ifdef WEBRTC_DEBUG - qCDebug(networking_webrtc) << "Dispose of connection for channel ID:" << connection->getDataChannelID(); + qCDebug(networking_webrtc) << "Dispose of connection for channel:" << connection->getDataChannelID(); #endif - _connectionsByWebSocket.remove(connection->getWebSocketID()); - _connectionsByDataChannel.remove(connection->getDataChannelID()); + _connectionsByID.remove(connection->getDataChannelID()); delete connection; #ifdef WEBRTC_DEBUG qCDebug(networking_webrtc) << "Disposed of connection"; diff --git a/libraries/networking/src/webrtc/WebRTCDataChannels.h b/libraries/networking/src/webrtc/WebRTCDataChannels.h index dc8152ee34..fe8af77078 100644 --- a/libraries/networking/src/webrtc/WebRTCDataChannels.h +++ b/libraries/networking/src/webrtc/WebRTCDataChannels.h @@ -22,6 +22,7 @@ #define emit #include "../NodeType.h" +#include "../SockAddr.h" class WebRTCDataChannels; class WDCConnection; @@ -128,17 +129,12 @@ public: /// @brief Constructs a new WDCConnection and opens a WebRTC data connection. /// @param parent The parent WebRTCDataChannels object. - /// @param webSocketID The signaling channel that initiated the opening of the WebRTC data channel. - /// @param dataChannelID - The WebRTC data channel ID to assign to this connection. - WDCConnection(WebRTCDataChannels* parent, quint16 webSocketID, int dataChannelID); + /// @param dataChannelID The data channel ID. + WDCConnection(WebRTCDataChannels* parent, const QString& dataChannelID); - /// @brief Gets the WebSocket ID. - /// @return The ID of the WebSocket. - 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() const { return _dataChannelID; } + /// @brief Gets the data channel ID. + /// @return The data channel ID. + QString getDataChannelID() const { return _dataChannelID; } /// @brief Sets the remote session description received from the remote client via the signaling channel. @@ -160,7 +156,7 @@ public: /// @param data The ICE candidate. void addIceCandidate(QJsonObject& data); - /// @brief Sends an ICE candidate to the remote vlient via the signaling channel. + /// @brief Sends an ICE candidate to the remote client via the signaling channel. /// @param candidate The ICE candidate. void sendIceCandidate(const webrtc::IceCandidateInterface* candidate); @@ -195,8 +191,7 @@ public: private: WebRTCDataChannels* _parent; - quint16 _webSocketID { 0 }; - int _dataChannelID { -1 }; + QString _dataChannelID; rtc::scoped_refptr _setSessionDescriptionObserver { nullptr }; rtc::scoped_refptr _createSessionDescriptionObserver { nullptr }; @@ -221,6 +216,9 @@ private: /// Additionally, for debugging purposes, instead of containing a Vircadia protocol payload, a WebRTC message may be an echo /// request. This is bounced back to the client. /// +/// A WebRTC data channel is identified by the IP address and port of the client WebSocket that was used when opening the data +/// channel - this is considered to be the WebRTC data channel's address. The IP address and port of the actual WebRTC +/// connection is not used. class WebRTCDataChannels : public QObject { Q_OBJECT @@ -242,41 +240,30 @@ public: /// @brief Immediately closes all connections and resets the socket. void reset(); - /// @brief Gets a new data channel ID to uniquely identify a WDCConnection. - /// @details This ID is assigned by WebRTCDataChannels; it is not the WebRTC data channel ID because that is only - /// unique within a peer connection. - /// @return A new data channel ID. - quint16 getNewDataChannelID(); - - /// @brief Gets the data channel ID associated with a WebSocket. - /// @param webSocketID The WebSocket. - /// @return The data channel ID associated with the WebSocket if found, `0` if the WebSocket was not found. - int getDataChannelIDForWebSocket(quint16 webSocketID) const; - /// @brief Handles a WebRTC data channel opening. /// @param connection The WebRTC data channel connection. - /// @param dataChannelID The WebRTC data channel ID. - void onDataChannelOpened(WDCConnection* connection, quint16 dataChannelID); + /// @param dataChannelID The IP address and port of the signaling WebSocket that the client used to connect, `"n.n.n.n:n"`. + void onDataChannelOpened(WDCConnection* connection, const QString& dataChannelID); /// @brief Emits a signalingMessage to be sent to the Interface client. /// @param message The WebRTC signaling message to send. void sendSignalingMessage(const QJsonObject& message); /// @brief Emits a dataMessage received from the Interface client. - /// @param dataChannelID The WebRTC data channel the message was received on. + /// @param dataChannelID The IP address and port of the signaling WebSocket that the client used to connect, `"n.n.n.n:n"`. /// @param byteArray The data message received. - void emitDataMessage(int dataChannelID, const QByteArray& byteArray); + void emitDataMessage(const QString& dataChannelID, const QByteArray& byteArray); /// @brief Sends a data message to an Interface client. - /// @param dataChannelID The WebRTC channel ID of the Interface client. + /// @param dataChannelID The IP address and port of the signaling WebSocket that the client used to connect, `"n.n.n.n:n"`. /// @param message The data message to send. /// @return `true` if the data message was sent, otherwise `false`. - bool sendDataMessage(int dataChannelID, const QByteArray& message); + bool sendDataMessage(const SockAddr& destination, const QByteArray& message); /// @brief Gets the number of bytes waiting to be sent on a data channel. - /// @param dataChannelID The data channel ID. + /// @param address The address of the signaling WebSocket that the client used to connect. /// @return The number of bytes waiting to be sent on the data channel. - qint64 getBufferedAmount(int dataChannelID) const; + qint64 getBufferedAmount(const SockAddr& address) const; /// @brief Creates a new WebRTC peer connection for connecting to an Interface client. /// @param peerConnectionObserver An observer to monitor the WebRTC peer connection. @@ -311,9 +298,9 @@ signals: /// @brief A WebRTC data message received from the Interface client. /// @details This message is for handling at a higher level in the Vircadia protocol. - /// @param dataChannelID The WebRTC data channel ID. + /// @param address The address of the signaling WebSocket that the client used to connect. /// @param byteArray The Vircadia protocol message. - void dataMessage(int dataChannelID, const QByteArray& byteArray); + void dataMessage(const SockAddr& address, const QByteArray& byteArray); /// @brief Signals that the peer connection for a WebRTC data channel should be closed. /// @details Used by {@link WebRTCDataChannels.closePeerConnection}. @@ -332,10 +319,9 @@ private: rtc::scoped_refptr _peerConnectionFactory { nullptr }; - quint16 _lastDataChannelID { 0 }; - - QHash _connectionsByWebSocket; - QHash _connectionsByDataChannel; + QHash _connectionsByID; // + // The client's WebSocket IP and port is used as the data channel ID to uniquely identify each. + // The WebSocket IP address and port is formatted as "n.n.n.n:n", the same as used in WebRTCSignalingServer. }; diff --git a/libraries/networking/src/webrtc/WebRTCSignalingServer.cpp b/libraries/networking/src/webrtc/WebRTCSignalingServer.cpp index a11d09ea41..b35cd5158b 100644 --- a/libraries/networking/src/webrtc/WebRTCSignalingServer.cpp +++ b/libraries/networking/src/webrtc/WebRTCSignalingServer.cpp @@ -62,7 +62,8 @@ void WebRTCSignalingServer::webSocketTextMessageReceived(const QString& message) source->sendTextMessage(echo); } else { // WebRTC message or assignment client echo request. (Send both to target.) - json.insert("from", source->peerPort()); + auto from = source->peerAddress().toString() + ":" + QString::number(source->peerPort()); + json.insert("from", from); emit messageReceived(json); } } else { @@ -71,9 +72,9 @@ void WebRTCSignalingServer::webSocketTextMessageReceived(const QString& message) } void WebRTCSignalingServer::sendMessage(const QJsonObject& message) { - quint16 destinationPort = message.value("to").toInt(); - if (_webSockets.contains(destinationPort)) { - _webSockets.value(destinationPort)->sendTextMessage(QString(QJsonDocument(message).toJson())); + auto destinationAddress = message.value("to").toString(); + if (_webSockets.contains(destinationAddress)) { + _webSockets.value(destinationAddress)->sendTextMessage(QString(QJsonDocument(message).toJson())); } else { qCWarning(networking_webrtc) << "Failed to find WebSocket for outgoing WebRTC signaling message."; } @@ -82,7 +83,9 @@ void WebRTCSignalingServer::sendMessage(const QJsonObject& message) { void WebRTCSignalingServer::webSocketDisconnected() { auto source = qobject_cast(sender()); if (source) { - _webSockets.remove(source->peerPort()); + auto address = source->peerAddress().toString() + ":" + QString::number(source->peerPort()); + delete _webSockets.value(address); + _webSockets.remove(address); } } @@ -90,7 +93,8 @@ void WebRTCSignalingServer::newWebSocketConnection() { auto webSocket = _webSocketServer->nextPendingConnection(); connect(webSocket, &QWebSocket::textMessageReceived, this, &WebRTCSignalingServer::webSocketTextMessageReceived); connect(webSocket, &QWebSocket::disconnected, this, &WebRTCSignalingServer::webSocketDisconnected); - _webSockets.insert(webSocket->peerPort(), webSocket); + auto webSocketAddress = webSocket->peerAddress().toString() + ":" + QString::number(webSocket->peerPort()); + _webSockets.insert(webSocketAddress, webSocket); } #endif // WEBRTC_DATA_CHANNELS diff --git a/libraries/networking/src/webrtc/WebRTCSignalingServer.h b/libraries/networking/src/webrtc/WebRTCSignalingServer.h index 418becd8eb..fb695cc6fc 100644 --- a/libraries/networking/src/webrtc/WebRTCSignalingServer.h +++ b/libraries/networking/src/webrtc/WebRTCSignalingServer.h @@ -39,19 +39,20 @@ /// signaling `data` payload or an `echo` request: /// /// | Interface -> Server || -/// | -------- | -----------------------| -/// | `to` | NodeType | -/// | `from` | WebSocket port number* | -/// | [`data`] | WebRTC payload | -/// | [`echo`] | Echo request | -/// * The `from` field is filled in by the WebRTCSignalingServer. +/// | -------- | ---------------------------------------- | +/// | `to` | NodeType | +/// | `from` | WebSocket IP address & port, "n.n.n.n:n" | +/// | [`data`] | WebRTC signaling payload | +/// | [`echo`] | Echo request | +/// +/// `*` The `from` field is filled in upon receipt by the WebRTCSignalingServer. /// /// | Server -> Interface || -/// | -------- | --------------------- | -/// | `to` | WebSocket port number | -/// | `from` | NodeType | -/// | [`data`] | WebRTC payload | -/// | [`echo`] | Echo response | +/// | -------- | ---------------------------------------- | +/// | `to` | WebSocket IP address & port, "n.n.n.n:n" | +/// | `from` | NodeType | +/// | [`data`] | WebRTC signaling payload | +/// | [`echo`] | Echo response | /// class WebRTCSignalingServer : public QObject { Q_OBJECT @@ -97,7 +98,9 @@ private: QHostAddress _address; quint16 _port { 0 }; - QHash _webSockets; // client WebSocket port, client WebSocket object + QHash _webSockets; // + // The WebSocket IP address and port is formatted as "n.n.n.n:n". + // A QString is used rather than a SockAddr, to make signaling easier. QTimer* _isWebSocketServerListeningTimer; }; diff --git a/libraries/networking/src/webrtc/WebRTCSocket.cpp b/libraries/networking/src/webrtc/WebRTCSocket.cpp index 589d46a76e..e34dc5d5a2 100644 --- a/libraries/networking/src/webrtc/WebRTCSocket.cpp +++ b/libraries/networking/src/webrtc/WebRTCSocket.cpp @@ -78,17 +78,17 @@ void WebRTCSocket::abort() { } -qint64 WebRTCSocket::writeDatagram(const QByteArray& datagram, quint16 port) { +qint64 WebRTCSocket::writeDatagram(const QByteArray& datagram, const SockAddr& destination) { clearError(); - if (_dataChannels.sendDataMessage(port, datagram)) { + if (_dataChannels.sendDataMessage(destination, datagram)) { return datagram.length(); } setError(QAbstractSocket::SocketError::UnknownSocketError, "Failed to write datagram"); return -1; } -qint64 WebRTCSocket::bytesToWrite(quint16 port) const { - return _dataChannels.getBufferedAmount(port); +qint64 WebRTCSocket::bytesToWrite(const SockAddr& destination) const { + return _dataChannels.getBufferedAmount(destination); } @@ -114,12 +114,11 @@ qint64 WebRTCSocket::readDatagram(char* data, qint64 maxSize, QHostAddress* addr } if (address) { - // WEBRTC TODO: Use signaling channel's remote WebSocket address? Or remote data channel address? - *address = QHostAddress::AnyIPv4; + *address = datagram.first.getAddress(); } if (port) { - *port = datagram.first; + *port = datagram.first.getPort(); } return length; @@ -148,14 +147,9 @@ void WebRTCSocket::clearError() { } -void WebRTCSocket::onDataChannelReceivedMessage(int dataChannelID, const QByteArray& message) { - _receivedQueue.enqueue(QPair(dataChannelID, message)); +void WebRTCSocket::onDataChannelReceivedMessage(const SockAddr& source, const QByteArray& message) { + _receivedQueue.enqueue(QPair(source, message)); emit readyRead(); } - -int WebRTCSocket::getDataChannelIDForWebSocket(quint16 webSocketID) const { - return _dataChannels.getDataChannelIDForWebSocket(webSocketID); -} - #endif // WEBRTC_DATA_CHANNELS diff --git a/libraries/networking/src/webrtc/WebRTCSocket.h b/libraries/networking/src/webrtc/WebRTCSocket.h index 53c8921f02..04856f50f1 100644 --- a/libraries/networking/src/webrtc/WebRTCSocket.h +++ b/libraries/networking/src/webrtc/WebRTCSocket.h @@ -24,6 +24,10 @@ /// @brief Provides a QUdpSocket-style interface for using WebRTCDataChannels. +/// +/// @details A WebRTC data channel is identified by the IP address and port of the client WebSocket that was used when opening +/// the data channel - this is considered to be the WebRTC data channel's address. The IP address and port of the actual WebRTC +/// connection is not used. class WebRTCSocket : public QObject { Q_OBJECT @@ -81,16 +85,16 @@ public: qintptr socketDescriptor() const { return -1; } - /// @brief Sends a datagram to the host on a data channel. + /// @brief Sends a datagram. /// @param datagram The datagram to send. - /// @param port The data channel ID. + /// @param destination The destination WebRTC data channel address. /// @return The number of bytes if successfully sent, otherwise -1. - qint64 writeDatagram(const QByteArray& datagram, quint16 port); + qint64 writeDatagram(const QByteArray& datagram, const SockAddr& destination); /// @brief Gets the number of bytes waiting to be written. - /// @param port The data channel ID. + /// @param destination The destination WebRTC data channel address. /// @return The number of bytes waiting to be written. - qint64 bytesToWrite(quint16 port) const; + qint64 bytesToWrite(const SockAddr& destination) const; /// @brief Gets whether there's a datagram waiting to be read. /// @return true if there's a datagram waiting to be read, false if there isn't. @@ -104,8 +108,8 @@ public: /// @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. + /// @param address The destination to put the WebRTC data channel's IP address. + /// @param port The destination to put the WebRTC data channel's port. /// @return The number of bytes read on success; -1 if reading unsuccessful. qint64 readDatagram(char* data, qint64 maxSize, QHostAddress* address = nullptr, quint16* port = nullptr); @@ -118,19 +122,13 @@ public: /// @return The description of the error that last occurred. QString errorString() const; - - /// @brief Gets the data channel ID associated with a WebSocket. - /// @param webSocketID - /// @return The data channel ID associated with the WebSocket if found, `0` if the WebSocket was not found. - int getDataChannelIDForWebSocket(quint16 webSocketID) 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 source The WebRTC data channel that the message was received on. /// @param message The message that was received. - void onDataChannelReceivedMessage(int dataChannelID, const QByteArray& message); + void onDataChannelReceivedMessage(const SockAddr& source, const QByteArray& message); signals: @@ -159,7 +157,7 @@ private: bool _isBound { false }; - QQueue> _receivedQueue; // Messages received are queued for reading from the "socket". + QQueue> _receivedQueue; // Messages received are queued for reading from the "socket". QAbstractSocket::SocketError _lastErrorType { QAbstractSocket::UnknownSocketError }; QString _lastErrorString;