mirror of
https://github.com/JulianGro/overte.git
synced 2025-04-14 06:46:39 +02:00
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:
commit
dfd71ab18b
30 changed files with 1070 additions and 222 deletions
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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)); });
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 };
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
39
libraries/networking/src/SocketType.h
Normal file
39
libraries/networking/src/SocketType.h
Normal 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
|
|
@ -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]());
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
284
libraries/networking/src/udt/NetworkSocket.cpp
Normal file
284
libraries/networking/src/udt/NetworkSocket.cpp
Normal 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);
|
||||
}
|
163
libraries/networking/src/udt/NetworkSocket.h
Normal file
163
libraries/networking/src/udt/NetworkSocket.h
Normal 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
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
157
libraries/networking/src/webrtc/WebRTCSocket.cpp
Normal file
157
libraries/networking/src/webrtc/WebRTCSocket.cpp
Normal 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
|
161
libraries/networking/src/webrtc/WebRTCSocket.h
Normal file
161
libraries/networking/src/webrtc/WebRTCSocket.h
Normal 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
|
|
@ -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
|
Loading…
Reference in a new issue