From 69f1cd80e6eea8e0b974f3bb931a8646e2293ef3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 22 May 2015 16:29:01 -0700 Subject: [PATCH 01/33] immediately send ICE heartbeat once data present --- interface/src/DatagramProcessor.cpp | 1 + libraries/audio-client/src/AudioClient.cpp | 241 ++++++++++---------- libraries/networking/src/AddressManager.cpp | 4 + libraries/networking/src/DomainHandler.cpp | 21 ++ libraries/networking/src/DomainHandler.h | 3 + libraries/networking/src/NodeList.cpp | 15 ++ libraries/networking/src/NodeList.h | 3 +- 7 files changed, 167 insertions(+), 121 deletions(-) diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index f691527186..51f21738f3 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -68,6 +68,7 @@ void DatagramProcessor::processDatagrams() { Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); } else { + // qDebug() << "Received audio data packet at" << usecTimestampNow(); QMetaObject::invokeMethod(DependencyManager::get().data(), "addReceivedAudioToStream", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 10e2bc3bbf..1fcd53dc38 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -116,7 +116,7 @@ AudioClient::AudioClient() : { // clear the array of locally injected samples memset(_localProceduralSamples, 0, AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL); - + connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples, this, &AudioClient::processReceivedSamples, Qt::DirectConnection); @@ -127,7 +127,7 @@ AudioClient::AudioClient() : QTimer* updateTimer = new QTimer(this); connect(updateTimer, &QTimer::timeout, this, &AudioClient::checkDevices); updateTimer->start(DEVICE_CHECK_INTERVAL_MSECS); - + // create GVerb filter _gverb = createGverbFilter(); configureGverbFilter(_gverb); @@ -135,7 +135,7 @@ AudioClient::AudioClient() : AudioClient::~AudioClient() { stop(); - + if (_gverb) { gverb_free(_gverb); } @@ -148,7 +148,7 @@ void AudioClient::reset() { _toneSource.reset(); _sourceGain.reset(); _inputGain.reset(); - + gverb_flush(_gverb); } @@ -186,7 +186,7 @@ int numDestinationSamplesRequired(const QAudioFormat& sourceFormat, const QAudio int numSourceSamples) { float ratio = (float) destinationFormat.channelCount() / sourceFormat.channelCount(); ratio *= (float) destinationFormat.sampleRate() / sourceFormat.sampleRate(); - + return (numSourceSamples * ratio) + 0.5f; } @@ -287,7 +287,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { } qCDebug(audioclient) << "DEBUG [" << deviceName << "] [" << getNamedAudioDeviceForMode(mode, deviceName).deviceName() << "]"; - + return getNamedAudioDeviceForMode(mode, deviceName); #endif @@ -302,7 +302,7 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, if (!audioDevice.isFormatSupported(desiredAudioFormat)) { qCDebug(audioclient) << "The desired format for audio I/O is" << desiredAudioFormat; qCDebug(audioclient, "The desired audio format is not supported by this device"); - + if (desiredAudioFormat.channelCount() == 1) { adjustedAudioFormat = desiredAudioFormat; adjustedAudioFormat.setChannelCount(2); @@ -313,17 +313,17 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, adjustedAudioFormat.setChannelCount(1); } } - + const int FORTY_FOUR = 44100; - + adjustedAudioFormat = desiredAudioFormat; - + #ifdef Q_OS_ANDROID adjustedAudioFormat.setSampleRate(FORTY_FOUR); #else - + const int HALF_FORTY_FOUR = FORTY_FOUR / 2; - + if (audioDevice.supportedSampleRates().contains(AudioConstants::SAMPLE_RATE * 2)) { // use 48, which is a sample downsample, upsample adjustedAudioFormat.setSampleRate(AudioConstants::SAMPLE_RATE * 2); @@ -335,7 +335,7 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, adjustedAudioFormat.setSampleRate(FORTY_FOUR); } #endif - + if (adjustedAudioFormat != desiredAudioFormat) { // return the nearest in case it needs 2 channels adjustedAudioFormat = audioDevice.nearestFormat(adjustedAudioFormat); @@ -357,18 +357,18 @@ bool sampleChannelConversion(const int16_t* sourceSamples, int16_t* destinationS for (uint i = 0; i < numSourceSamples; i += 2) { destinationSamples[i / 2] = (sourceSamples[i] / 2) + (sourceSamples[i + 1] / 2); } - + return true; } else if (sourceAudioFormat.channelCount() == 1 && destinationAudioFormat.channelCount() == 2) { - + // loop through the mono input audio and repeat each sample twice for (uint i = 0; i < numSourceSamples; ++i) { destinationSamples[i * 2] = destinationSamples[(i * 2) + 1] = sourceSamples[i]; } - + return true; } - + return false; } @@ -376,7 +376,7 @@ soxr_error_t possibleResampling(soxr_t resampler, const int16_t* sourceSamples, int16_t* destinationSamples, unsigned int numSourceSamples, unsigned int numDestinationSamples, const QAudioFormat& sourceAudioFormat, const QAudioFormat& destinationAudioFormat) { - + if (numSourceSamples > 0) { if (!resampler) { if (!sampleChannelConversion(sourceSamples, destinationSamples, numSourceSamples, @@ -384,41 +384,41 @@ soxr_error_t possibleResampling(soxr_t resampler, // no conversion, we can copy the samples directly across memcpy(destinationSamples, sourceSamples, numSourceSamples * sizeof(int16_t)); } - + return 0; } else { soxr_error_t resampleError = 0; - + if (sourceAudioFormat.channelCount() != destinationAudioFormat.channelCount()) { float channelCountRatio = (float) destinationAudioFormat.channelCount() / sourceAudioFormat.channelCount(); - + int numChannelCoversionSamples = (int) (numSourceSamples * channelCountRatio); int16_t* channelConversionSamples = new int16_t[numChannelCoversionSamples]; - + sampleChannelConversion(sourceSamples, channelConversionSamples, numSourceSamples, sourceAudioFormat, destinationAudioFormat); - + resampleError = soxr_process(resampler, channelConversionSamples, numChannelCoversionSamples, NULL, destinationSamples, numDestinationSamples, NULL); - + delete[] channelConversionSamples; } else { - + unsigned int numAdjustedSourceSamples = numSourceSamples; unsigned int numAdjustedDestinationSamples = numDestinationSamples; - + if (sourceAudioFormat.channelCount() == 2 && destinationAudioFormat.channelCount() == 2) { numAdjustedSourceSamples /= 2; numAdjustedDestinationSamples /= 2; } - + resampleError = soxr_process(resampler, sourceSamples, numAdjustedSourceSamples, NULL, destinationSamples, numAdjustedDestinationSamples, NULL); } - + return resampleError; } } else { @@ -429,30 +429,30 @@ soxr_error_t possibleResampling(soxr_t resampler, soxr_t soxrResamplerFromInputFormatToOutputFormat(const QAudioFormat& sourceAudioFormat, const QAudioFormat& destinationAudioFormat) { soxr_error_t soxrError; - + // setup soxr_io_spec_t for input and output soxr_io_spec_t inputToNetworkSpec = soxr_io_spec(soxrDataTypeFromQAudioFormat(sourceAudioFormat), soxrDataTypeFromQAudioFormat(destinationAudioFormat)); - + // setup soxr_quality_spec_t for quality options soxr_quality_spec_t qualitySpec = soxr_quality_spec(SOXR_MQ, 0); - + int channelCount = (sourceAudioFormat.channelCount() == 2 && destinationAudioFormat.channelCount() == 2) ? 2 : 1; - + soxr_t newResampler = soxr_create(sourceAudioFormat.sampleRate(), destinationAudioFormat.sampleRate(), channelCount, &soxrError, &inputToNetworkSpec, &qualitySpec, 0); - + if (soxrError) { qCDebug(audioclient) << "There was an error setting up the soxr resampler -" << "soxr error code was " << soxrError; - + soxr_delete(newResampler); - + return NULL; } - + return newResampler; } @@ -476,7 +476,7 @@ void AudioClient::start() { QAudioDeviceInfo outputDeviceInfo = defaultAudioDeviceForMode(QAudio::AudioOutput); qCDebug(audioclient) << "The default audio output device is" << outputDeviceInfo.deviceName(); bool outputFormatSupported = switchOutputToAudioDevice(outputDeviceInfo); - + if (!inputFormatSupported) { qCDebug(audioclient) << "Unable to set up audio input because of a problem with input format."; qCDebug(audioclient) << "The closest format available is" << inputDeviceInfo.nearestFormat(_desiredInputFormat); @@ -503,11 +503,11 @@ void AudioClient::stop() { _sourceGain.finalize(); _noiseSource.finalize(); _toneSource.finalize(); - + // "switch" to invalid devices in order to shut down the state switchInputToAudioDevice(QAudioDeviceInfo()); switchOutputToAudioDevice(QAudioDeviceInfo()); - + if (_loopbackResampler) { soxr_delete(_loopbackResampler); _loopbackResampler = NULL; @@ -543,7 +543,7 @@ ty_gverb* AudioClient::createGverbFilter() { _reverbOptions->getReverbTime(), _reverbOptions->getDamping(), _reverbOptions->getSpread(), _reverbOptions->getInputBandwidth(), _reverbOptions->getEarlyLevel(), _reverbOptions->getTailLevel()); - + return filter; } @@ -560,7 +560,7 @@ void AudioClient::configureGverbFilter(ty_gverb* filter) { void AudioClient::updateGverbOptions() { bool reverbChanged = false; if (_receivedAudioStream.hasReverb()) { - + if (_zoneReverbOptions.getReverbTime() != _receivedAudioStream.getRevebTime()) { _zoneReverbOptions.setReverbTime(_receivedAudioStream.getRevebTime()); reverbChanged = true; @@ -569,7 +569,7 @@ void AudioClient::updateGverbOptions() { _zoneReverbOptions.setWetLevel(_receivedAudioStream.getWetLevel()); // Not part of actual filter config, no need to set reverbChanged to true } - + if (_reverbOptions != &_zoneReverbOptions) { _reverbOptions = &_zoneReverbOptions; reverbChanged = true; @@ -578,7 +578,7 @@ void AudioClient::updateGverbOptions() { _reverbOptions = &_scriptReverbOptions; reverbChanged = true; } - + if (reverbChanged) { gverb_free(_gverb); _gverb = createGverbFilter(); @@ -588,7 +588,7 @@ void AudioClient::updateGverbOptions() { void AudioClient::setReverb(bool reverb) { _reverb = reverb; - + if (!_reverb) { gverb_flush(_gverb); } @@ -620,7 +620,7 @@ void AudioClient::addReverb(ty_gverb* gverb, int16_t* samplesData, int16_t* reve QAudioFormat& audioFormat, bool noEcho) { float wetFraction = DB_CO(_reverbOptions->getWetLevel()); float dryFraction = 1.0f - wetFraction; - + float lValue,rValue; for (int sample = 0; sample < numSamples; sample += audioFormat.channelCount()) { // Run GVerb @@ -634,7 +634,7 @@ void AudioClient::addReverb(ty_gverb* gverb, int16_t* samplesData, int16_t* reve int lResult = glm::clamp((int)(samplesData[j] * dryFraction + lValue * wetFraction), AudioConstants::MIN_SAMPLE_VALUE, AudioConstants::MAX_SAMPLE_VALUE); samplesData[j] = (int16_t)lResult; - + if (noEcho) { reverbAlone[j] = (int16_t)lValue * wetFraction; } @@ -643,7 +643,7 @@ void AudioClient::addReverb(ty_gverb* gverb, int16_t* samplesData, int16_t* reve int rResult = glm::clamp((int)(samplesData[j] * dryFraction + rValue * wetFraction), AudioConstants::MIN_SAMPLE_VALUE, AudioConstants::MAX_SAMPLE_VALUE); samplesData[j] = (int16_t)rResult; - + if (noEcho) { reverbAlone[j] = (int16_t)rValue * wetFraction; } @@ -660,53 +660,53 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { if (_muted || !_audioOutput || (!_shouldEchoLocally && !hasReverb)) { return; } - + // if this person wants local loopback add that to the locally injected audio // if there is reverb apply it to local audio and substract the origin samples - + if (!_loopbackOutputDevice && _loopbackAudioOutput) { // we didn't have the loopback output device going so set that up now _loopbackOutputDevice = _loopbackAudioOutput->start(); - + if (!_loopbackOutputDevice) { return; } } - + // do we need to setup a resampler? if (_inputFormat.sampleRate() != _outputFormat.sampleRate() && !_loopbackResampler) { qCDebug(audioclient) << "Attemping to create a resampler for input format to output format for audio loopback."; _loopbackResampler = soxrResamplerFromInputFormatToOutputFormat(_inputFormat, _outputFormat); - + if (!_loopbackResampler) { return; } } - + static QByteArray reverbAlone; // Intermediary for local reverb with no echo static QByteArray loopBackByteArray; - + int numInputSamples = inputByteArray.size() / sizeof(int16_t); int numLoopbackSamples = numDestinationSamplesRequired(_inputFormat, _outputFormat, numInputSamples); - + reverbAlone.resize(numInputSamples * sizeof(int16_t)); loopBackByteArray.resize(numLoopbackSamples * sizeof(int16_t)); - + int16_t* inputSamples = reinterpret_cast(inputByteArray.data()); int16_t* reverbAloneSamples = reinterpret_cast(reverbAlone.data()); int16_t* loopbackSamples = reinterpret_cast(loopBackByteArray.data()); - + if (hasReverb) { updateGverbOptions(); addReverb(_gverb, inputSamples, reverbAloneSamples, numInputSamples, _inputFormat, !_shouldEchoLocally); } - + possibleResampling(_loopbackResampler, (_shouldEchoLocally) ? inputSamples : reverbAloneSamples, loopbackSamples, numInputSamples, numLoopbackSamples, _inputFormat, _outputFormat); - + _loopbackOutputDevice->write(loopBackByteArray); } @@ -726,7 +726,7 @@ void AudioClient::handleAudioInput() { int inputSamplesRequired = (int)((float)AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio); QByteArray inputByteArray = _inputDevice->readAll(); - + // Add audio source injection if enabled if (!_muted && _audioSourceInjectEnabled) { int16_t* inputFrameData = (int16_t*)inputByteArray.data(); @@ -745,11 +745,11 @@ void AudioClient::handleAudioInput() { _sourceGain.render(_inputFrameBuffer); // post gain _inputFrameBuffer.copyFrames(1, inputFrameCount, inputFrameData, true /*copy out*/); } - + handleLocalEchoAndReverb(inputByteArray); _inputRingBuffer.writeData(inputByteArray.data(), inputByteArray.size()); - + float audioInputMsecsRead = inputByteArray.size() / (float)(_inputFormat.bytesForDuration(USECS_PER_MSEC)); _stats.updateInputMsecsRead(audioInputMsecsRead); @@ -763,50 +763,50 @@ void AudioClient::handleAudioInput() { : AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; if (!_muted) { - + // zero out the monoAudioSamples array and the locally injected audio memset(networkAudioSamples, 0, numNetworkBytes); - + // Increment the time since the last clip if (_timeSinceLastClip >= 0.0f) { _timeSinceLastClip += (float) numNetworkSamples / (float) AudioConstants::SAMPLE_RATE; } - + int16_t* inputAudioSamples = new int16_t[inputSamplesRequired]; _inputRingBuffer.readSamples(inputAudioSamples, inputSamplesRequired); - + possibleResampling(_inputToNetworkResampler, inputAudioSamples, networkAudioSamples, inputSamplesRequired, numNetworkSamples, _inputFormat, _desiredInputFormat); - + delete[] inputAudioSamples; - + // only impose the noise gate and perform tone injection if we are sending mono audio if (!_isStereoInput && !_audioSourceInjectEnabled && _isNoiseGateEnabled) { _inputGate.gateSamples(networkAudioSamples, numNetworkSamples); - + // if we performed the noise gate we can get values from it instead of enumerating the samples again _lastInputLoudness = _inputGate.getLastLoudness(); - + if (_inputGate.clippedInLastFrame()) { _timeSinceLastClip = 0.0f; } } else { float loudness = 0.0f; - + for (int i = 0; i < numNetworkSamples; i++) { int thisSample = std::abs(networkAudioSamples[i]); loudness += (float) thisSample; - + if (thisSample > (AudioConstants::MAX_SAMPLE_VALUE * AudioNoiseGate::CLIPPING_THRESHOLD)) { _timeSinceLastClip = 0.0f; } } - + _lastInputLoudness = fabs(loudness / numNetworkSamples); } - + emit inputReceived(QByteArray(reinterpret_cast(networkAudioSamples), AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL * sizeof(AudioConstants::AudioSample))); @@ -814,13 +814,13 @@ void AudioClient::handleAudioInput() { // our input loudness is 0, since we're muted _lastInputLoudness = 0; _timeSinceLastClip = 0.0f; - + _inputRingBuffer.shiftReadPosition(inputSamplesRequired); } auto nodeList = DependencyManager::get(); SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); - + if (audioMixer && audioMixer->getActiveSocket()) { glm::vec3 headPosition = _positionGetter(); glm::quat headOrientation = _orientationGetter(); @@ -852,7 +852,7 @@ void AudioClient::handleAudioInput() { // memcpy the three float positions memcpy(currentPacketPtr, &headPosition, sizeof(headPosition)); currentPacketPtr += (sizeof(headPosition)); - + // memcpy our orientation memcpy(currentPacketPtr, &headOrientation, sizeof(headOrientation)); currentPacketPtr += sizeof(headOrientation); @@ -864,7 +864,7 @@ void AudioClient::handleAudioInput() { // memcpy the three float positions memcpy(currentPacketPtr, &headPosition, sizeof(headPosition)); currentPacketPtr += (sizeof(headPosition)); - + // memcpy our orientation memcpy(currentPacketPtr, &headOrientation, sizeof(headOrientation)); currentPacketPtr += sizeof(headOrientation); @@ -875,6 +875,8 @@ void AudioClient::handleAudioInput() { _stats.sentPacket(); + // qDebug() << "Sending audio packet at" << usecTimestampNow(); + int packetBytes = currentPacketPtr - audioDataPacket; nodeList->writeDatagram(audioDataPacket, packetBytes, audioMixer); _outgoingAvatarAudioSequenceNumber++; @@ -890,7 +892,7 @@ void AudioClient::processReceivedSamples(const QByteArray& inputBuffer, QByteArr outputBuffer.resize(numDeviceOutputSamples * sizeof(int16_t)); const int16_t* receivedSamples = reinterpret_cast(inputBuffer.data()); - + // copy the packet from the RB to the output possibleResampling(_networkToOutputResampler, receivedSamples, reinterpret_cast(outputBuffer.data()), @@ -900,20 +902,20 @@ void AudioClient::processReceivedSamples(const QByteArray& inputBuffer, QByteArr void AudioClient::sendMuteEnvironmentPacket() { auto nodeList = DependencyManager::get(); - + QByteArray mutePacket = nodeList->byteArrayWithPopulatedHeader(PacketTypeMuteEnvironment); int headerSize = mutePacket.size(); - + const float MUTE_RADIUS = 50; - + glm::vec3 currentSourcePosition = _positionGetter(); mutePacket.resize(mutePacket.size() + sizeof(glm::vec3) + sizeof(float)); memcpy(mutePacket.data() + headerSize, ¤tSourcePosition, sizeof(glm::vec3)); memcpy(mutePacket.data() + headerSize + sizeof(glm::vec3), &MUTE_RADIUS, sizeof(float)); - + // grab our audio mixer from the NodeList, if it exists SharedNodePointer audioMixer = nodeList->soloNodeOfType(NodeType::AudioMixer); - + if (audioMixer) { // send off this mute packet nodeList->writeDatagram(mutePacket, audioMixer); @@ -922,6 +924,7 @@ void AudioClient::sendMuteEnvironmentPacket() { void AudioClient::addReceivedAudioToStream(const QByteArray& audioByteArray) { if (_audioOutput) { + // qDebug() << "Adding received audio to stream at" << usecTimestampNow(); // Audio output must exist and be correctly set up if we're going to process received audio _receivedAudioStream.parseData(audioByteArray); } @@ -930,11 +933,11 @@ void AudioClient::addReceivedAudioToStream(const QByteArray& audioByteArray) { void AudioClient::parseAudioEnvironmentData(const QByteArray &packet) { int numBytesPacketHeader = numBytesForPacketHeader(packet); const char* dataAt = packet.constData() + numBytesPacketHeader; - + char bitset; memcpy(&bitset, dataAt, sizeof(char)); dataAt += sizeof(char); - + bool hasReverb = oneAtBit(bitset, HAS_REVERB_BIT);; if (hasReverb) { float reverbTime, wetLevel; @@ -956,13 +959,13 @@ void AudioClient::toggleMute() { void AudioClient::setIsStereoInput(bool isStereoInput) { if (isStereoInput != _isStereoInput) { _isStereoInput = isStereoInput; - + if (_isStereoInput) { _desiredInputFormat.setChannelCount(2); } else { _desiredInputFormat.setChannelCount(1); } - + // change in channel count for desired input format, restart the input device switchInputToAudioDevice(_inputAudioDeviceName); } @@ -976,7 +979,7 @@ void AudioClient::selectAudioSourcePinkNoise() { _noiseSourceEnabled = true; _toneSourceEnabled = false; } - + void AudioClient::selectAudioSourceSine440() { _toneSourceEnabled = true; _noiseSourceEnabled = false; @@ -986,24 +989,24 @@ bool AudioClient::outputLocalInjector(bool isStereo, AudioInjector* injector) { if (injector->getLocalBuffer()) { QAudioFormat localFormat = _desiredOutputFormat; localFormat.setChannelCount(isStereo ? 2 : 1); - + QAudioOutput* localOutput = new QAudioOutput(getNamedAudioDeviceForMode(QAudio::AudioOutput, _outputAudioDeviceName), localFormat, injector->getLocalBuffer()); - + // move the localOutput to the same thread as the local injector buffer localOutput->moveToThread(injector->getLocalBuffer()->thread()); - + // have it be stopped when that local buffer is about to close connect(injector->getLocalBuffer(), &AudioInjectorLocalBuffer::bufferEmpty, localOutput, &QAudioOutput::stop); connect(injector->getLocalBuffer(), &QIODevice::aboutToClose, localOutput, &QAudioOutput::stop); - + qCDebug(audioclient) << "Starting QAudioOutput for local injector" << localOutput; - + localOutput->start(injector->getLocalBuffer()); return localOutput->state() == QAudio::ActiveState; } - + return false; } @@ -1015,7 +1018,7 @@ void AudioClient::outputFormatChanged() { bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceInfo) { bool supportedFormat = false; - + // cleanup any previously initialized device if (_audioInput) { // The call to stop() causes _inputDevice to be destructed. @@ -1030,7 +1033,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn _inputAudioDeviceName = ""; } - + if (_inputToNetworkResampler) { // if we were using an input to network resampler, delete it here soxr_delete(_inputToNetworkResampler); @@ -1040,36 +1043,36 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn if (!inputDeviceInfo.isNull()) { qCDebug(audioclient) << "The audio input device " << inputDeviceInfo.deviceName() << "is available."; _inputAudioDeviceName = inputDeviceInfo.deviceName().trimmed(); - + if (adjustedFormatForAudioDevice(inputDeviceInfo, _desiredInputFormat, _inputFormat)) { qCDebug(audioclient) << "The format to be used for audio input is" << _inputFormat; - + // we've got the best we can get for input // if required, setup a soxr resampler for this input to our desired network format if (_inputFormat != _desiredInputFormat && _inputFormat.sampleRate() != _desiredInputFormat.sampleRate()) { qCDebug(audioclient) << "Attemping to create a soxr resampler for input format to network format."; _inputToNetworkResampler = soxrResamplerFromInputFormatToOutputFormat(_inputFormat, _desiredInputFormat); - + if (!_inputToNetworkResampler) { return false; } } else { qCDebug(audioclient) << "No resampling required for audio input to match desired network format."; } - + // if the user wants stereo but this device can't provide then bail - if (!_isStereoInput || _inputFormat.channelCount() == 2) { + if (!_isStereoInput || _inputFormat.channelCount() == 2) { _audioInput = new QAudioInput(inputDeviceInfo, _inputFormat, this); _numInputCallbackBytes = calculateNumberOfInputCallbackBytes(_inputFormat); _audioInput->setBufferSize(_numInputCallbackBytes); - + // how do we want to handle input working, but output not working? int numFrameSamples = calculateNumberOfFrameSamples(_numInputCallbackBytes); _inputRingBuffer.resizeForFrameSize(numFrameSamples); - + _inputDevice = _audioInput->start(); - + if (_inputDevice) { connect(_inputDevice, SIGNAL(readyRead()), this, SLOT(handleAudioInput())); supportedFormat = true; @@ -1079,7 +1082,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn } } } - + return supportedFormat; } @@ -1117,7 +1120,7 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice // cleanup any previously initialized device if (_audioOutput) { _audioOutput->stop(); - + delete _audioOutput; _audioOutput = NULL; @@ -1125,13 +1128,13 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice delete _loopbackAudioOutput; _loopbackAudioOutput = NULL; } - + if (_networkToOutputResampler) { // if we were using an input to network resampler, delete it here soxr_delete(_networkToOutputResampler); _networkToOutputResampler = NULL; } - + if (_loopbackResampler) { // if we were using an input to output resample, delete it here soxr_delete(_loopbackResampler); @@ -1141,24 +1144,24 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice if (!outputDeviceInfo.isNull()) { qCDebug(audioclient) << "The audio output device " << outputDeviceInfo.deviceName() << "is available."; _outputAudioDeviceName = outputDeviceInfo.deviceName().trimmed(); - + if (adjustedFormatForAudioDevice(outputDeviceInfo, _desiredOutputFormat, _outputFormat)) { qCDebug(audioclient) << "The format to be used for audio output is" << _outputFormat; - + // we've got the best we can get for input // if required, setup a soxr resampler for this input to our desired network format if (_desiredOutputFormat != _outputFormat && _desiredOutputFormat.sampleRate() != _outputFormat.sampleRate()) { qCDebug(audioclient) << "Attemping to create a resampler for network format to output format."; _networkToOutputResampler = soxrResamplerFromInputFormatToOutputFormat(_desiredOutputFormat, _outputFormat); - + if (!_networkToOutputResampler) { return false; } } else { qCDebug(audioclient) << "No resampling required for network output to match actual output format."; } - + outputFormatChanged(); // setup our general output device for audio-mixer audio @@ -1168,20 +1171,20 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice connect(_audioOutput, &QAudioOutput::notify, this, &AudioClient::outputNotify); qCDebug(audioclient) << "Output Buffer capacity in frames: " << _audioOutput->bufferSize() / sizeof(int16_t) / (float)_outputFrameSize; - + _audioOutputIODevice.start(); _audioOutput->start(&_audioOutputIODevice); // setup a loopback audio output device _loopbackAudioOutput = new QAudioOutput(outputDeviceInfo, _outputFormat, this); - + _timeSinceLastReceived.start(); supportedFormat = true; } } - + return supportedFormat; } @@ -1202,7 +1205,7 @@ void AudioClient::setOutputBufferSize(int numFrames) { // The following constant is operating system dependent due to differences in // the way input audio is handled. The audio input buffer size is inversely -// proportional to the accelerator ratio. +// proportional to the accelerator ratio. #ifdef Q_OS_WIN const float AudioClient::CALLBACK_ACCELERATOR_RATIO = 0.1f; @@ -1226,7 +1229,7 @@ int AudioClient::calculateNumberOfInputCallbackBytes(const QAudioFormat& format) } float AudioClient::calculateDeviceToNetworkInputRatio() const { - float inputToNetworkInputRatio = (int)((_numInputCallbackBytes + float inputToNetworkInputRatio = (int)((_numInputCallbackBytes * CALLBACK_ACCELERATOR_RATIO / AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL) + 0.5f); @@ -1257,7 +1260,7 @@ qint64 AudioClient::AudioOutputIODevice::readData(char * data, qint64 maxSize) { int samplesRequested = maxSize / sizeof(int16_t); int samplesPopped; int bytesWritten; - + if ((samplesPopped = _receivedAudioStream.popSamples(samplesRequested, false)) > 0) { AudioRingBuffer::ConstIterator lastPopOutput = _receivedAudioStream.getLastPopOutput(); lastPopOutput.readSamples((int16_t*)data, samplesPopped); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 48d9655e43..84db573dfb 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -106,6 +106,8 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) { qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString(); + qDebug() << "Lookup of HF URL at" << usecTimestampNow(); + // there are 4 possible lookup strings // 1. global place name (name of domain or place) - example: sanfrancisco @@ -163,6 +165,8 @@ void AddressManager::handleAPIResponse(QNetworkReply& requestReply) { QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object(); QJsonObject dataObject = responseObject["data"].toObject(); + qDebug() << "Go to address from API response at" << usecTimestampNow(); + goToAddressFromObject(dataObject.toVariantMap(), requestReply); emit lookupResultsFinished(); diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index f7d460d7dd..16672565d4 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -135,10 +135,21 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, replaceableSockAddr->~HifiSockAddr(); replaceableSockAddr = new (replaceableSockAddr) HifiSockAddr(iceServerHostname, ICE_SERVER_DEFAULT_PORT); + if (_iceServerSockAddr.getAddress().isNull()) { + // connect to lookup completed for ice-server socket so we can request a heartbeat once hostname is looked up + connect(&_iceServerSockAddr, &HifiSockAddr::lookupCompleted, this, &DomainHandler::completedIceServerHostnameLookup); + } else { + completedIceServerHostnameLookup(); + } + + // refresh our ICE client UUID to something new _iceClientID = QUuid::createUuid(); qCDebug(networking) << "ICE required to connect to domain via ice server at" << iceServerHostname; + + qDebug() << "ICE server info set at" << usecTimestampNow(); + } } @@ -172,6 +183,14 @@ void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) { qCDebug(networking, "Failed domain server lookup"); } +void DomainHandler::completedIceServerHostnameLookup() { + qDebug() << "ICE server socket is at" << _iceServerSockAddr; + qDebug() << "ICE server socket lookup completed at" << usecTimestampNow(); + // emit our signal so we can send a heartbeat to ice-server immediately + emit iceSocketAndIDReceived(); + +} + void DomainHandler::setIsConnected(bool isConnected) { if (_isConnected != isConnected) { _isConnected = isConnected; @@ -270,6 +289,8 @@ void DomainHandler::processICEResponsePacket(const QByteArray& icePacket) { NetworkPeer packetPeer; iceResponseStream >> packetPeer; + qDebug() << "Recieved response for network peer from ICE server at" << usecTimestampNow(); + if (packetPeer.getUUID() != _iceDomainID) { qCDebug(networking) << "Received a network peer with ID that does not match current domain. Will not attempt connection."; } else { diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 80a211405b..794a7793bb 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -85,7 +85,9 @@ public slots: private slots: void completedHostnameLookup(const QHostInfo& hostInfo); + void completedIceServerHostnameLookup(); void settingsRequestFinished(); + signals: void hostnameChanged(const QString& hostname); @@ -96,6 +98,7 @@ signals: void connectedToDomain(const QString& hostname); void disconnectedFromDomain(); + void iceSocketAndIDReceived(); void requestICEConnectionAttempt(); void settingsReceived(const QJsonObject& domainSettingsObject); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index b39503f709..462e256d15 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -64,6 +64,9 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned // clear our NodeList when the domain changes connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, this, &NodeList::reset); + // send an ICE heartbeat as soon as we get ice server information + connect(&_domainHandler, &DomainHandler::iceSocketAndIDReceived, this, &NodeList::handleICEConnectionToDomainServer); + // handle ICE signal from DS so connection is attempted immediately connect(&_domainHandler, &DomainHandler::requestICEConnectionAttempt, this, &NodeList::handleICEConnectionToDomainServer); @@ -160,6 +163,7 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr if (!_domainHandler.getSockAddr().isNull()) { // only process a list from domain-server if we're talking to a domain // TODO: how do we make sure this is actually the domain we want the list from (DTLS probably) + qDebug() << "Processing domain server list at" << usecTimestampNow(); processDomainServerList(packet); } break; @@ -198,6 +202,8 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr if (sendingNode) { sendingNode->setLastHeardMicrostamp(usecTimestampNow()); + qDebug() << "Activating socket for node" << sendingNode->getUUID() << "at" << usecTimestampNow(); + // activate the appropriate socket for this node, if not yet updated activateSocketFromNodeCommunication(packet, sendingNode); @@ -216,6 +222,8 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr case PacketTypeUnverifiedPingReply: { qCDebug(networking) << "Received reply from domain-server on" << senderSockAddr; + qDebug() << "Received reply from domain server at" << usecTimestampNow(); + // for now we're unsafely assuming this came back from the domain if (senderSockAddr == _domainHandler.getICEPeer().getLocalSocket()) { qCDebug(networking) << "Connecting to domain using local socket"; @@ -379,6 +387,8 @@ void NodeList::sendDomainServerCheckIn() { } } + qDebug() << "Sending domain server check in at" << usecTimestampNow(); + if (!isUsingDTLS) { writeUnverifiedDatagram(domainServerPacket, _domainHandler.getSockAddr()); } @@ -504,6 +514,8 @@ void NodeList::handleICEConnectionToDomainServer() { _domainHandler.getICEPeer().resetConnectionAttemps(); + qDebug() << "Sending heartbeat to ice server at" << usecTimestampNow(); + LimitedNodeList::sendHeartbeatToIceServer(_domainHandler.getICEServerSockAddr(), _domainHandler.getICEClientID(), _domainHandler.getICEDomainID()); @@ -511,6 +523,8 @@ void NodeList::handleICEConnectionToDomainServer() { qCDebug(networking) << "Sending ping packets to establish connectivity with domain-server with ID" << uuidStringWithoutCurlyBraces(_domainHandler.getICEDomainID()); + qDebug() << "Sending ping packet to domain server at" << usecTimestampNow(); + // send the ping packet to the local and public sockets for this node QByteArray localPingPacket = constructPingPacket(PingType::Local, false, _domainHandler.getICEClientID()); writeUnverifiedDatagram(localPingPacket, _domainHandler.getICEPeer().getLocalSocket()); @@ -596,6 +610,7 @@ void NodeList::sendAssignment(Assignment& assignment) { } void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) { + qDebug() << "Sending ping punch to node" << node->getUUID() << "at" << usecTimestampNow(); // send the ping packet to the local and public sockets for this node QByteArray localPingPacket = constructPingPacket(PingType::Local); diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 39b1e3e2d2..aba7661663 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -76,6 +76,7 @@ signals: void limitOfSilentDomainCheckInsReached(); private slots: void sendPendingDSPathQuery(); + void handleICEConnectionToDomainServer(); private: NodeList() : LimitedNodeList(0, 0) { assert(false); } // Not implemented, needed for DependencyManager templates compile NodeList(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0); @@ -85,8 +86,6 @@ private: void sendSTUNRequest(); bool processSTUNResponse(const QByteArray& packet); - void handleICEConnectionToDomainServer(); - void processDomainServerAuthRequest(const QByteArray& packet); void requestAuthForDomainServer(); void activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode); From 27717db9d11df12d6223263e06529adf54ef3706 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 22 May 2015 16:38:26 -0700 Subject: [PATCH 02/33] fire off DS check immediately once DS info is known --- libraries/networking/src/NodeList.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 462e256d15..b44bc44e9f 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -234,6 +234,9 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr } else { qCDebug(networking) << "Reply does not match either local or public socket for domain. Will not connect."; } + + // immediately send a domain-server check in now that we have channel to talk to domain-server on + sendDomainServerCheckIn(); } case PacketTypeStunResponse: { // a STUN packet begins with 00, we've checked the second zero with packetVersionMatch From f28ade10753e29e99a85d6f263ae293db57fd3e2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 May 2015 10:37:44 -0700 Subject: [PATCH 03/33] log connection times for eventual stats display --- interface/src/DatagramProcessor.cpp | 1 - libraries/audio-client/src/AudioClient.cpp | 1 - libraries/networking/src/AddressManager.cpp | 9 ++++-- libraries/networking/src/DomainHandler.cpp | 25 ++++++++++++--- libraries/networking/src/NodeList.cpp | 34 +++++++++++++++++---- libraries/networking/src/NodeList.h | 25 +++++++++++++++ 6 files changed, 79 insertions(+), 16 deletions(-) diff --git a/interface/src/DatagramProcessor.cpp b/interface/src/DatagramProcessor.cpp index 51f21738f3..f691527186 100644 --- a/interface/src/DatagramProcessor.cpp +++ b/interface/src/DatagramProcessor.cpp @@ -68,7 +68,6 @@ void DatagramProcessor::processDatagrams() { Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); } else { - // qDebug() << "Received audio data packet at" << usecTimestampNow(); QMetaObject::invokeMethod(DependencyManager::get().data(), "addReceivedAudioToStream", Qt::QueuedConnection, Q_ARG(QByteArray, incomingPacket)); diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 1fcd53dc38..7c93bb0252 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -924,7 +924,6 @@ void AudioClient::sendMuteEnvironmentPacket() { void AudioClient::addReceivedAudioToStream(const QByteArray& audioByteArray) { if (_audioOutput) { - // qDebug() << "Adding received audio to stream at" << usecTimestampNow(); // Audio output must exist and be correctly set up if we're going to process received audio _receivedAudioStream.parseData(audioByteArray); } diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 84db573dfb..34d43a894e 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -106,7 +106,8 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) { qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString(); - qDebug() << "Lookup of HF URL at" << usecTimestampNow(); + DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::LookupAddress, + usecTimestampNow()); // there are 4 possible lookup strings @@ -165,8 +166,6 @@ void AddressManager::handleAPIResponse(QNetworkReply& requestReply) { QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object(); QJsonObject dataObject = responseObject["data"].toObject(); - qDebug() << "Go to address from API response at" << usecTimestampNow(); - goToAddressFromObject(dataObject.toVariantMap(), requestReply); emit lookupResultsFinished(); @@ -206,6 +205,8 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const const QString DOMAIN_NETWORK_PORT_KEY = "network_port"; const QString DOMAIN_ICE_SERVER_ADDRESS_KEY = "ice_server_address"; + DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::HandleAddress); + if (domainObject.contains(DOMAIN_NETWORK_ADDRESS_KEY)) { QString domainHostname = domainObject[DOMAIN_NETWORK_ADDRESS_KEY].toString(); @@ -436,6 +437,8 @@ void AddressManager::setDomainInfo(const QString& hostname, quint16 port) { qCDebug(networking) << "Possible domain change required to connect to domain at" << hostname << "on" << port; + DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::HandleAddress); + emit possibleDomainChangeRequired(hostname, port); } diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 16672565d4..a5d53e8c7a 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -84,6 +84,10 @@ void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hos _sockAddr = sockAddr; } + if (!_sockAddr.isNull()) { + DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::SetDomainSocket); + } + // some callers may pass a hostname, this is not to be used for lookup but for DTLS certificate verification _hostname = hostname; } @@ -111,6 +115,8 @@ void DomainHandler::setHostnameAndPort(const QString& hostname, quint16 port) { qCDebug(networking, "Looking up DS hostname %s.", _hostname.toLocal8Bit().constData()); QHostInfo::lookupHost(_hostname, this, SLOT(completedHostnameLookup(const QHostInfo&))); + DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::SetDomainHostname); + UserActivityLogger::getInstance().changedDomain(_hostname); emit hostnameChanged(_hostname); } @@ -135,6 +141,10 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, replaceableSockAddr->~HifiSockAddr(); replaceableSockAddr = new (replaceableSockAddr) HifiSockAddr(iceServerHostname, ICE_SERVER_DEFAULT_PORT); + auto nodeList = DependencyManager::get(); + + nodeList->flagTimeForConnectionStep(NodeList::ConnectionStep::LookupICEHostname); + if (_iceServerSockAddr.getAddress().isNull()) { // connect to lookup completed for ice-server socket so we can request a heartbeat once hostname is looked up connect(&_iceServerSockAddr, &HifiSockAddr::lookupCompleted, this, &DomainHandler::completedIceServerHostnameLookup); @@ -148,18 +158,19 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, qCDebug(networking) << "ICE required to connect to domain via ice server at" << iceServerHostname; - qDebug() << "ICE server info set at" << usecTimestampNow(); - + nodeList->flagTimeForConnectionStep(NodeList::ConnectionStep::SetICEServerInfo); } } void DomainHandler::activateICELocalSocket() { + DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::SetDomainSocket); _sockAddr = _icePeer.getLocalSocket(); _hostname = _sockAddr.getAddress().toString(); emit completedSocketDiscovery(); } void DomainHandler::activateICEPublicSocket() { + DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::SetDomainSocket); _sockAddr = _icePeer.getPublicSocket(); _hostname = _sockAddr.getAddress().toString(); emit completedSocketDiscovery(); @@ -170,6 +181,9 @@ void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) { if (hostInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) { _sockAddr.setAddress(hostInfo.addresses()[i]); + DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::SetDomainSocket); + + qCDebug(networking, "DS at %s is at %s", _hostname.toLocal8Bit().constData(), _sockAddr.getAddress().toString().toLocal8Bit().constData()); @@ -185,10 +199,11 @@ void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) { void DomainHandler::completedIceServerHostnameLookup() { qDebug() << "ICE server socket is at" << _iceServerSockAddr; - qDebug() << "ICE server socket lookup completed at" << usecTimestampNow(); + + DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::HandleICEHostname); + // emit our signal so we can send a heartbeat to ice-server immediately emit iceSocketAndIDReceived(); - } void DomainHandler::setIsConnected(bool isConnected) { @@ -289,7 +304,7 @@ void DomainHandler::processICEResponsePacket(const QByteArray& icePacket) { NetworkPeer packetPeer; iceResponseStream >> packetPeer; - qDebug() << "Recieved response for network peer from ICE server at" << usecTimestampNow(); + DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::ReceiveDSPeerInformation); if (packetPeer.getUUID() != _iceDomainID) { qCDebug(networking) << "Received a network peer with ID that does not match current domain. Will not attempt connection."; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index b44bc44e9f..6d172ed8ea 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -163,7 +164,6 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr if (!_domainHandler.getSockAddr().isNull()) { // only process a list from domain-server if we're talking to a domain // TODO: how do we make sure this is actually the domain we want the list from (DTLS probably) - qDebug() << "Processing domain server list at" << usecTimestampNow(); processDomainServerList(packet); } break; @@ -222,8 +222,6 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr case PacketTypeUnverifiedPingReply: { qCDebug(networking) << "Received reply from domain-server on" << senderSockAddr; - qDebug() << "Received reply from domain server at" << usecTimestampNow(); - // for now we're unsafely assuming this came back from the domain if (senderSockAddr == _domainHandler.getICEPeer().getLocalSocket()) { qCDebug(networking) << "Connecting to domain using local socket"; @@ -271,6 +269,9 @@ void NodeList::reset() { if (_dtlsSocket) { disconnect(_dtlsSocket, 0, this, 0); } + + // reset the connection times + _lastConnectionTimes.clear(); } void NodeList::addNodeTypeToInterestSet(NodeType_t nodeTypeToAdd) { @@ -390,7 +391,7 @@ void NodeList::sendDomainServerCheckIn() { } } - qDebug() << "Sending domain server check in at" << usecTimestampNow(); + flagTimeForConnectionStep(NodeList::ConnectionStep::SendFirstDSCheckIn); if (!isUsingDTLS) { writeUnverifiedDatagram(domainServerPacket, _domainHandler.getSockAddr()); @@ -517,7 +518,7 @@ void NodeList::handleICEConnectionToDomainServer() { _domainHandler.getICEPeer().resetConnectionAttemps(); - qDebug() << "Sending heartbeat to ice server at" << usecTimestampNow(); + flagTimeForConnectionStep(NodeList::ConnectionStep::SendFirstICEServerHearbeat); LimitedNodeList::sendHeartbeatToIceServer(_domainHandler.getICEServerSockAddr(), _domainHandler.getICEClientID(), @@ -526,7 +527,7 @@ void NodeList::handleICEConnectionToDomainServer() { qCDebug(networking) << "Sending ping packets to establish connectivity with domain-server with ID" << uuidStringWithoutCurlyBraces(_domainHandler.getICEDomainID()); - qDebug() << "Sending ping packet to domain server at" << usecTimestampNow(); + flagTimeForConnectionStep(NodeList::ConnectionStep::SendFirstPingsToDS); // send the ping packet to the local and public sockets for this node QByteArray localPingPacket = constructPingPacket(PingType::Local, false, _domainHandler.getICEClientID()); @@ -543,6 +544,8 @@ int NodeList::processDomainServerList(const QByteArray& packet) { // this is a packet from the domain server, reset the count of un-replied check-ins _numNoReplyDomainCheckIns = 0; + DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::ReceiveFirstDSList); + // if this was the first domain-server list from this domain, we've now connected if (!_domainHandler.isConnected()) { _domainHandler.setUUID(uuidFromPacketHeader(packet)); @@ -655,3 +658,22 @@ void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, con sendingNode->activateSymmetricSocket(); } } + +void NodeList::flagTimeForConnectionStep(NodeList::ConnectionStep::Value connectionStep) { + QMetaObject::invokeMethod(this, "flagTimeForConnectionStep", + Q_ARG(NodeList::ConnectionStep::Value, connectionStep), + Q_ARG(quint64, usecTimestampNow())); +} + +void NodeList::flagTimeForConnectionStep(NodeList::ConnectionStep::Value connectionStep, quint64 timestamp) { + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, "flagTimeForConnectionStep", + Q_ARG(NodeList::ConnectionStep::Value, connectionStep), + Q_ARG(quint64, timestamp)); + } else { + // we only add a timestamp on the first call for each NodeList::ConnectionStep + if (!_lastConnectionTimes.contains(connectionStep)) { + _lastConnectionTimes[connectionStep] = timestamp; + } + } +} diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index aba7661663..c2b460ecc2 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -45,6 +45,25 @@ class NodeList : public LimitedNodeList { SINGLETON_DEPENDENCY public: + + class ConnectionStep { + public: + enum Value { + LookupAddress, + HandleAddress, + SetICEServerInfo, + LookupICEHostname, + HandleICEHostname, + SendFirstICEServerHearbeat, + ReceiveDSPeerInformation, + SendFirstPingsToDS, + SetDomainHostname, + SetDomainSocket, + SendFirstDSCheckIn, + ReceiveFirstDSList + }; + }; + NodeType_t getOwnerType() const { return _ownerType; } void setOwnerType(NodeType_t ownerType) { _ownerType = ownerType; } @@ -63,6 +82,8 @@ public: int processDomainServerList(const QByteArray& packet); + void flagTimeForConnectionStep(NodeList::ConnectionStep::Value connectionStep); + void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; } void sendAssignment(Assignment& assignment); @@ -95,6 +116,8 @@ private: void sendDSPathQuery(const QString& newPath); + void flagTimeForConnectionStep(NodeList::ConnectionStep::Value connectionStep, qint64 timestamp); + NodeType_t _ownerType; NodeSet _nodeTypesOfInterest; DomainHandler _domainHandler; @@ -103,6 +126,8 @@ private: bool _hasCompletedInitialSTUNFailure; unsigned int _stunRequestsSinceSuccess; + QMap _lastConnectionTimes; + friend class Application; }; From f9251471da08e7a2213bc28b52a05a04abe85dec Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 May 2015 10:44:45 -0700 Subject: [PATCH 04/33] add audio connection to final part of connection steps --- libraries/audio-client/src/AudioClient.cpp | 4 +++- libraries/networking/src/AddressManager.cpp | 3 +-- libraries/networking/src/NodeList.cpp | 8 ++++++++ libraries/networking/src/NodeList.h | 8 ++++++-- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 7c93bb0252..2bf25a3575 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -875,7 +875,7 @@ void AudioClient::handleAudioInput() { _stats.sentPacket(); - // qDebug() << "Sending audio packet at" << usecTimestampNow(); + nodeList->flagTimeForConnectionStep(NodeList::ConnectionStep::SendFirstAudioPacket); int packetBytes = currentPacketPtr - audioDataPacket; nodeList->writeDatagram(audioDataPacket, packetBytes, audioMixer); @@ -923,6 +923,8 @@ void AudioClient::sendMuteEnvironmentPacket() { } void AudioClient::addReceivedAudioToStream(const QByteArray& audioByteArray) { + DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::ReceiveFirstAudioPacket); + if (_audioOutput) { // Audio output must exist and be correctly set up if we're going to process received audio _receivedAudioStream.parseData(audioByteArray); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 34d43a894e..63bcf4c4bc 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -106,8 +106,7 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) { qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString(); - DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::LookupAddress, - usecTimestampNow()); + DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::LookupAddress); // there are 4 possible lookup strings diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 6d172ed8ea..31e7a88e66 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -636,6 +636,10 @@ void NodeList::pingInactiveNodes() { if (!node->getActiveSocket()) { // we don't have an active link to this node, ping it to set that up pingPunchForInactiveNode(node); + + if (node->getType() == NodeType::AudioMixer) { + flagTimeForConnectionStep(NodeList::ConnectionStep::SendFirstAudioPing); + } } }); } @@ -657,6 +661,10 @@ void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, con } else if (pingType == PingType::Symmetric && !sendingNode->getActiveSocket()) { sendingNode->activateSymmetricSocket(); } + + if (sendingNode->getType() == NodeType::AudioMixer) { + flagTimeForConnectionStep(NodeList::ConnectionStep::SetAudioMixerSocket); + } } void NodeList::flagTimeForConnectionStep(NodeList::ConnectionStep::Value connectionStep) { diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index c2b460ecc2..2ceee95f94 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -60,7 +60,11 @@ public: SetDomainHostname, SetDomainSocket, SendFirstDSCheckIn, - ReceiveFirstDSList + ReceiveFirstDSList, + SendFirstAudioPing, + SetAudioMixerSocket, + SendFirstAudioPacket, + ReceiveFirstAudioPacket }; }; @@ -116,7 +120,7 @@ private: void sendDSPathQuery(const QString& newPath); - void flagTimeForConnectionStep(NodeList::ConnectionStep::Value connectionStep, qint64 timestamp); + void flagTimeForConnectionStep(NodeList::ConnectionStep::Value connectionStep, quint64 timestamp); NodeType_t _ownerType; NodeSet _nodeTypesOfInterest; From 626f22191c3d2d7a3b05c217858408733318507b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 May 2015 14:17:02 -0700 Subject: [PATCH 05/33] add a QAbstractTableModel for domain connection steps --- .../src/ui/DomainConnectionTableModel.cpp | 82 +++++++++++++++++++ interface/src/ui/DomainConnectionTableModel.h | 41 ++++++++++ libraries/networking/src/DomainHandler.cpp | 6 +- libraries/networking/src/NodeList.cpp | 27 +++--- libraries/networking/src/NodeList.h | 46 +++++------ 5 files changed, 157 insertions(+), 45 deletions(-) create mode 100644 interface/src/ui/DomainConnectionTableModel.cpp create mode 100644 interface/src/ui/DomainConnectionTableModel.h diff --git a/interface/src/ui/DomainConnectionTableModel.cpp b/interface/src/ui/DomainConnectionTableModel.cpp new file mode 100644 index 0000000000..0f27b56434 --- /dev/null +++ b/interface/src/ui/DomainConnectionTableModel.cpp @@ -0,0 +1,82 @@ +// +// DomainConnectionTableModel.cpp +// interface/src/ui +// +// Created by Stephen Birarda on 05/26/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include + +#include +#include + +#include "DomainConnectionTableModel.h" + +DomainConnectionTableModel::DomainConnectionTableModel(QObject* parent) : + QAbstractTableModel(parent) +{ + // ask the NodeList for the current values for connection times + QMap times = DependencyManager::get()->getLastConnectionTimes(); + + // setup our data with the returned values + + quint64 totalTime = 0; + quint64 firstStepTime = times[NodeList::ConnectionStep::LookupAddress] / USECS_PER_MSEC; + quint64 lastStepTime = firstStepTime; + + const QMetaObject &nodeListMeta = NodeList::staticMetaObject; + QMetaEnum stepEnum = nodeListMeta.enumerator(nodeListMeta.indexOfEnumerator("ConnectionStep")); + + for (int i = 0; i < stepEnum.keyCount(); i++) { + NodeList::ConnectionStep step = static_cast(i); + + if (times.contains(step)) { + // When did this step occur, how long since the last step, how long since the start? + _timestamps[_numRows] = times[step] / USECS_PER_MSEC; + _deltas[_numRows] = (_timestamps[_numRows] - lastStepTime); + _totals[_numRows] = _timestamps[_numRows] - firstStepTime; + + // increment the total time by this delta to keep track + totalTime += _deltas[_numRows]; + + lastStepTime = _timestamps[_numRows]; + + // increment our counted number of rows + ++_numRows; + } + } +} + +QVariant DomainConnectionTableModel::headerData(int section, Qt::Orientation orientation, int role) const { + switch(section) { + case 0: + return QVariant("Name"); + case 1: + return QVariant("Timestamp (ms)"); + case 2: + return QVariant("Delta (ms)"); + case 3: + return QVariant("Total Elapsed (ms)"); + default: + return QVariant(); + } +} + +QVariant DomainConnectionTableModel::data(const QModelIndex& index, int role) const { + switch(index.column()) { + case 0: + return _names[index.row()]; + case 1: + return QVariant(_timestamps[index.row()]); + case 2: + return QVariant(_deltas[index.row()]); + case 3: + return QVariant(_totals[index.row()]); + default: + return QVariant(); + } +} diff --git a/interface/src/ui/DomainConnectionTableModel.h b/interface/src/ui/DomainConnectionTableModel.h new file mode 100644 index 0000000000..9c2001dc00 --- /dev/null +++ b/interface/src/ui/DomainConnectionTableModel.h @@ -0,0 +1,41 @@ +// +// DomainConnectionTableModel.h +// interface/src/ui +// +// Created by Stephen Birarda on 05/26/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_DomainConnectionTableModel_h +#define hifi_DomainConnectionTableModel_h + +#pragma once + +#include + +class DomainConnectionTableModel: public QAbstractTableModel { + Q_OBJECT +public: + DomainConnectionTableModel(QObject* parent = 0); + + const int NUM_COLUMNS = 4; // name, time, delta, since start + + int rowCount(const QModelIndex& parent = QModelIndex()) const { return _numRows; } + int columnCount(const QModelIndex& parent = QModelIndex()) const { return NUM_COLUMNS; } + + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; +private: + int _numRows = 0; + + QVariantList _names; + QList _timestamps; + QList _deltas; + QList _totals; +}; + + +#endif // hifi_DomainConnectionTableModel_h diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index a5d53e8c7a..622eec6fd5 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -143,7 +143,7 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, auto nodeList = DependencyManager::get(); - nodeList->flagTimeForConnectionStep(NodeList::ConnectionStep::LookupICEHostname); + nodeList->flagTimeForConnectionStep(NodeList::ConnectionStep::SetICEServerHostname); if (_iceServerSockAddr.getAddress().isNull()) { // connect to lookup completed for ice-server socket so we can request a heartbeat once hostname is looked up @@ -157,8 +157,6 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, _iceClientID = QUuid::createUuid(); qCDebug(networking) << "ICE required to connect to domain via ice server at" << iceServerHostname; - - nodeList->flagTimeForConnectionStep(NodeList::ConnectionStep::SetICEServerInfo); } } @@ -200,7 +198,7 @@ void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) { void DomainHandler::completedIceServerHostnameLookup() { qDebug() << "ICE server socket is at" << _iceServerSockAddr; - DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::HandleICEHostname); + DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::SetICEServerSocket); // emit our signal so we can send a heartbeat to ice-server immediately emit iceSocketAndIDReceived(); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 31e7a88e66..4bfedfd555 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -76,6 +77,8 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned // clear our NodeList when logout is requested connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset); + + qRegisterMetaType("NodeList::ConnectionStep"); } qint64 NodeList::sendStats(const QJsonObject& statsObject, const HifiSockAddr& destination) { @@ -202,8 +205,6 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr if (sendingNode) { sendingNode->setLastHeardMicrostamp(usecTimestampNow()); - qDebug() << "Activating socket for node" << sendingNode->getUUID() << "at" << usecTimestampNow(); - // activate the appropriate socket for this node, if not yet updated activateSocketFromNodeCommunication(packet, sendingNode); @@ -616,8 +617,6 @@ void NodeList::sendAssignment(Assignment& assignment) { } void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) { - qDebug() << "Sending ping punch to node" << node->getUUID() << "at" << usecTimestampNow(); - // send the ping packet to the local and public sockets for this node QByteArray localPingPacket = constructPingPacket(PingType::Local); writeDatagram(localPingPacket, node, node->getLocalSocket()); @@ -667,21 +666,15 @@ void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, con } } -void NodeList::flagTimeForConnectionStep(NodeList::ConnectionStep::Value connectionStep) { +void NodeList::flagTimeForConnectionStep(NodeList::ConnectionStep connectionStep) { QMetaObject::invokeMethod(this, "flagTimeForConnectionStep", - Q_ARG(NodeList::ConnectionStep::Value, connectionStep), - Q_ARG(quint64, usecTimestampNow())); + Q_ARG(NodeList::ConnectionStep, connectionStep), + Q_ARG(quint64, usecTimestampNow())); } -void NodeList::flagTimeForConnectionStep(NodeList::ConnectionStep::Value connectionStep, quint64 timestamp) { - if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(this, "flagTimeForConnectionStep", - Q_ARG(NodeList::ConnectionStep::Value, connectionStep), - Q_ARG(quint64, timestamp)); - } else { - // we only add a timestamp on the first call for each NodeList::ConnectionStep - if (!_lastConnectionTimes.contains(connectionStep)) { - _lastConnectionTimes[connectionStep] = timestamp; - } +void NodeList::flagTimeForConnectionStep(NodeList::ConnectionStep connectionStep, quint64 timestamp) { + // we only add a timestamp on the first call for each NodeList::ConnectionStep + if (!_lastConnectionTimes.contains(connectionStep)) { + _lastConnectionTimes[connectionStep] = timestamp; } } diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 2ceee95f94..42356570a9 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -46,28 +46,26 @@ class NodeList : public LimitedNodeList { public: - class ConnectionStep { - public: - enum Value { - LookupAddress, - HandleAddress, - SetICEServerInfo, - LookupICEHostname, - HandleICEHostname, - SendFirstICEServerHearbeat, - ReceiveDSPeerInformation, - SendFirstPingsToDS, - SetDomainHostname, - SetDomainSocket, - SendFirstDSCheckIn, - ReceiveFirstDSList, - SendFirstAudioPing, - SetAudioMixerSocket, - SendFirstAudioPacket, - ReceiveFirstAudioPacket - }; + enum ConnectionStep { + LookupAddress, + HandleAddress, + SetICEServerHostname, + SetICEServerSocket, + SendFirstICEServerHearbeat, + ReceiveDSPeerInformation, + SendFirstPingsToDS, + SetDomainHostname, + SetDomainSocket, + SendFirstDSCheckIn, + ReceiveFirstDSList, + SendFirstAudioPing, + SetAudioMixerSocket, + SendFirstAudioPacket, + ReceiveFirstAudioPacket }; + Q_ENUMS(ConnectionStep); + NodeType_t getOwnerType() const { return _ownerType; } void setOwnerType(NodeType_t ownerType) { _ownerType = ownerType; } @@ -86,7 +84,8 @@ public: int processDomainServerList(const QByteArray& packet); - void flagTimeForConnectionStep(NodeList::ConnectionStep::Value connectionStep); + const QMap getLastConnectionTimes() const; + void flagTimeForConnectionStep(NodeList::ConnectionStep connectionStep); void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; } void sendAssignment(Assignment& assignment); @@ -102,6 +101,7 @@ signals: private slots: void sendPendingDSPathQuery(); void handleICEConnectionToDomainServer(); + void flagTimeForConnectionStep(NodeList::ConnectionStep connectionStep, quint64 timestamp); private: NodeList() : LimitedNodeList(0, 0) { assert(false); } // Not implemented, needed for DependencyManager templates compile NodeList(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0); @@ -120,8 +120,6 @@ private: void sendDSPathQuery(const QString& newPath); - void flagTimeForConnectionStep(NodeList::ConnectionStep::Value connectionStep, quint64 timestamp); - NodeType_t _ownerType; NodeSet _nodeTypesOfInterest; DomainHandler _domainHandler; @@ -130,7 +128,7 @@ private: bool _hasCompletedInitialSTUNFailure; unsigned int _stunRequestsSinceSuccess; - QMap _lastConnectionTimes; + QMap _lastConnectionTimes; friend class Application; }; From d1489c50fec226658a2a3495829d5ae0cd025c9f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 May 2015 14:18:58 -0700 Subject: [PATCH 06/33] add connection times getter to NodeList --- libraries/networking/src/NodeList.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 42356570a9..8d40e252e5 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -84,7 +84,7 @@ public: int processDomainServerList(const QByteArray& packet); - const QMap getLastConnectionTimes() const; + const QMap getLastConnectionTimes() const { return _lastConnectionTimes; } void flagTimeForConnectionStep(NodeList::ConnectionStep connectionStep); void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; } From 61029fd0b160c07eb3b99499721a322d63e97dba Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 May 2015 15:22:25 -0700 Subject: [PATCH 07/33] add ability to display domain connection times --- interface/src/Menu.cpp | 76 +++++++++-------- interface/src/Menu.h | 25 +++--- interface/src/ui/DialogsManager.cpp | 22 +++-- interface/src/ui/DialogsManager.h | 17 ++-- interface/src/ui/DomainConnectionDialog.cpp | 74 +++++++++++++++++ interface/src/ui/DomainConnectionDialog.h | 25 ++++++ .../src/ui/DomainConnectionTableModel.cpp | 82 ------------------- interface/src/ui/DomainConnectionTableModel.h | 41 ---------- 8 files changed, 178 insertions(+), 184 deletions(-) create mode 100644 interface/src/ui/DomainConnectionDialog.cpp create mode 100644 interface/src/ui/DomainConnectionDialog.h delete mode 100644 interface/src/ui/DomainConnectionTableModel.cpp delete mode 100644 interface/src/ui/DomainConnectionTableModel.h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index ddd581fe53..6242318170 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -71,14 +71,14 @@ Menu::Menu() { { addActionToQMenuAndActionHash(fileMenu, MenuOption::Login); - + // connect to the appropriate signal of the AccountManager so that we can change the Login/Logout menu item connect(&accountManager, &AccountManager::profileChanged, dialogsManager.data(), &DialogsManager::toggleLoginDialog); connect(&accountManager, &AccountManager::logoutComplete, dialogsManager.data(), &DialogsManager::toggleLoginDialog); } - + addDisabledActionAndSeparator(fileMenu, "Scripts"); addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O, qApp, SLOT(loadDialog())); @@ -92,7 +92,7 @@ Menu::Menu() { addDisabledActionAndSeparator(fileMenu, "Location"); qApp->getBookmarks()->setupMenus(this, fileMenu); - + addActionToQMenuAndActionHash(fileMenu, MenuOption::AddressBar, Qt::CTRL | Qt::Key_L, @@ -148,8 +148,8 @@ Menu::Menu() { SLOT(setEnabled(bool))); connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool))); #endif - - addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat, + + addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat, 0, // QML Qt::Key_Backslash, dialogsManager.data(), SLOT(showIRCLink())); addActionToQMenuAndActionHash(toolsMenu, MenuOption::AddRemoveFriends, 0, @@ -175,7 +175,7 @@ Menu::Menu() { discoverabilityManager.data(), SLOT(setVisibility())); visibilityGroup->addAction(visibleToNoOne); - connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged, + connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged, discoverabilityManager.data(), &DiscoverabilityManager::visibilityChanged); } @@ -196,7 +196,7 @@ Menu::Menu() { 0, // QML Qt::Key_Apostrophe, qApp, SLOT(resetSensors())); - + addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0, qApp, SLOT(packageModel())); @@ -245,17 +245,17 @@ Menu::Menu() { qApp, SLOT(setFullscreen(bool))); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, - 0, // QML Qt::Key_P, + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, + 0, // QML Qt::Key_P, true, qApp, SLOT(cameraMenuChanged())); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, - 0, //QML Qt::SHIFT | Qt::Key_H, + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, + 0, //QML Qt::SHIFT | Qt::Key_H, true); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, - 0, // QML Qt::Key_H, + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, + 0, // QML Qt::Key_H, false, qApp, SLOT(cameraMenuChanged())); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools, + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools, #ifdef Q_OS_MAC Qt::META | Qt::Key_H, #else @@ -285,8 +285,8 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats); - addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, - Qt::CTRL | Qt::SHIFT | Qt::Key_L, + addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, + Qt::CTRL | Qt::SHIFT | Qt::Key_L, qApp, SLOT(toggleLogDialog())); addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, dialogsManager.data(), SLOT(bandwidthDetails())); @@ -297,8 +297,8 @@ Menu::Menu() { MenuWrapper* developerMenu = addMenu("Developer"); MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render"); - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, - 0, // QML Qt::SHIFT | Qt::Key_A, + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, + 0, // QML Qt::SHIFT | Qt::Key_A, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AmbientOcclusion); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DontFadeOnOctreeServerChanges); @@ -317,7 +317,7 @@ Menu::Menu() { ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight7, 0, false)); ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false)); ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false)); - + MenuWrapper* shadowMenu = renderOptionsMenu->addMenu("Shadows"); QActionGroup* shadowGroup = new QActionGroup(shadowMenu); shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true)); @@ -351,14 +351,14 @@ Menu::Menu() { resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, false)); resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false)); - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, 0, // QML Qt::Key_Asterisk, true); - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableGlowEffect, 0, true, + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableGlowEffect, 0, true, DependencyManager::get().data(), SLOT(toggleGlowEffect(bool))); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Wireframe, Qt::ALT | Qt::Key_W, false); - addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, + addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, 0, // QML Qt::SHIFT | Qt::Key_L, dialogsManager.data(), SLOT(lodTools())); @@ -384,7 +384,7 @@ Menu::Menu() { faceTrackerGroup->addAction(faceshiftFaceTracker); #endif #ifdef HAVE_DDE - QAction* ddeFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseCamera, + QAction* ddeFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseCamera, 0, true, qApp, SLOT(setActiveFaceTracker())); faceTrackerGroup->addAction(ddeFaceTracker); @@ -404,13 +404,13 @@ Menu::Menu() { #endif #if defined(HAVE_FACESHIFT) || defined(HAVE_DDE) faceTrackingMenu->addSeparator(); - addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::MuteFaceTracking, + addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::MuteFaceTracking, Qt::CTRL | Qt::SHIFT | Qt::Key_F, true, // DDE face tracking is on by default qApp, SLOT(toggleFaceTrackerMute())); addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::AutoMuteAudio, 0, true); #endif - - auto avatarManager = DependencyManager::get(); + + auto avatarManager = DependencyManager::get(); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::AvatarReceiveStats, 0, false, avatarManager.data(), SLOT(setShouldShowReceiveStats(bool))); @@ -426,7 +426,7 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false); - + MenuWrapper* sixenseOptionsMenu = handOptionsMenu->addMenu("Sixense"); #ifdef __APPLE__ addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, @@ -472,7 +472,11 @@ Menu::Menu() { addActionToQMenuAndActionHash(networkMenu, MenuOption::DiskCacheEditor, 0, dialogsManager.data(), SLOT(toggleDiskCacheEditor())); + addActionToQMenuAndActionHash(networkMenu, MenuOption::ShowDSConnectTable, 0, + dialogsManager.data(), SLOT(showDomainConnectionDialog())); + MenuWrapper* timingMenu = developerMenu->addMenu("Timing and Stats"); + MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer"); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true); @@ -510,7 +514,7 @@ Menu::Menu() { 0, audioIO.data(), SLOT(sendMuteEnvironmentPacket())); - + auto scope = DependencyManager::get(); MenuWrapper* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope"); @@ -548,7 +552,7 @@ Menu::Menu() { audioScopeFramesGroup->addAction(twentyFrames); audioScopeFramesGroup->addAction(fiftyFrames); } - + auto statsRenderer = DependencyManager::get(); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioStats, Qt::CTRL | Qt::SHIFT | Qt::Key_A, @@ -767,7 +771,7 @@ QAction* Menu::getActionFromName(const QString& menuName, MenuWrapper* menu) { } else { menuActions = actions(); } - + foreach (QAction* menuAction, menuActions) { QString actionText = menuAction->text(); if (menuName == menuAction->text()) { @@ -868,14 +872,14 @@ MenuWrapper* Menu::addMenu(const QString& menuName) { } addTo = menu; } - + QMenuBar::repaint(); return menu; } void Menu::removeMenu(const QString& menuName) { QAction* action = getMenuAction(menuName); - + // only proceed if the menu actually exists if (action) { QString finalMenuPart; @@ -885,14 +889,14 @@ void Menu::removeMenu(const QString& menuName) { } else { QMenuBar::removeAction(action); } - + QMenuBar::repaint(); } } bool Menu::menuExists(const QString& menuName) { QAction* action = getMenuAction(menuName); - + // only proceed if the menu actually exists if (action) { return true; @@ -937,7 +941,7 @@ void Menu::addMenuItem(const MenuItemProperties& properties) { if (!properties.shortcutKeySequence.isEmpty()) { shortcut = new QShortcut(properties.shortcutKeySequence, this); } - + // check for positioning requests int requestedPosition = properties.position; if (requestedPosition == UNSPECIFIED_POSITION && !properties.beforeItem.isEmpty()) { @@ -951,7 +955,7 @@ void Menu::addMenuItem(const MenuItemProperties& properties) { requestedPosition = afterPosition + 1; } } - + QAction* menuItemAction = NULL; if (properties.isSeparator) { addDisabledActionAndSeparator(menuObj, properties.menuItemName, requestedPosition); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 12ec312a56..6107744abc 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -58,15 +58,15 @@ class Menu : public QMenuBar { Q_OBJECT public: static Menu* getInstance(); - + void loadSettings(); void saveSettings(); - + MenuWrapper* getMenu(const QString& menuName); void triggerOption(const QString& menuOption); QAction* getActionForOption(const QString& menuOption); - + QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, const QString& actionName, const QKeySequence& shortcut = 0, @@ -80,9 +80,9 @@ public: const QKeySequence& shortcut = 0, QAction::MenuRole role = QAction::NoRole, int menuItemLocation = UNSPECIFIED_POSITION); - + void removeAction(MenuWrapper* menu, const QString& actionName); - + public slots: MenuWrapper* addMenu(const QString& menuName); void removeMenu(const QString& menuName); @@ -94,21 +94,21 @@ public slots: bool menuItemExists(const QString& menuName, const QString& menuitem); bool isOptionChecked(const QString& menuOption) const; void setIsOptionChecked(const QString& menuOption, bool isChecked); - + private: static Menu* _instance; Menu(); - + typedef void(*settingsAction)(Settings&, QAction&); static void loadAction(Settings& settings, QAction& action); static void saveAction(Settings& settings, QAction& action); void scanMenuBar(settingsAction modifySetting); void scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings); - + /// helper method to have separators with labels that are also compatible with OS X void addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName, int menuItemLocation = UNSPECIFIED_POSITION); - + QAction* addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu, const QString& actionName, const QKeySequence& shortcut = 0, @@ -116,15 +116,15 @@ private: const QObject* receiver = NULL, const char* member = NULL, int menuItemLocation = UNSPECIFIED_POSITION); - + QAction* getActionFromName(const QString& menuName, MenuWrapper* menu); MenuWrapper* getSubMenuFromName(const QString& menuName, MenuWrapper* menu); MenuWrapper* getMenuParent(const QString& menuName, QString& finalMenuPart); - + QAction* getMenuAction(const QString& menuName); int findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem); int positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition); - + QHash _actionHash; }; @@ -262,6 +262,7 @@ namespace MenuOption { const QString RunTimingTests = "Run Timing Tests"; const QString ScriptEditor = "Script Editor..."; const QString ScriptedMotorControl = "Enable Scripted Motor Control"; + const QString ShowDSConnectTable = "Show Domain Connection Timing"; const QString ShowBordersEntityNodes = "Show Entity Nodes"; const QString ShowIKConstraints = "Show IK Constraints"; const QString SimpleShadows = "Simple"; diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 56dd69eeeb..ca7a13eb07 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -24,6 +24,7 @@ #include "BandwidthDialog.h" #include "CachesSizeDialog.h" #include "DiskCacheEditor.h" +#include "DomainConnectionDialog.h" #include "HMDToolsDialog.h" #include "LodToolsDialog.h" #include "LoginDialog.h" @@ -52,7 +53,7 @@ void DialogsManager::showLoginDialog() { void DialogsManager::octreeStatsDetails() { if (!_octreeStatsDialog) { _octreeStatsDialog = new OctreeStatsDialog(qApp->getWindow(), qApp->getOcteeSceneStats()); - + if (_hmdToolsDialog) { _hmdToolsDialog->watchWindow(_octreeStatsDialog->windowHandle()); } @@ -65,7 +66,7 @@ void DialogsManager::octreeStatsDetails() { void DialogsManager::cachesSizeDialog() { if (!_cachesSizeDialog) { maybeCreateDialog(_cachesSizeDialog); - + connect(_cachesSizeDialog, SIGNAL(closed()), _cachesSizeDialog, SLOT(deleteLater())); _cachesSizeDialog->show(); } @@ -112,11 +113,11 @@ void DialogsManager::bandwidthDetails() { if (! _bandwidthDialog) { _bandwidthDialog = new BandwidthDialog(qApp->getWindow()); connect(_bandwidthDialog, SIGNAL(closed()), _bandwidthDialog, SLOT(deleteLater())); - + if (_hmdToolsDialog) { _hmdToolsDialog->watchWindow(_bandwidthDialog->windowHandle()); } - + _bandwidthDialog->show(); } _bandwidthDialog->raise(); @@ -125,7 +126,7 @@ void DialogsManager::bandwidthDetails() { void DialogsManager::lodTools() { if (!_lodToolsDialog) { maybeCreateDialog(_lodToolsDialog); - + connect(_lodToolsDialog, SIGNAL(closed()), _lodToolsDialog, SLOT(deleteLater())); _lodToolsDialog->show(); } @@ -172,7 +173,16 @@ void DialogsManager::showIRCLink() { _ircInfoBox->setAttribute(Qt::WA_DeleteOnClose); _ircInfoBox->show(); } - + _ircInfoBox->raise(); } +void DialogsManager::showDomainConnectionDialog() { + if (!_domainConnectionDialog) { + // if the dialog already exists we delete it so the connection data is refreshed + maybeCreateDialog(_domainConnectionDialog); + + _domainConnectionDialog->show(); + _domainConnectionDialog->raise(); + } +} diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index d5d9c33a9a..fc2dad072b 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -34,18 +34,19 @@ class PreferencesDialog; class ScriptEditorWindow; class QMessageBox; class AvatarAppearanceDialog; +class DomainConnectionDialog; class DialogsManager : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY - + public: QPointer getBandwidthDialog() const { return _bandwidthDialog; } QPointer getHMDToolsDialog() const { return _hmdToolsDialog; } QPointer getLodToolsDialog() const { return _lodToolsDialog; } QPointer getOctreeStatsDialog() const { return _octreeStatsDialog; } QPointer getPreferencesDialog() const { return _preferencesDialog; } - + public slots: void toggleAddressBar(); void toggleDiskCacheEditor(); @@ -62,14 +63,15 @@ public slots: void showScriptEditor(); void showIRCLink(); void changeAvatarAppearance(); + void showDomainConnectionDialog(); private slots: void toggleToolWindow(); void hmdToolsClosed(); - + private: DialogsManager() {} - + template void maybeCreateDialog(QPointer& member) { if (!member) { @@ -77,13 +79,13 @@ private: Q_CHECK_PTR(parent); member = new T(parent); Q_CHECK_PTR(member); - + if (_hmdToolsDialog && member->windowHandle()) { _hmdToolsDialog->watchWindow(member->windowHandle()); } } } - + QPointer _addressBarDialog; QPointer _animationsDialog; QPointer _attachmentsDialog; @@ -98,6 +100,7 @@ private: QPointer _preferencesDialog; QPointer _scriptEditor; QPointer _avatarAppearanceDialog; + QPointer _domainConnectionDialog; }; -#endif // hifi_DialogsManager_h \ No newline at end of file +#endif // hifi_DialogsManager_h diff --git a/interface/src/ui/DomainConnectionDialog.cpp b/interface/src/ui/DomainConnectionDialog.cpp new file mode 100644 index 0000000000..98c0401296 --- /dev/null +++ b/interface/src/ui/DomainConnectionDialog.cpp @@ -0,0 +1,74 @@ +// +// DomainConnectionDialog.cpp +// interface/src/ui +// +// Created by Stephen Birarda on 05/26/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include +#include +#include + +#include +#include + +#include "DomainConnectionDialog.h" + +DomainConnectionDialog::DomainConnectionDialog(QWidget* parent) : + QDialog(parent, Qt::Window | Qt::WindowCloseButtonHint) +{ + setWindowTitle("Domain Connection Timing"); + setAttribute(Qt::WA_DeleteOnClose); + + // setup a QTableWidget so we can populate it with our values + QTableWidget* timeTable = new QTableWidget; + + const QStringList TABLE_HEADERS = QStringList() << "Name" << "Timestamp (ms)" << "Delta (ms)" << "Time elapsed (ms)"; + + timeTable->setHorizontalHeaderLabels(TABLE_HEADERS); + timeTable->setColumnCount(TABLE_HEADERS.size()); + + // ask the NodeList for the current values for connection times + QMap times = DependencyManager::get()->getLastConnectionTimes(); + + timeTable->setRowCount(times.size()); + + // setup our data with the values from the NodeList + quint64 firstStepTime = times[NodeList::ConnectionStep::LookupAddress] / USECS_PER_MSEC; + quint64 lastStepTime = firstStepTime; + + int thisRow = 0; + + const QMetaObject &nodeListMeta = NodeList::staticMetaObject; + QMetaEnum stepEnum = nodeListMeta.enumerator(nodeListMeta.indexOfEnumerator("ConnectionStep")); + + for (int i = 0; i < stepEnum.keyCount(); i++) { + NodeList::ConnectionStep step = static_cast(i); + + if (times.contains(step)) { + // When did this step occur, how long since the last step, how long since the start? + quint64 stepTime = times[step] / USECS_PER_MSEC; + quint64 delta = (stepTime - lastStepTime); + quint64 elapsed = stepTime - firstStepTime; + + lastStepTime = stepTime; + + // setup the columns for this row in the table + timeTable->setItem(thisRow, 0, new QTableWidgetItem(stepEnum.valueToKey(i))); + timeTable->setItem(thisRow, 1, new QTableWidgetItem(QString::number(stepTime))); + timeTable->setItem(thisRow, 2, new QTableWidgetItem(QString::number(delta))); + timeTable->setItem(thisRow, 3, new QTableWidgetItem(QString::number(elapsed))); + + ++thisRow; + } + } + + QHBoxLayout* hBoxLayout = new QHBoxLayout; + hBoxLayout->addWidget(timeTable); + + setLayout(hBoxLayout); +} diff --git a/interface/src/ui/DomainConnectionDialog.h b/interface/src/ui/DomainConnectionDialog.h new file mode 100644 index 0000000000..0f49a1ee68 --- /dev/null +++ b/interface/src/ui/DomainConnectionDialog.h @@ -0,0 +1,25 @@ +// +// DomainConnectionDialog.h +// interface/src/ui +// +// Created by Stephen Birarda on 05/26/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_DomainConnectionDialog_h +#define hifi_DomainConnectionDialog_h + +#pragma once + +#include + +class DomainConnectionDialog : public QDialog { + Q_OBJECT +public: + DomainConnectionDialog(QWidget* parent); +}; + +#endif // hifi_DomainConnectionDialog_h diff --git a/interface/src/ui/DomainConnectionTableModel.cpp b/interface/src/ui/DomainConnectionTableModel.cpp deleted file mode 100644 index 0f27b56434..0000000000 --- a/interface/src/ui/DomainConnectionTableModel.cpp +++ /dev/null @@ -1,82 +0,0 @@ -// -// DomainConnectionTableModel.cpp -// interface/src/ui -// -// Created by Stephen Birarda on 05/26/15. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include - -#include -#include - -#include "DomainConnectionTableModel.h" - -DomainConnectionTableModel::DomainConnectionTableModel(QObject* parent) : - QAbstractTableModel(parent) -{ - // ask the NodeList for the current values for connection times - QMap times = DependencyManager::get()->getLastConnectionTimes(); - - // setup our data with the returned values - - quint64 totalTime = 0; - quint64 firstStepTime = times[NodeList::ConnectionStep::LookupAddress] / USECS_PER_MSEC; - quint64 lastStepTime = firstStepTime; - - const QMetaObject &nodeListMeta = NodeList::staticMetaObject; - QMetaEnum stepEnum = nodeListMeta.enumerator(nodeListMeta.indexOfEnumerator("ConnectionStep")); - - for (int i = 0; i < stepEnum.keyCount(); i++) { - NodeList::ConnectionStep step = static_cast(i); - - if (times.contains(step)) { - // When did this step occur, how long since the last step, how long since the start? - _timestamps[_numRows] = times[step] / USECS_PER_MSEC; - _deltas[_numRows] = (_timestamps[_numRows] - lastStepTime); - _totals[_numRows] = _timestamps[_numRows] - firstStepTime; - - // increment the total time by this delta to keep track - totalTime += _deltas[_numRows]; - - lastStepTime = _timestamps[_numRows]; - - // increment our counted number of rows - ++_numRows; - } - } -} - -QVariant DomainConnectionTableModel::headerData(int section, Qt::Orientation orientation, int role) const { - switch(section) { - case 0: - return QVariant("Name"); - case 1: - return QVariant("Timestamp (ms)"); - case 2: - return QVariant("Delta (ms)"); - case 3: - return QVariant("Total Elapsed (ms)"); - default: - return QVariant(); - } -} - -QVariant DomainConnectionTableModel::data(const QModelIndex& index, int role) const { - switch(index.column()) { - case 0: - return _names[index.row()]; - case 1: - return QVariant(_timestamps[index.row()]); - case 2: - return QVariant(_deltas[index.row()]); - case 3: - return QVariant(_totals[index.row()]); - default: - return QVariant(); - } -} diff --git a/interface/src/ui/DomainConnectionTableModel.h b/interface/src/ui/DomainConnectionTableModel.h deleted file mode 100644 index 9c2001dc00..0000000000 --- a/interface/src/ui/DomainConnectionTableModel.h +++ /dev/null @@ -1,41 +0,0 @@ -// -// DomainConnectionTableModel.h -// interface/src/ui -// -// Created by Stephen Birarda on 05/26/15. -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#ifndef hifi_DomainConnectionTableModel_h -#define hifi_DomainConnectionTableModel_h - -#pragma once - -#include - -class DomainConnectionTableModel: public QAbstractTableModel { - Q_OBJECT -public: - DomainConnectionTableModel(QObject* parent = 0); - - const int NUM_COLUMNS = 4; // name, time, delta, since start - - int rowCount(const QModelIndex& parent = QModelIndex()) const { return _numRows; } - int columnCount(const QModelIndex& parent = QModelIndex()) const { return NUM_COLUMNS; } - - QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; - QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const; -private: - int _numRows = 0; - - QVariantList _names; - QList _timestamps; - QList _deltas; - QList _totals; -}; - - -#endif // hifi_DomainConnectionTableModel_h From 5b5518077d67987f95e650660f5f08a0a8f42861 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 May 2015 15:51:00 -0700 Subject: [PATCH 08/33] fix headers and stats for domain connection timing --- interface/src/ui/DomainConnectionDialog.cpp | 27 ++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/DomainConnectionDialog.cpp b/interface/src/ui/DomainConnectionDialog.cpp index 98c0401296..3695fdff4d 100644 --- a/interface/src/ui/DomainConnectionDialog.cpp +++ b/interface/src/ui/DomainConnectionDialog.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -29,7 +30,6 @@ DomainConnectionDialog::DomainConnectionDialog(QWidget* parent) : const QStringList TABLE_HEADERS = QStringList() << "Name" << "Timestamp (ms)" << "Delta (ms)" << "Time elapsed (ms)"; - timeTable->setHorizontalHeaderLabels(TABLE_HEADERS); timeTable->setColumnCount(TABLE_HEADERS.size()); // ask the NodeList for the current values for connection times @@ -37,6 +37,8 @@ DomainConnectionDialog::DomainConnectionDialog(QWidget* parent) : timeTable->setRowCount(times.size()); + timeTable->setHorizontalHeaderLabels(TABLE_HEADERS); + // setup our data with the values from the NodeList quint64 firstStepTime = times[NodeList::ConnectionStep::LookupAddress] / USECS_PER_MSEC; quint64 lastStepTime = firstStepTime; @@ -67,8 +69,31 @@ DomainConnectionDialog::DomainConnectionDialog(QWidget* parent) : } } + timeTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + timeTable->resizeColumnsToContents(); + + const int TABLE_HEADER_GAP = 4; + + // figure out the size of the table so we can size the dialog + int tableWidth = timeTable->verticalHeader()->width() + TABLE_HEADER_GAP; + for (int i = 0; i < timeTable->columnCount(); i++) { + tableWidth += timeTable->columnWidth(i); + } + + int tableHeight = timeTable->horizontalHeader()->height() + TABLE_HEADER_GAP; + for (int i = 0; i < timeTable->rowCount(); i++) { + tableHeight += timeTable->rowHeight(i); + } + + timeTable->setMaximumSize(QSize(tableWidth, tableHeight)); + timeTable->setMinimumSize(timeTable->maximumSize()); + QHBoxLayout* hBoxLayout = new QHBoxLayout; hBoxLayout->addWidget(timeTable); + hBoxLayout->setSizeConstraint(QLayout::SetMinimumSize); + + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); setLayout(hBoxLayout); + adjustSize(); } From 08affbfd0db6f9036cf35d4d3e596f6941dcd799 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 May 2015 16:14:12 -0700 Subject: [PATCH 09/33] fix table size calculations --- interface/src/ui/DomainConnectionDialog.cpp | 35 +++++++++------------ 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/interface/src/ui/DomainConnectionDialog.cpp b/interface/src/ui/DomainConnectionDialog.cpp index 3695fdff4d..0e82b2ed93 100644 --- a/interface/src/ui/DomainConnectionDialog.cpp +++ b/interface/src/ui/DomainConnectionDialog.cpp @@ -69,30 +69,25 @@ DomainConnectionDialog::DomainConnectionDialog(QWidget* parent) : } } - timeTable->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - timeTable->resizeColumnsToContents(); - - const int TABLE_HEADER_GAP = 4; - - // figure out the size of the table so we can size the dialog - int tableWidth = timeTable->verticalHeader()->width() + TABLE_HEADER_GAP; - for (int i = 0; i < timeTable->columnCount(); i++) { - tableWidth += timeTable->columnWidth(i); - } - - int tableHeight = timeTable->horizontalHeader()->height() + TABLE_HEADER_GAP; - for (int i = 0; i < timeTable->rowCount(); i++) { - tableHeight += timeTable->rowHeight(i); - } - - timeTable->setMaximumSize(QSize(tableWidth, tableHeight)); - timeTable->setMinimumSize(timeTable->maximumSize()); - QHBoxLayout* hBoxLayout = new QHBoxLayout; hBoxLayout->addWidget(timeTable); hBoxLayout->setSizeConstraint(QLayout::SetMinimumSize); - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + timeTable->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + timeTable->resizeColumnsToContents(); + + // figure out the size of the table so we can size the dialog + int tableWidth = timeTable->verticalHeader()->sizeHint().width(); + for (int i = 0; i < timeTable->columnCount(); i++) { + tableWidth += timeTable->columnWidth(i); + } + + int tableHeight = timeTable->horizontalHeader()->sizeHint().height(); + for (int i = 0; i < timeTable->rowCount(); i++) { + tableHeight += timeTable->rowHeight(i); + } + + timeTable->setMinimumSize(tableWidth, tableHeight); setLayout(hBoxLayout); adjustSize(); From 9cb6ccaa46f591e5fc59753e4eb226039083cf2a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 26 May 2015 16:49:18 -0700 Subject: [PATCH 10/33] thread safe timing of domain connection --- interface/src/ui/DomainConnectionDialog.cpp | 4 +++- libraries/networking/src/NodeList.cpp | 10 +++++++--- libraries/networking/src/NodeList.h | 5 ++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/interface/src/ui/DomainConnectionDialog.cpp b/interface/src/ui/DomainConnectionDialog.cpp index 0e82b2ed93..f5fdc530d5 100644 --- a/interface/src/ui/DomainConnectionDialog.cpp +++ b/interface/src/ui/DomainConnectionDialog.cpp @@ -69,11 +69,12 @@ DomainConnectionDialog::DomainConnectionDialog(QWidget* parent) : } } + // setup a horizontal box layout QHBoxLayout* hBoxLayout = new QHBoxLayout; hBoxLayout->addWidget(timeTable); hBoxLayout->setSizeConstraint(QLayout::SetMinimumSize); - timeTable->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + // resize the table columns timeTable->resizeColumnsToContents(); // figure out the size of the table so we can size the dialog @@ -87,6 +88,7 @@ DomainConnectionDialog::DomainConnectionDialog(QWidget* parent) : tableHeight += timeTable->rowHeight(i); } + // set the minimum size of the table to whatever we got timeTable->setMinimumSize(tableWidth, tableHeight); setLayout(hBoxLayout); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 4bfedfd555..f9189e47d7 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -270,9 +270,6 @@ void NodeList::reset() { if (_dtlsSocket) { disconnect(_dtlsSocket, 0, this, 0); } - - // reset the connection times - _lastConnectionTimes.clear(); } void NodeList::addNodeTypeToInterestSet(NodeType_t nodeTypeToAdd) { @@ -673,6 +670,13 @@ void NodeList::flagTimeForConnectionStep(NodeList::ConnectionStep connectionStep } void NodeList::flagTimeForConnectionStep(NodeList::ConnectionStep connectionStep, quint64 timestamp) { + QWriteLocker writeLock(&_connectionTimeLock); + + if (connectionStep == NodeList::ConnectionStep::LookupAddress) { + // we clear the current times if the user just fired off a lookup + _lastConnectionTimes.clear(); + } + // we only add a timestamp on the first call for each NodeList::ConnectionStep if (!_lastConnectionTimes.contains(connectionStep)) { _lastConnectionTimes[connectionStep] = timestamp; diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 8d40e252e5..acdfe535f1 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -84,8 +84,10 @@ public: int processDomainServerList(const QByteArray& packet); - const QMap getLastConnectionTimes() const { return _lastConnectionTimes; } + const QMap getLastConnectionTimes() const + { QReadLocker readLock(&_connectionTimeLock); return _lastConnectionTimes; } void flagTimeForConnectionStep(NodeList::ConnectionStep connectionStep); + void resetConnectionTimes() { QWriteLocker writeLock(&_connectionTimeLock); _lastConnectionTimes.clear(); } void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; } void sendAssignment(Assignment& assignment); @@ -128,6 +130,7 @@ private: bool _hasCompletedInitialSTUNFailure; unsigned int _stunRequestsSinceSuccess; + mutable QReadWriteLock _connectionTimeLock { }; QMap _lastConnectionTimes; friend class Application; From 4b6b34836db8aaf4b25bb3889f88732a57a05765 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 May 2015 10:30:45 -0700 Subject: [PATCH 11/33] repair anomalies in timing table --- libraries/networking/src/NodeList.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index f9189e47d7..113ec4f155 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -677,8 +677,21 @@ void NodeList::flagTimeForConnectionStep(NodeList::ConnectionStep connectionStep _lastConnectionTimes.clear(); } - // we only add a timestamp on the first call for each NodeList::ConnectionStep if (!_lastConnectionTimes.contains(connectionStep)) { + // if there is no time for existing step add a timestamp on the first call for each NodeList::ConnectionStep _lastConnectionTimes[connectionStep] = timestamp; + } else { + // if the existing time for this step is before the nearest sibling before then replace it + // this handles the case where audio comes in after an address lookup after the previous times have been cleared + quint64 currentTime = _lastConnectionTimes[connectionStep]; + + // go down the sibling steps and check if the registered time is actually before the sibling + for (int i = ((int) connectionStep) - 1; i >= 0; i--) { + NodeList::ConnectionStep thisStep = static_cast(i); + if (_lastConnectionTimes.contains(thisStep) && _lastConnectionTimes[thisStep] >= currentTime) { + _lastConnectionTimes[connectionStep] = timestamp; + break; + } + } } } From c7c542ef4c5dd147f45635225e13c67aa7a7b34b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 May 2015 14:24:15 -0700 Subject: [PATCH 12/33] decouple STUN from DS check in --- domain-server/src/DomainServer.cpp | 38 +++------- domain-server/src/DomainServer.h | 1 - libraries/networking/src/DomainHandler.cpp | 1 - libraries/networking/src/LimitedNodeList.cpp | 77 +++++++++++++++++++- libraries/networking/src/LimitedNodeList.h | 28 ++++--- libraries/networking/src/NodeList.cpp | 73 +++---------------- libraries/networking/src/NodeList.h | 6 +- 7 files changed, 116 insertions(+), 108 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 6bf5c62710..2c784abe54 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -354,37 +354,23 @@ bool DomainServer::optionallySetupAssignmentPayment() { void DomainServer::setupAutomaticNetworking() { auto nodeList = DependencyManager::get(); - const int STUN_REFLEXIVE_KEEPALIVE_INTERVAL_MSECS = 10 * 1000; - const int STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS = 30 * 1000; - - // setup our timer to check our IP via stun every X seconds - QTimer* dynamicIPTimer = new QTimer(this); - connect(dynamicIPTimer, &QTimer::timeout, this, &DomainServer::requestCurrentPublicSocketViaSTUN); - _automaticNetworkingSetting = _settingsManager.valueOrDefaultValueForKeyPath(METAVERSE_AUTOMATIC_NETWORKING_KEY_PATH).toString(); if (_automaticNetworkingSetting == FULL_AUTOMATIC_NETWORKING_VALUE) { - dynamicIPTimer->start(STUN_REFLEXIVE_KEEPALIVE_INTERVAL_MSECS); - - // setup a timer to heartbeat with the ice-server every so often - QTimer* iceHeartbeatTimer = new QTimer(this); - connect(iceHeartbeatTimer, &QTimer::timeout, this, &DomainServer::performICEUpdates); - iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS); - // call our sendHeartbeatToIceServer immediately anytime a local or public socket changes connect(nodeList.data(), &LimitedNodeList::localSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer); connect(nodeList.data(), &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::sendHeartbeatToIceServer); - // attempt to update our public socket now, this will send a heartbeat once we get public socket - requestCurrentPublicSocketViaSTUN(); - - // in case the STUN lookup is still happening we should re-request a public socket once we get that address - connect(&nodeList->getSTUNSockAddr(), &HifiSockAddr::lookupCompleted, - this, &DomainServer::requestCurrentPublicSocketViaSTUN); + // we need this DS to know what our public IP is - start trying to figure that out now + nodeList->startSTUNPublicSocketUpdate(); + // setup a timer to heartbeat with the ice-server every so often + QTimer* iceHeartbeatTimer = new QTimer(this); + connect(iceHeartbeatTimer, &QTimer::timeout, this, &DomainServer::performICEUpdates); + iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS); } if (!didSetupAccountManagerWithAccessToken()) { @@ -404,14 +390,12 @@ void DomainServer::setupAutomaticNetworking() { << uuidStringWithoutCurlyBraces(domainID) << "via" << _oauthProviderURL.toString(); if (_automaticNetworkingSetting == IP_ONLY_AUTOMATIC_NETWORKING_VALUE) { - dynamicIPTimer->start(STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS); - - // send public socket changes to the data server so nodes can find us at our new IP + // send any public socket changes to the data server so nodes can find us at our new IP connect(nodeList.data(), &LimitedNodeList::publicSockAddrChanged, this, &DomainServer::performIPAddressUpdate); - // attempt to update our sockets now - requestCurrentPublicSocketViaSTUN(); + // have the LNL enable public socket updating via STUN + nodeList->startSTUNPublicSocketUpdate(); } else { // send our heartbeat to data server so it knows what our network settings are sendHeartbeatToDataServer(); @@ -1216,10 +1200,6 @@ void DomainServer::transactionJSONCallback(const QJsonObject& data) { } } -void DomainServer::requestCurrentPublicSocketViaSTUN() { - DependencyManager::get()->sendSTUNRequest(); -} - QJsonObject jsonForDomainSocketUpdate(const HifiSockAddr& socket) { const QString SOCKET_NETWORK_ADDRESS_KEY = "network_address"; const QString SOCKET_PORT_KEY = "port"; diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 9e9d3f13f3..22e3efa378 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -62,7 +62,6 @@ private slots: void setupPendingAssignmentCredits(); void sendPendingTransactionsToServer(); - void requestCurrentPublicSocketViaSTUN(); void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr); void performICEUpdates(); void sendHeartbeatToDataServer() { sendHeartbeatToDataServer(QString()); } diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 622eec6fd5..9dab4152d9 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -181,7 +181,6 @@ void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) { DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::SetDomainSocket); - qCDebug(networking, "DS at %s is at %s", _hostname.toLocal8Bit().constData(), _sockAddr.getAddress().toString().toLocal8Bit().constData()); diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 34e0576e81..bd412be414 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -596,6 +596,22 @@ const int NUM_BYTES_STUN_HEADER = 20; void LimitedNodeList::sendSTUNRequest() { + static quint64 lastTimeStamp = usecTimestampNow(); + lastTimeStamp = usecTimestampNow(); + + const int NUM_INITIAL_STUN_REQUESTS_BEFORE_FAIL = 10; + + if (!_hasCompletedInitialSTUN) { + qCDebug(networking) << "Sending intial stun request to" << STUN_SERVER_HOSTNAME; + + if (_numInitialSTUNRequests > NUM_INITIAL_STUN_REQUESTS_BEFORE_FAIL) { + // we're still trying to do our initial STUN we're over the fail threshold + stopInitialSTUNUpdate(false); + } + + ++_numInitialSTUNRequests; + } + unsigned char stunRequestPacket[NUM_BYTES_STUN_HEADER]; int packetIndex = 0; @@ -652,8 +668,6 @@ bool LimitedNodeList::processSTUNResponse(const QByteArray& packet) { const int NUM_BYTES_FAMILY_ALIGN = 1; const uint8_t IPV4_FAMILY_NETWORK_ORDER = htons(0x01) >> 8; - - int byteIndex = attributeStartIndex + NUM_BYTES_STUN_ATTR_TYPE_AND_LENGTH + NUM_BYTES_FAMILY_ALIGN; uint8_t addressFamily = 0; @@ -679,6 +693,11 @@ bool LimitedNodeList::processSTUNResponse(const QByteArray& packet) { QHostAddress newPublicAddress = QHostAddress(stunAddress); if (newPublicAddress != _publicSockAddr.getAddress() || newPublicPort != _publicSockAddr.getPort()) { + if (!_hasCompletedInitialSTUN) { + // if we're here we have definitely completed our initial STUN sequence + stopInitialSTUNUpdate(true); + } + _publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort); qCDebug(networking, "New public socket received from STUN server is %s:%hu", @@ -707,6 +726,60 @@ bool LimitedNodeList::processSTUNResponse(const QByteArray& packet) { return false; } +void LimitedNodeList::startSTUNPublicSocketUpdate() { + assert(!_initialSTUNTimer); + + if (!_initialSTUNTimer) { + // if we don't know the STUN IP yet we need to have ourselves be called once it is known + if (_stunSockAddr.getAddress().isNull()) { + connect(&_stunSockAddr, &HifiSockAddr::lookupCompleted, this, &LimitedNodeList::startSTUNPublicSocketUpdate); + } else { + // setup our initial STUN timer here so we can quickly find out our public IP address + _initialSTUNTimer = new QTimer(this); + + connect(_initialSTUNTimer.data(), &QTimer::timeout, this, &LimitedNodeList::sendSTUNRequest); + + const int STUN_INITIAL_UPDATE_INTERVAL_MSECS = 250; + _initialSTUNTimer->start(STUN_INITIAL_UPDATE_INTERVAL_MSECS); + } + } +} + +void LimitedNodeList::stopInitialSTUNUpdate(bool success) { + _hasCompletedInitialSTUN = true; + + if (!success) { + // if we're here this was the last failed STUN request + // use our DS as our stun server + qCDebug(networking, "Failed to lookup public address via STUN server at %s:%hu.", + STUN_SERVER_HOSTNAME, STUN_SERVER_PORT); + 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()); + + // we have changed the publicSockAddr, so emit our signal + emit publicSockAddrChanged(_publicSockAddr); + } + + assert(_initialSTUNTimer); + + // stop our initial fast timer + if (_initialSTUNTimer) { + _initialSTUNTimer->stop(); + _initialSTUNTimer->deleteLater(); + } + + // We now setup a timer here to fire every so often to check that our IP address has not changed. + // Or, if we failed - if will check if we can eventually get a public socket + const int STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS = 30 * 1000; + + QTimer* stunOccasionalTimer = new QTimer(this); + connect(stunOccasionalTimer, &QTimer::timeout, this, &LimitedNodeList::sendSTUNRequest); + + stunOccasionalTimer->start(STUN_IP_ADDRESS_CHECK_INTERVAL_MSECS); +} + void LimitedNodeList::updateLocalSockAddr() { HifiSockAddr newSockAddr(getLocalAddress(), _nodeSocket.localPort()); if (newSockAddr != _localSockAddr) { diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 3401cd7ca8..faaeac3c57 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -21,13 +21,14 @@ #include // not on windows, not needed for mac or windows #endif -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include @@ -130,6 +131,8 @@ public: const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, bool canAdjustLocks, bool canRez); + bool hasCompletedInitialSTUN() const { return _hasCompletedInitialSTUN; } + const HifiSockAddr& getLocalSockAddr() const { return _localSockAddr; } const HifiSockAddr& getSTUNSockAddr() const { return _stunSockAddr; } @@ -149,7 +152,6 @@ public: const QUuid& packetHeaderID = QUuid()); QByteArray constructPingReplyPacket(const QByteArray& pingPacket, const QUuid& packetHeaderID = QUuid()); - virtual void sendSTUNRequest(); virtual bool processSTUNResponse(const QByteArray& packet); void sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr, @@ -210,6 +212,9 @@ public slots: void updateLocalSockAddr(); + void startSTUNPublicSocketUpdate(); + virtual void sendSTUNRequest(); + void killNodeWithUUID(const QUuid& nodeUUID); signals: void uuidChanged(const QUuid& ownerUUID, const QUuid& oldUUID); @@ -240,6 +245,8 @@ protected: void handleNodeKill(const SharedNodePointer& node); + void stopInitialSTUNUpdate(bool success); + QUuid _sessionUUID; NodeHash _nodeHash; QReadWriteLock _nodeMutex; @@ -259,6 +266,10 @@ protected: std::unordered_map _packetSequenceNumbers; + QPointer _initialSTUNTimer; + int _numInitialSTUNRequests = 0; + bool _hasCompletedInitialSTUN = false; + template void eachNodeHashIterator(IteratorLambda functor) { QWriteLocker writeLock(&_nodeMutex); @@ -268,7 +279,6 @@ protected: functor(it); } } - }; #endif // hifi_LimitedNodeList_h diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 113ec4f155..20d9604c59 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -36,9 +36,7 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned _nodeTypesOfInterest(), _domainHandler(this), _numNoReplyDomainCheckIns(0), - _assignmentServerSocket(), - _hasCompletedInitialSTUNFailure(false), - _stunRequestsSinceSuccess(0) + _assignmentServerSocket() { static bool firstCall = true; if (firstCall) { @@ -63,6 +61,12 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned // fire off any pending DS path query when we get socket information connect(&_domainHandler, &DomainHandler::completedSocketDiscovery, this, &NodeList::sendPendingDSPathQuery); + // send a domain server check in immediately once the DS socket is known + connect(&_domainHandler, &DomainHandler::completedSocketDiscovery, this, &NodeList::sendDomainServerCheckIn); + + // send a domain server check in immediately if there is a public socket change + connect(this, &LimitedNodeList::publicSockAddrChanged, this, &NodeList::sendDomainServerCheckIn); + // clear our NodeList when the domain changes connect(&_domainHandler, &DomainHandler::disconnectedFromDomain, this, &NodeList::reset); @@ -79,6 +83,9 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset); qRegisterMetaType("NodeList::ConnectionStep"); + + // we definitely want STUN to update our public socket, so call the LNL to kick that off + startSTUNPublicSocketUpdate(); } qint64 NodeList::sendStats(const QJsonObject& statsObject, const HifiSockAddr& destination) { @@ -233,9 +240,6 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr } else { qCDebug(networking) << "Reply does not match either local or public socket for domain. Will not connect."; } - - // immediately send a domain-server check in now that we have channel to talk to domain-server on - sendDomainServerCheckIn(); } case PacketTypeStunResponse: { // a STUN packet begins with 00, we've checked the second zero with packetVersionMatch @@ -280,57 +284,13 @@ void NodeList::addSetOfNodeTypesToNodeInterestSet(const NodeSet& setOfNodeTypes) _nodeTypesOfInterest.unite(setOfNodeTypes); } - -const unsigned int NUM_STUN_REQUESTS_BEFORE_FALLBACK = 5; - -void NodeList::sendSTUNRequest() { - - if (!_hasCompletedInitialSTUNFailure) { - qCDebug(networking) << "Sending intial stun request to" << STUN_SERVER_HOSTNAME; - } - - LimitedNodeList::sendSTUNRequest(); - - _stunRequestsSinceSuccess++; - - if (_stunRequestsSinceSuccess >= NUM_STUN_REQUESTS_BEFORE_FALLBACK) { - if (!_hasCompletedInitialSTUNFailure) { - // if we're here this was the last failed STUN request - // use our DS as our stun server - qCDebug(networking, "Failed to lookup public address via STUN server at %s:%hu. Using DS for STUN.", - STUN_SERVER_HOSTNAME, STUN_SERVER_PORT); - - _hasCompletedInitialSTUNFailure = true; - } - - // reset the public address and port - // use 0 so the DS knows to act as out STUN server - _publicSockAddr = HifiSockAddr(QHostAddress(), _nodeSocket.localPort()); - } -} - -bool NodeList::processSTUNResponse(const QByteArray& packet) { - if (LimitedNodeList::processSTUNResponse(packet)) { - // reset the number of failed STUN requests since last success - _stunRequestsSinceSuccess = 0; - - _hasCompletedInitialSTUNFailure = true; - - return true; - } else { - return false; - } -} - void NodeList::sendDomainServerCheckIn() { - if (_publicSockAddr.isNull() && !_hasCompletedInitialSTUNFailure) { + if (_publicSockAddr.isNull()) { // we don't know our public socket and we need to send it to the domain server - // send a STUN request to figure it out - sendSTUNRequest(); + qCDebug(networking) << "Waiting for inital public socket from STUN. Will not send domain-server check in."; } else if (_domainHandler.getIP().isNull() && _domainHandler.requiresICE()) { handleICEConnectionToDomainServer(); } else if (!_domainHandler.getIP().isNull()) { - bool isUsingDTLS = false; PacketType domainPacketType = !_domainHandler.isConnected() @@ -375,7 +335,6 @@ void NodeList::sendDomainServerCheckIn() { // pack our data to send to the domain-server packetStream << _ownerType << _publicSockAddr << _localSockAddr << _nodeTypesOfInterest.toList(); - // if this is a connect request, and we can present a username signature, send it along if (!_domainHandler.isConnected()) { DataServerAccountInfo& accountInfo = AccountManager::getInstance().getAccountInfo(); @@ -395,14 +354,6 @@ void NodeList::sendDomainServerCheckIn() { writeUnverifiedDatagram(domainServerPacket, _domainHandler.getSockAddr()); } - const int NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST = 5; - static unsigned int numDomainCheckins = 0; - - // send a STUN request every Nth domain server check in so we update our public socket, if required - if (numDomainCheckins++ % NUM_DOMAIN_SERVER_CHECKINS_PER_STUN_REQUEST == 0) { - sendSTUNRequest(); - } - if (_numNoReplyDomainCheckIns >= MAX_SILENT_DOMAIN_SERVER_CHECK_INS) { // we haven't heard back from DS in MAX_SILENT_DOMAIN_SERVER_CHECK_INS // so emit our signal that says that diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index acdfe535f1..d52dd6e101 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -56,6 +56,7 @@ public: SendFirstPingsToDS, SetDomainHostname, SetDomainSocket, + ForcedSTUNRequest, SendFirstDSCheckIn, ReceiveFirstDSList, SendFirstAudioPing, @@ -110,9 +111,6 @@ private: NodeList(NodeList const&); // Don't implement, needed to avoid copies of singleton void operator=(NodeList const&); // Don't implement, needed to avoid copies of singleton - void sendSTUNRequest(); - bool processSTUNResponse(const QByteArray& packet); - void processDomainServerAuthRequest(const QByteArray& packet); void requestAuthForDomainServer(); void activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode); @@ -127,8 +125,6 @@ private: DomainHandler _domainHandler; int _numNoReplyDomainCheckIns; HifiSockAddr _assignmentServerSocket; - bool _hasCompletedInitialSTUNFailure; - unsigned int _stunRequestsSinceSuccess; mutable QReadWriteLock _connectionTimeLock { }; QMap _lastConnectionTimes; From 734798a365c7081aca44f034eb4c07b3811e8231 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 May 2015 15:50:31 -0700 Subject: [PATCH 13/33] fix move of connection steps to LNL --- interface/src/ui/DomainConnectionDialog.cpp | 34 +++++----- libraries/audio-client/src/AudioClient.cpp | 4 +- libraries/networking/src/LimitedNodeList.cpp | 55 ++++++++++++++-- libraries/networking/src/LimitedNodeList.h | 37 ++++++++++- libraries/networking/src/NodeList.cpp | 66 +++++--------------- libraries/networking/src/NodeList.h | 31 --------- 6 files changed, 122 insertions(+), 105 deletions(-) diff --git a/interface/src/ui/DomainConnectionDialog.cpp b/interface/src/ui/DomainConnectionDialog.cpp index f5fdc530d5..c0471dc5e1 100644 --- a/interface/src/ui/DomainConnectionDialog.cpp +++ b/interface/src/ui/DomainConnectionDialog.cpp @@ -28,19 +28,21 @@ DomainConnectionDialog::DomainConnectionDialog(QWidget* parent) : // setup a QTableWidget so we can populate it with our values QTableWidget* timeTable = new QTableWidget; + timeTable->setEditTriggers(QAbstractItemView::NoEditTriggers); + const QStringList TABLE_HEADERS = QStringList() << "Name" << "Timestamp (ms)" << "Delta (ms)" << "Time elapsed (ms)"; timeTable->setColumnCount(TABLE_HEADERS.size()); // ask the NodeList for the current values for connection times - QMap times = DependencyManager::get()->getLastConnectionTimes(); + QMap times = DependencyManager::get()->getLastConnectionTimes(); timeTable->setRowCount(times.size()); timeTable->setHorizontalHeaderLabels(TABLE_HEADERS); // setup our data with the values from the NodeList - quint64 firstStepTime = times[NodeList::ConnectionStep::LookupAddress] / USECS_PER_MSEC; + quint64 firstStepTime = times.firstKey() / USECS_PER_MSEC; quint64 lastStepTime = firstStepTime; int thisRow = 0; @@ -48,25 +50,23 @@ DomainConnectionDialog::DomainConnectionDialog(QWidget* parent) : const QMetaObject &nodeListMeta = NodeList::staticMetaObject; QMetaEnum stepEnum = nodeListMeta.enumerator(nodeListMeta.indexOfEnumerator("ConnectionStep")); - for (int i = 0; i < stepEnum.keyCount(); i++) { - NodeList::ConnectionStep step = static_cast(i); + foreach(quint64 timestamp, times.keys()) { + // When did this step occur, how long since the last step, how long since the start? + quint64 stepTime = timestamp / USECS_PER_MSEC; + quint64 delta = (stepTime - lastStepTime); + quint64 elapsed = stepTime - firstStepTime; - if (times.contains(step)) { - // When did this step occur, how long since the last step, how long since the start? - quint64 stepTime = times[step] / USECS_PER_MSEC; - quint64 delta = (stepTime - lastStepTime); - quint64 elapsed = stepTime - firstStepTime; + lastStepTime = stepTime; - lastStepTime = stepTime; + // setup the columns for this row in the table + int stepIndex = (int) times.value(timestamp); - // setup the columns for this row in the table - timeTable->setItem(thisRow, 0, new QTableWidgetItem(stepEnum.valueToKey(i))); - timeTable->setItem(thisRow, 1, new QTableWidgetItem(QString::number(stepTime))); - timeTable->setItem(thisRow, 2, new QTableWidgetItem(QString::number(delta))); - timeTable->setItem(thisRow, 3, new QTableWidgetItem(QString::number(elapsed))); + timeTable->setItem(thisRow, 0, new QTableWidgetItem(stepEnum.valueToKey(stepIndex))); + timeTable->setItem(thisRow, 1, new QTableWidgetItem(QString::number(stepTime))); + timeTable->setItem(thisRow, 2, new QTableWidgetItem(QString::number(delta))); + timeTable->setItem(thisRow, 3, new QTableWidgetItem(QString::number(elapsed))); - ++thisRow; - } + ++thisRow; } // setup a horizontal box layout diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 2bf25a3575..5c123b3bd0 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -875,7 +875,7 @@ void AudioClient::handleAudioInput() { _stats.sentPacket(); - nodeList->flagTimeForConnectionStep(NodeList::ConnectionStep::SendFirstAudioPacket); + nodeList->flagTimeForConnectionStep(NodeList::ConnectionStep::SendAudioPacket); int packetBytes = currentPacketPtr - audioDataPacket; nodeList->writeDatagram(audioDataPacket, packetBytes, audioMixer); @@ -923,7 +923,7 @@ void AudioClient::sendMuteEnvironmentPacket() { } void AudioClient::addReceivedAudioToStream(const QByteArray& audioByteArray) { - DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::ReceiveFirstAudioPacket); + DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveFirstAudioPacket); if (_audioOutput) { // Audio output must exist and be correctly set up if we're going to process received audio diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index bd412be414..76bdc5d089 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -61,6 +61,8 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short firstCall = false; } + qRegisterMetaType("ConnectionStep"); + _nodeSocket.bind(QHostAddress::AnyIPv4, socketListenPort); qCDebug(networking) << "NodeList socket is listening on" << _nodeSocket.localPort(); @@ -636,6 +638,8 @@ void LimitedNodeList::sendSTUNRequest() { QUuid randomUUID = QUuid::createUuid(); memcpy(stunRequestPacket + packetIndex, randomUUID.toRfc4122().data(), NUM_TRANSACTION_ID_BYTES); + flagTimeForConnectionStep(ConnectionStep::SendSTUNRequest); + _nodeSocket.writeDatagram((char*) stunRequestPacket, sizeof(stunRequestPacket), _stunSockAddr.getAddress(), _stunSockAddr.getPort()); } @@ -693,18 +697,20 @@ bool LimitedNodeList::processSTUNResponse(const QByteArray& packet) { QHostAddress newPublicAddress = QHostAddress(stunAddress); if (newPublicAddress != _publicSockAddr.getAddress() || newPublicPort != _publicSockAddr.getPort()) { - if (!_hasCompletedInitialSTUN) { - // if we're here we have definitely completed our initial STUN sequence - stopInitialSTUNUpdate(true); - } - _publicSockAddr = HifiSockAddr(newPublicAddress, newPublicPort); qCDebug(networking, "New public socket received from STUN server is %s:%hu", _publicSockAddr.getAddress().toString().toLocal8Bit().constData(), _publicSockAddr.getPort()); + if (!_hasCompletedInitialSTUN) { + // if we're here we have definitely completed our initial STUN sequence + stopInitialSTUNUpdate(true); + } + emit publicSockAddrChanged(_publicSockAddr); + + flagTimeForConnectionStep(ConnectionStep::SetPublicSocketFromSTUN); } return true; @@ -741,6 +747,9 @@ void LimitedNodeList::startSTUNPublicSocketUpdate() { const int STUN_INITIAL_UPDATE_INTERVAL_MSECS = 250; _initialSTUNTimer->start(STUN_INITIAL_UPDATE_INTERVAL_MSECS); + + // send an initial STUN request right away + sendSTUNRequest(); } } } @@ -760,6 +769,8 @@ void LimitedNodeList::stopInitialSTUNUpdate(bool success) { // we have changed the publicSockAddr, so emit our signal emit publicSockAddrChanged(_publicSockAddr); + + flagTimeForConnectionStep(ConnectionStep::SetPublicSocketFromSTUN); } assert(_initialSTUNTimer); @@ -847,3 +858,37 @@ bool LimitedNodeList::getLocalServerPortFromSharedMemory(const QString key, quin return true; } } + +void LimitedNodeList::flagTimeForConnectionStep(ConnectionStep connectionStep) { + QMetaObject::invokeMethod(this, "flagTimeForConnectionStep", + Q_ARG(ConnectionStep, connectionStep), + Q_ARG(quint64, usecTimestampNow())); +} + +void LimitedNodeList::flagTimeForConnectionStep(ConnectionStep connectionStep, quint64 timestamp) { + if (!_areConnectionTimesComplete) { + QWriteLocker writeLock(&_connectionTimeLock); + + if (connectionStep == ConnectionStep::LookupAddress) { + // we clear the current times if the user just fired off a lookup + _lastConnectionTimes.clear(); + _areConnectionTimesComplete = false; + } + + // anything > than sending the first DS check should not come before the DS check in, so we drop those + // this handles the case where you lookup an address and get packets in the existing domain before changing domains + if (connectionStep > LimitedNodeList::ConnectionStep::SendDSCheckIn + && (_lastConnectionTimes.key(ConnectionStep::SendDSCheckIn) == 0 + || timestamp <= _lastConnectionTimes.key(ConnectionStep::SendDSCheckIn))) { + return; + } + + // if there is no time for existing step add a timestamp on the first call for each ConnectionStep + _lastConnectionTimes[timestamp] = connectionStep; + + // if this is a received audio packet we consider our connection times complete + if (connectionStep == ConnectionStep::ReceiveFirstAudioPacket) { + _areConnectionTimesComplete = true; + } + } +} diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index faaeac3c57..990508e89d 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -80,8 +80,30 @@ namespace PingType { class LimitedNodeList : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY - public: + + enum ConnectionStep { + LookupAddress = 1, + HandleAddress, + SendSTUNRequest, + SetPublicSocketFromSTUN, + SetICEServerHostname, + SetICEServerSocket, + SendICEServerHearbeat, + ReceiveDSPeerInformation, + SendPingsToDS, + SetDomainHostname, + SetDomainSocket, + SendDSCheckIn, + ReceiveDSList, + SendAudioPing, + SetAudioMixerSocket, + SendAudioPacket, + ReceiveFirstAudioPacket + }; + + Q_ENUMS(ConnectionStep); + const QUuid& getSessionUUID() const { return _sessionUUID; } void setSessionUUID(const QUuid& sessionUUID); @@ -204,6 +226,11 @@ public: void putLocalPortIntoSharedMemory(const QString key, QObject* parent, quint16 localPort); bool getLocalServerPortFromSharedMemory(const QString key, quint16& localPort); + const QMap getLastConnectionTimes() const + { QReadLocker readLock(&_connectionTimeLock); return _lastConnectionTimes; } + void flagTimeForConnectionStep(ConnectionStep connectionStep); + + public slots: void reset(); void eraseAllNodes(); @@ -269,6 +296,12 @@ protected: QPointer _initialSTUNTimer; int _numInitialSTUNRequests = 0; bool _hasCompletedInitialSTUN = false; + quint64 _firstSTUNTime = 0; + quint64 _publicSocketUpdateTime = 0; + + mutable QReadWriteLock _connectionTimeLock { }; + QMap _lastConnectionTimes; + bool _areConnectionTimesComplete = false; template void eachNodeHashIterator(IteratorLambda functor) { @@ -279,6 +312,8 @@ protected: functor(it); } } +private slots: + void flagTimeForConnectionStep(ConnectionStep connectionStep, quint64 timestamp); }; #endif // hifi_LimitedNodeList_h diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 20d9604c59..b7870bcdea 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -82,8 +82,6 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned // clear our NodeList when logout is requested connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset); - qRegisterMetaType("NodeList::ConnectionStep"); - // we definitely want STUN to update our public socket, so call the LNL to kick that off startSTUNPublicSocketUpdate(); } @@ -230,15 +228,18 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr case PacketTypeUnverifiedPingReply: { qCDebug(networking) << "Received reply from domain-server on" << senderSockAddr; - // for now we're unsafely assuming this came back from the domain - if (senderSockAddr == _domainHandler.getICEPeer().getLocalSocket()) { - qCDebug(networking) << "Connecting to domain using local socket"; - _domainHandler.activateICELocalSocket(); - } else if (senderSockAddr == _domainHandler.getICEPeer().getPublicSocket()) { - qCDebug(networking) << "Conecting to domain using public socket"; - _domainHandler.activateICEPublicSocket(); - } else { - qCDebug(networking) << "Reply does not match either local or public socket for domain. Will not connect."; + if (_domainHandler.getIP().isNull()) { + // for now we're unsafely assuming this came back from the domain + if (senderSockAddr == _domainHandler.getICEPeer().getLocalSocket()) { + qCDebug(networking) << "Connecting to domain using local socket"; + _domainHandler.activateICELocalSocket(); + } else if (senderSockAddr == _domainHandler.getICEPeer().getPublicSocket()) { + qCDebug(networking) << "Conecting to domain using public socket"; + _domainHandler.activateICEPublicSocket(); + } else { + qCDebug(networking) << "Reply does not match either local or public socket for domain. Will not connect."; + } + } } case PacketTypeStunResponse: { @@ -348,7 +349,7 @@ void NodeList::sendDomainServerCheckIn() { } } - flagTimeForConnectionStep(NodeList::ConnectionStep::SendFirstDSCheckIn); + flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendDSCheckIn); if (!isUsingDTLS) { writeUnverifiedDatagram(domainServerPacket, _domainHandler.getSockAddr()); @@ -467,7 +468,7 @@ void NodeList::handleICEConnectionToDomainServer() { _domainHandler.getICEPeer().resetConnectionAttemps(); - flagTimeForConnectionStep(NodeList::ConnectionStep::SendFirstICEServerHearbeat); + flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendICEServerHearbeat); LimitedNodeList::sendHeartbeatToIceServer(_domainHandler.getICEServerSockAddr(), _domainHandler.getICEClientID(), @@ -476,7 +477,7 @@ void NodeList::handleICEConnectionToDomainServer() { qCDebug(networking) << "Sending ping packets to establish connectivity with domain-server with ID" << uuidStringWithoutCurlyBraces(_domainHandler.getICEDomainID()); - flagTimeForConnectionStep(NodeList::ConnectionStep::SendFirstPingsToDS); + flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendPingsToDS); // send the ping packet to the local and public sockets for this node QByteArray localPingPacket = constructPingPacket(PingType::Local, false, _domainHandler.getICEClientID()); @@ -493,7 +494,7 @@ int NodeList::processDomainServerList(const QByteArray& packet) { // this is a packet from the domain server, reset the count of un-replied check-ins _numNoReplyDomainCheckIns = 0; - DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::ReceiveFirstDSList); + DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSList); // if this was the first domain-server list from this domain, we've now connected if (!_domainHandler.isConnected()) { @@ -585,7 +586,7 @@ void NodeList::pingInactiveNodes() { pingPunchForInactiveNode(node); if (node->getType() == NodeType::AudioMixer) { - flagTimeForConnectionStep(NodeList::ConnectionStep::SendFirstAudioPing); + flagTimeForConnectionStep(NodeList::ConnectionStep::SendAudioPing); } } }); @@ -613,36 +614,3 @@ void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, con flagTimeForConnectionStep(NodeList::ConnectionStep::SetAudioMixerSocket); } } - -void NodeList::flagTimeForConnectionStep(NodeList::ConnectionStep connectionStep) { - QMetaObject::invokeMethod(this, "flagTimeForConnectionStep", - Q_ARG(NodeList::ConnectionStep, connectionStep), - Q_ARG(quint64, usecTimestampNow())); -} - -void NodeList::flagTimeForConnectionStep(NodeList::ConnectionStep connectionStep, quint64 timestamp) { - QWriteLocker writeLock(&_connectionTimeLock); - - if (connectionStep == NodeList::ConnectionStep::LookupAddress) { - // we clear the current times if the user just fired off a lookup - _lastConnectionTimes.clear(); - } - - if (!_lastConnectionTimes.contains(connectionStep)) { - // if there is no time for existing step add a timestamp on the first call for each NodeList::ConnectionStep - _lastConnectionTimes[connectionStep] = timestamp; - } else { - // if the existing time for this step is before the nearest sibling before then replace it - // this handles the case where audio comes in after an address lookup after the previous times have been cleared - quint64 currentTime = _lastConnectionTimes[connectionStep]; - - // go down the sibling steps and check if the registered time is actually before the sibling - for (int i = ((int) connectionStep) - 1; i >= 0; i--) { - NodeList::ConnectionStep thisStep = static_cast(i); - if (_lastConnectionTimes.contains(thisStep) && _lastConnectionTimes[thisStep] >= currentTime) { - _lastConnectionTimes[connectionStep] = timestamp; - break; - } - } - } -} diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index d52dd6e101..67810180f4 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -45,28 +45,6 @@ class NodeList : public LimitedNodeList { SINGLETON_DEPENDENCY public: - - enum ConnectionStep { - LookupAddress, - HandleAddress, - SetICEServerHostname, - SetICEServerSocket, - SendFirstICEServerHearbeat, - ReceiveDSPeerInformation, - SendFirstPingsToDS, - SetDomainHostname, - SetDomainSocket, - ForcedSTUNRequest, - SendFirstDSCheckIn, - ReceiveFirstDSList, - SendFirstAudioPing, - SetAudioMixerSocket, - SendFirstAudioPacket, - ReceiveFirstAudioPacket - }; - - Q_ENUMS(ConnectionStep); - NodeType_t getOwnerType() const { return _ownerType; } void setOwnerType(NodeType_t ownerType) { _ownerType = ownerType; } @@ -85,11 +63,6 @@ public: int processDomainServerList(const QByteArray& packet); - const QMap getLastConnectionTimes() const - { QReadLocker readLock(&_connectionTimeLock); return _lastConnectionTimes; } - void flagTimeForConnectionStep(NodeList::ConnectionStep connectionStep); - void resetConnectionTimes() { QWriteLocker writeLock(&_connectionTimeLock); _lastConnectionTimes.clear(); } - void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; } void sendAssignment(Assignment& assignment); @@ -104,7 +77,6 @@ signals: private slots: void sendPendingDSPathQuery(); void handleICEConnectionToDomainServer(); - void flagTimeForConnectionStep(NodeList::ConnectionStep connectionStep, quint64 timestamp); private: NodeList() : LimitedNodeList(0, 0) { assert(false); } // Not implemented, needed for DependencyManager templates compile NodeList(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0); @@ -126,9 +98,6 @@ private: int _numNoReplyDomainCheckIns; HifiSockAddr _assignmentServerSocket; - mutable QReadWriteLock _connectionTimeLock { }; - QMap _lastConnectionTimes; - friend class Application; }; From e6e2c4b95d47b318c89761d4b0081242dc9c07ad Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 May 2015 15:54:39 -0700 Subject: [PATCH 14/33] look for connection steps in LimitedNodeList --- libraries/audio-client/src/AudioClient.cpp | 2 +- libraries/networking/src/AddressManager.cpp | 6 +++--- libraries/networking/src/DomainHandler.cpp | 16 ++++++++-------- libraries/networking/src/NodeList.cpp | 4 ++-- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 5c123b3bd0..41c8cb9537 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -875,7 +875,7 @@ void AudioClient::handleAudioInput() { _stats.sentPacket(); - nodeList->flagTimeForConnectionStep(NodeList::ConnectionStep::SendAudioPacket); + nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPacket); int packetBytes = currentPacketPtr - audioDataPacket; nodeList->writeDatagram(audioDataPacket, packetBytes, audioMixer); diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index 63bcf4c4bc..3c6b7bd3f5 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -106,7 +106,7 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) { qCDebug(networking) << "Trying to go to URL" << lookupUrl.toString(); - DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::LookupAddress); + DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::LookupAddress); // there are 4 possible lookup strings @@ -204,7 +204,7 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const const QString DOMAIN_NETWORK_PORT_KEY = "network_port"; const QString DOMAIN_ICE_SERVER_ADDRESS_KEY = "ice_server_address"; - DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::HandleAddress); + DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress); if (domainObject.contains(DOMAIN_NETWORK_ADDRESS_KEY)) { QString domainHostname = domainObject[DOMAIN_NETWORK_ADDRESS_KEY].toString(); @@ -436,7 +436,7 @@ void AddressManager::setDomainInfo(const QString& hostname, quint16 port) { qCDebug(networking) << "Possible domain change required to connect to domain at" << hostname << "on" << port; - DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::HandleAddress); + DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::HandleAddress); emit possibleDomainChangeRequired(hostname, port); } diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 9dab4152d9..593f0b1597 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -85,7 +85,7 @@ void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hos } if (!_sockAddr.isNull()) { - DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::SetDomainSocket); + DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainSocket); } // some callers may pass a hostname, this is not to be used for lookup but for DTLS certificate verification @@ -115,7 +115,7 @@ void DomainHandler::setHostnameAndPort(const QString& hostname, quint16 port) { qCDebug(networking, "Looking up DS hostname %s.", _hostname.toLocal8Bit().constData()); QHostInfo::lookupHost(_hostname, this, SLOT(completedHostnameLookup(const QHostInfo&))); - DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::SetDomainHostname); + DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainHostname); UserActivityLogger::getInstance().changedDomain(_hostname); emit hostnameChanged(_hostname); @@ -143,7 +143,7 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, auto nodeList = DependencyManager::get(); - nodeList->flagTimeForConnectionStep(NodeList::ConnectionStep::SetICEServerHostname); + nodeList->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetICEServerHostname); if (_iceServerSockAddr.getAddress().isNull()) { // connect to lookup completed for ice-server socket so we can request a heartbeat once hostname is looked up @@ -161,14 +161,14 @@ void DomainHandler::setIceServerHostnameAndID(const QString& iceServerHostname, } void DomainHandler::activateICELocalSocket() { - DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::SetDomainSocket); + DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainSocket); _sockAddr = _icePeer.getLocalSocket(); _hostname = _sockAddr.getAddress().toString(); emit completedSocketDiscovery(); } void DomainHandler::activateICEPublicSocket() { - DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::SetDomainSocket); + DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainSocket); _sockAddr = _icePeer.getPublicSocket(); _hostname = _sockAddr.getAddress().toString(); emit completedSocketDiscovery(); @@ -179,7 +179,7 @@ void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) { if (hostInfo.addresses()[i].protocol() == QAbstractSocket::IPv4Protocol) { _sockAddr.setAddress(hostInfo.addresses()[i]); - DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::SetDomainSocket); + DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetDomainSocket); qCDebug(networking, "DS at %s is at %s", _hostname.toLocal8Bit().constData(), _sockAddr.getAddress().toString().toLocal8Bit().constData()); @@ -197,7 +197,7 @@ void DomainHandler::completedHostnameLookup(const QHostInfo& hostInfo) { void DomainHandler::completedIceServerHostnameLookup() { qDebug() << "ICE server socket is at" << _iceServerSockAddr; - DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::SetICEServerSocket); + DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetICEServerSocket); // emit our signal so we can send a heartbeat to ice-server immediately emit iceSocketAndIDReceived(); @@ -301,7 +301,7 @@ void DomainHandler::processICEResponsePacket(const QByteArray& icePacket) { NetworkPeer packetPeer; iceResponseStream >> packetPeer; - DependencyManager::get()->flagTimeForConnectionStep(NodeList::ConnectionStep::ReceiveDSPeerInformation); + DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSPeerInformation); if (packetPeer.getUUID() != _iceDomainID) { qCDebug(networking) << "Received a network peer with ID that does not match current domain. Will not attempt connection."; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index b7870bcdea..d6e394cc7b 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -586,7 +586,7 @@ void NodeList::pingInactiveNodes() { pingPunchForInactiveNode(node); if (node->getType() == NodeType::AudioMixer) { - flagTimeForConnectionStep(NodeList::ConnectionStep::SendAudioPing); + flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPing); } } }); @@ -611,6 +611,6 @@ void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, con } if (sendingNode->getType() == NodeType::AudioMixer) { - flagTimeForConnectionStep(NodeList::ConnectionStep::SetAudioMixerSocket); + flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SetAudioMixerSocket); } } From 757d02a600e0828b50f6c0f26d2e7d31b0bdb69b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 May 2015 16:00:56 -0700 Subject: [PATCH 15/33] fix reset on address lookup --- libraries/networking/src/LimitedNodeList.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 76bdc5d089..8133f1417c 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -866,14 +866,18 @@ void LimitedNodeList::flagTimeForConnectionStep(ConnectionStep connectionStep) { } void LimitedNodeList::flagTimeForConnectionStep(ConnectionStep connectionStep, quint64 timestamp) { - if (!_areConnectionTimesComplete) { + + if (connectionStep == ConnectionStep::LookupAddress) { + QWriteLocker writeLock(&_connectionTimeLock); + + // we clear the current times if the user just fired off a lookup + _lastConnectionTimes.clear(); + _areConnectionTimesComplete = false; + + _lastConnectionTimes[timestamp] = connectionStep; + } else if (!_areConnectionTimesComplete) { QWriteLocker writeLock(&_connectionTimeLock); - if (connectionStep == ConnectionStep::LookupAddress) { - // we clear the current times if the user just fired off a lookup - _lastConnectionTimes.clear(); - _areConnectionTimesComplete = false; - } // anything > than sending the first DS check should not come before the DS check in, so we drop those // this handles the case where you lookup an address and get packets in the existing domain before changing domains From 0cb67cce2cd998ff13885cdc6691a6dfa10e171f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 May 2015 18:22:23 -0700 Subject: [PATCH 16/33] add ping timer tied to NetworkPeer --- libraries/networking/src/LimitedNodeList.cpp | 7 ++- libraries/networking/src/LimitedNodeList.h | 3 +- libraries/networking/src/NetworkPeer.cpp | 32 +++++++++--- libraries/networking/src/NetworkPeer.h | 41 ++++++++++------ libraries/networking/src/Node.cpp | 37 ++++++++------ libraries/networking/src/Node.h | 23 +++++---- libraries/networking/src/NodeList.cpp | 49 ++++++++++++------- libraries/networking/src/NodeList.h | 4 +- libraries/networking/src/ThreadedAssignment.h | 1 - 9 files changed, 126 insertions(+), 71 deletions(-) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 8133f1417c..8c18c42f50 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -472,7 +472,8 @@ void LimitedNodeList::handleNodeKill(const SharedNodePointer& node) { SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, - bool canAdjustLocks, bool canRez) { + bool canAdjustLocks, bool canRez, + const QUuid& connectionSecret) { NodeHash::const_iterator it = _nodeHash.find(uuid); if (it != _nodeHash.end()) { @@ -482,11 +483,13 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t matchingNode->setLocalSocket(localSocket); matchingNode->setCanAdjustLocks(canAdjustLocks); matchingNode->setCanRez(canRez); + matchingNode->setConnectionSecret(connectionSecret); return matchingNode; } else { // we didn't have this node, so add them - Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, canAdjustLocks, canRez); + Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, canAdjustLocks, canRez, connectionSecret); + SharedNodePointer newNodePointer(newNode); _nodeHash.insert(UUIDNodePair(newNode->getUUID(), newNodePointer)); diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 990508e89d..8d99c28d5c 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -151,7 +151,8 @@ public: SharedNodePointer addOrUpdateNode(const QUuid& uuid, NodeType_t nodeType, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, - bool canAdjustLocks, bool canRez); + bool canAdjustLocks, bool canRez, + const QUuid& connectionSecret = QUuid()); bool hasCompletedInitialSTUN() const { return _hasCompletedInitialSTUN; } diff --git a/libraries/networking/src/NetworkPeer.cpp b/libraries/networking/src/NetworkPeer.cpp index de1b8f66ba..69099c5201 100644 --- a/libraries/networking/src/NetworkPeer.cpp +++ b/libraries/networking/src/NetworkPeer.cpp @@ -25,7 +25,7 @@ NetworkPeer::NetworkPeer() : _lastHeardMicrostamp(usecTimestampNow()), _connectionAttempts(0) { - + } NetworkPeer::NetworkPeer(const QUuid& uuid, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) : @@ -36,14 +36,14 @@ NetworkPeer::NetworkPeer(const QUuid& uuid, const HifiSockAddr& publicSocket, co _lastHeardMicrostamp(usecTimestampNow()), _connectionAttempts(0) { - + } NetworkPeer::NetworkPeer(const NetworkPeer& otherPeer) : QObject() { _uuid = otherPeer._uuid; _publicSocket = otherPeer._publicSocket; _localSocket = otherPeer._localSocket; - + _wakeTimestamp = otherPeer._wakeTimestamp; _lastHeardMicrostamp = otherPeer._lastHeardMicrostamp; _connectionAttempts = otherPeer._connectionAttempts; @@ -57,7 +57,7 @@ NetworkPeer& NetworkPeer::operator=(const NetworkPeer& otherPeer) { void NetworkPeer::swap(NetworkPeer& otherPeer) { using std::swap; - + swap(_uuid, otherPeer._uuid); swap(_publicSocket, otherPeer._publicSocket); swap(_localSocket, otherPeer._localSocket); @@ -71,15 +71,33 @@ QByteArray NetworkPeer::toByteArray() const { QDataStream peerStream(&peerByteArray, QIODevice::Append); peerStream << *this; - + return peerByteArray; } +void NetworkPeer::startPingTimer() { + if (!_pingTimer) { + _pingTimer = new QTimer(this); + + connect(_pingTimer, &QTimer::timeout, this, &NetworkPeer::pingTimerTimeout); + + _pingTimer->start(UDP_PUNCH_PING_INTERVAL_MS); + } +} + +void NetworkPeer::stopPingTimer() { + if (_pingTimer) { + _pingTimer->stop(); + _pingTimer->deleteLater(); + _pingTimer = NULL; + } +} + QDataStream& operator<<(QDataStream& out, const NetworkPeer& peer) { out << peer._uuid; out << peer._publicSocket; out << peer._localSocket; - + return out; } @@ -87,7 +105,7 @@ QDataStream& operator>>(QDataStream& in, NetworkPeer& peer) { in >> peer._uuid; in >> peer._publicSocket; in >> peer._localSocket; - + return in; } diff --git a/libraries/networking/src/NetworkPeer.h b/libraries/networking/src/NetworkPeer.h index e44fac2dcc..2a060408c1 100644 --- a/libraries/networking/src/NetworkPeer.h +++ b/libraries/networking/src/NetworkPeer.h @@ -12,8 +12,9 @@ #ifndef hifi_NetworkPeer_h #define hifi_NetworkPeer_h -#include -#include +#include +#include +#include #include "HifiSockAddr.h" @@ -22,35 +23,38 @@ const int ICE_SERVER_DEFAULT_PORT = 7337; const int ICE_HEARBEAT_INTERVAL_MSECS = 2 * 1000; const int MAX_ICE_CONNECTION_ATTEMPTS = 5; +const int UDP_PUNCH_PING_INTERVAL_MS = 25; + class NetworkPeer : public QObject { + Q_OBJECT public: NetworkPeer(); NetworkPeer(const QUuid& uuid, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket); - + // privatize copy and assignment operator to disallow peer copying NetworkPeer(const NetworkPeer &otherPeer); NetworkPeer& operator=(const NetworkPeer& otherPeer); - + bool isNull() const { return _uuid.isNull(); } - + const QUuid& getUUID() const { return _uuid; } void setUUID(const QUuid& uuid) { _uuid = uuid; } - + void reset(); - + const HifiSockAddr& getPublicSocket() const { return _publicSocket; } virtual void setPublicSocket(const HifiSockAddr& publicSocket) { _publicSocket = publicSocket; } const HifiSockAddr& getLocalSocket() const { return _localSocket; } virtual void setLocalSocket(const HifiSockAddr& localSocket) { _localSocket = localSocket; } - + quint64 getWakeTimestamp() const { return _wakeTimestamp; } void setWakeTimestamp(quint64 wakeTimestamp) { _wakeTimestamp = wakeTimestamp; } - + quint64 getLastHeardMicrostamp() const { return _lastHeardMicrostamp; } void setLastHeardMicrostamp(quint64 lastHeardMicrostamp) { _lastHeardMicrostamp = lastHeardMicrostamp; } - + QByteArray toByteArray() const; - + int getConnectionAttempts() const { return _connectionAttempts; } void incrementConnectionAttempts() { ++_connectionAttempts; } void resetConnectionAttemps() { _connectionAttempts = 0; } @@ -60,18 +64,25 @@ public: float getOutboundBandwidth(); // in kbps float getInboundBandwidth(); // in kbps - + + void startPingTimer(); + void stopPingTimer(); + friend QDataStream& operator<<(QDataStream& out, const NetworkPeer& peer); friend QDataStream& operator>>(QDataStream& in, NetworkPeer& peer); +signals: + void pingTimerTimeout(); protected: QUuid _uuid; - + HifiSockAddr _publicSocket; HifiSockAddr _localSocket; - + quint64 _wakeTimestamp; quint64 _lastHeardMicrostamp; - + + QTimer* _pingTimer = NULL; + int _connectionAttempts; private: void swap(NetworkPeer& otherPeer); diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index ddc0571db4..f651553ff6 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -42,12 +42,12 @@ const QString& NodeType::getNodeTypeName(NodeType_t nodeType) { } Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, - const HifiSockAddr& localSocket, bool canAdjustLocks, bool canRez) : + const HifiSockAddr& localSocket, bool canAdjustLocks, bool canRez, const QUuid& connectionSecret) : NetworkPeer(uuid, publicSocket, localSocket), _type(type), _activeSocket(NULL), _symmetricSocket(), - _connectionSecret(), + _connectionSecret(connectionSecret), _linkedData(NULL), _isAlive(true), _pingMs(-1), // "Uninitialized" @@ -57,7 +57,7 @@ Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, _canAdjustLocks(canAdjustLocks), _canRez(canRez) { - + } Node::~Node() { @@ -75,11 +75,11 @@ void Node::setPublicSocket(const HifiSockAddr& publicSocket) { // if the active socket was the public socket then reset it to NULL _activeSocket = NULL; } - + if (!_publicSocket.isNull()) { qCDebug(networking) << "Public socket change for node" << *this; } - + _publicSocket = publicSocket; } } @@ -90,11 +90,11 @@ void Node::setLocalSocket(const HifiSockAddr& localSocket) { // if the active socket was the local socket then reset it to NULL _activeSocket = NULL; } - + if (!_localSocket.isNull()) { qCDebug(networking) << "Local socket change for node" << *this; } - + _localSocket = localSocket; } } @@ -105,32 +105,39 @@ void Node::setSymmetricSocket(const HifiSockAddr& symmetricSocket) { // if the active socket was the symmetric socket then reset it to NULL _activeSocket = NULL; } - + if (!_symmetricSocket.isNull()) { qCDebug(networking) << "Symmetric socket change for node" << *this; } - + _symmetricSocket = symmetricSocket; } } +void Node::setActiveSocket(HifiSockAddr* discoveredSocket) { + _activeSocket = discoveredSocket; + + // we have an active socket, stop our ping timer + stopPingTimer(); +} + void Node::activateLocalSocket() { qCDebug(networking) << "Activating local socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid); - _activeSocket = &_localSocket; + setActiveSocket(&_localSocket); } void Node::activatePublicSocket() { qCDebug(networking) << "Activating public socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid); - _activeSocket = &_publicSocket; + setActiveSocket(&_publicSocket); } void Node::activateSymmetricSocket() { qCDebug(networking) << "Activating symmetric socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid); - _activeSocket = &_symmetricSocket; + setActiveSocket(&_symmetricSocket); } PacketSequenceNumber Node::getLastSequenceNumberForPacketType(PacketType packetType) const { - auto typeMatch = _lastSequenceNumbers.find(packetType); + auto typeMatch = _lastSequenceNumbers.find(packetType); if (typeMatch != _lastSequenceNumbers.end()) { return typeMatch->second; } else { @@ -145,7 +152,7 @@ QDataStream& operator<<(QDataStream& out, const Node& node) { out << node._localSocket; out << node._canAdjustLocks; out << node._canRez; - + return out; } @@ -156,7 +163,7 @@ QDataStream& operator>>(QDataStream& in, Node& node) { in >> node._localSocket; in >> node._canAdjustLocks; in >> node._canRez; - + return in; } diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index 0836a448b2..884e65a626 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -37,16 +37,17 @@ namespace NodeType { const NodeType_t AudioMixer = 'M'; const NodeType_t AvatarMixer = 'W'; const NodeType_t Unassigned = 1; - + void init(); const QString& getNodeTypeName(NodeType_t nodeType); } class Node : public NetworkPeer { Q_OBJECT -public: +public: Node(const QUuid& uuid, NodeType_t type, - const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, bool canAdjustLocks, bool canRez); + const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, + bool canAdjustLocks, bool canRez, const QUuid& connectionSecret = QUuid()); ~Node(); bool operator==(const Node& otherNode) const { return _uuid == otherNode._uuid; } @@ -54,7 +55,7 @@ public: char getType() const { return _type; } void setType(char type) { _type = type; } - + const QUuid& getConnectionSecret() const { return _connectionSecret; } void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; } @@ -70,12 +71,12 @@ public: int getClockSkewUsec() const { return _clockSkewUsec; } void updateClockSkewUsec(int clockSkewSample); QMutex& getMutex() { return _mutex; } - + virtual void setPublicSocket(const HifiSockAddr& publicSocket); virtual void setLocalSocket(const HifiSockAddr& localSocket); const HifiSockAddr& getSymmetricSocket() const { return _symmetricSocket; } virtual void setSymmetricSocket(const HifiSockAddr& symmetricSocket); - + const HifiSockAddr* getActiveSocket() const { return _activeSocket; } void setCanAdjustLocks(bool canAdjustLocks) { _canAdjustLocks = canAdjustLocks; } @@ -83,7 +84,7 @@ public: void setCanRez(bool canRez) { _canRez = canRez; } bool getCanRez() { return _canRez; } - + void activatePublicSocket(); void activateLocalSocket(); void activateSymmetricSocket(); @@ -91,7 +92,7 @@ public: void setLastSequenceNumberForPacketType(PacketSequenceNumber sequenceNumber, PacketType packetType) { _lastSequenceNumbers[packetType] = sequenceNumber; } PacketSequenceNumber getLastSequenceNumberForPacketType(PacketType packetType) const; - + friend QDataStream& operator<<(QDataStream& out, const Node& node); friend QDataStream& operator>>(QDataStream& in, Node& node); @@ -100,11 +101,13 @@ private: Node(const Node &otherNode); Node& operator=(Node otherNode); + void setActiveSocket(HifiSockAddr* discoveredSocket); + NodeType_t _type; - + HifiSockAddr* _activeSocket; HifiSockAddr _symmetricSocket; - + QUuid _connectionSecret; NodeData* _linkedData; bool _isAlive; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index d6e394cc7b..67876ccc73 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -82,6 +82,9 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned // clear our NodeList when logout is requested connect(&AccountManager::getInstance(), &AccountManager::logoutComplete , this, &NodeList::reset); + // anytime we get a new node we will want to attempt to punch to it + connect(this, &LimitedNodeList::nodeAdded, this, &NodeList::startNodeHolePunch); + // we definitely want STUN to update our public socket, so call the LNL to kick that off startSTUNPublicSocketUpdate(); } @@ -521,7 +524,7 @@ int NodeList::processDomainServerList(const QByteArray& packet) { setThisNodeCanRez(thisNodeCanRez); // pull each node in the packet - while(packetStream.device()->pos() < packet.size()) { + while (packetStream.device()->pos() < packet.size()) { // setup variables to read into from QDataStream qint8 nodeType; QUuid nodeUUID, connectionUUID; @@ -537,16 +540,12 @@ int NodeList::processDomainServerList(const QByteArray& packet) { nodePublicSocket.setAddress(_domainHandler.getIP()); } - SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket, - nodeLocalSocket, canAdjustLocks, canRez); - packetStream >> connectionUUID; - node->setConnectionSecret(connectionUUID); - } - // ping inactive nodes in conjunction with receipt of list from domain-server - // this makes it happen every second and also pings any newly added nodes - pingInactiveNodes(); + SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket, + nodeLocalSocket, canAdjustLocks, canRez, + connectionUUID); + } return readNodes; } @@ -566,6 +565,10 @@ void NodeList::sendAssignment(Assignment& assignment) { } void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) { + if (node->getType() == NodeType::AudioMixer) { + flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPing); + } + // send the ping packet to the local and public sockets for this node QByteArray localPingPacket = constructPingPacket(PingType::Local); writeDatagram(localPingPacket, node, node->getLocalSocket()); @@ -577,19 +580,27 @@ void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) { QByteArray symmetricPingPacket = constructPingPacket(PingType::Symmetric); writeDatagram(symmetricPingPacket, node, node->getSymmetricSocket()); } + + node->incrementConnectionAttempts(); } -void NodeList::pingInactiveNodes() { - eachNode([this](const SharedNodePointer& node){ - if (!node->getActiveSocket()) { - // we don't have an active link to this node, ping it to set that up - pingPunchForInactiveNode(node); +void NodeList::startNodeHolePunch(const SharedNodePointer& node) { + // connect to the correct signal on this node so we know when to ping it + connect(node.data(), &Node::pingTimerTimeout, this, &NodeList::handleNodePingTimeout); - if (node->getType() == NodeType::AudioMixer) { - flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPing); - } - } - }); + // start the ping timer for this node + node->startPingTimer(); + + // ping this node immediately + pingPunchForInactiveNode(node); +} + +void NodeList::handleNodePingTimeout() { + Node* senderNode = qobject_cast(sender()); + + if (senderNode) { + pingPunchForInactiveNode(nodeWithUUID(senderNode->getUUID())); + } } void NodeList::activateSocketFromNodeCommunication(const QByteArray& packet, const SharedNodePointer& sendingNode) { diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index 67810180f4..e699f99aad 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -70,13 +70,15 @@ public: public slots: void reset(); void sendDomainServerCheckIn(); - void pingInactiveNodes(); void handleDSPathQuery(const QString& newPath); signals: void limitOfSilentDomainCheckInsReached(); private slots: void sendPendingDSPathQuery(); void handleICEConnectionToDomainServer(); + + void startNodeHolePunch(const SharedNodePointer& node); + void handleNodePingTimeout(); private: NodeList() : LimitedNodeList(0, 0) { assert(false); } // Not implemented, needed for DependencyManager templates compile NodeList(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0); diff --git a/libraries/networking/src/ThreadedAssignment.h b/libraries/networking/src/ThreadedAssignment.h index 676d1a04ce..e2e0aa0fed 100644 --- a/libraries/networking/src/ThreadedAssignment.h +++ b/libraries/networking/src/ThreadedAssignment.h @@ -46,7 +46,6 @@ protected: private slots: void checkInWithDomainServerOrExit(); - }; typedef QSharedPointer SharedAssignmentPointer; From 667df66963d2609dc0ad1b7b49a99db0bc5131e0 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 27 May 2015 18:25:38 -0700 Subject: [PATCH 17/33] add packet type for DS node add --- libraries/networking/src/PacketHeaders.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 8b952e6eb3..56045be1c3 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -49,7 +49,7 @@ enum PacketType { PacketTypeDataServerConfirm, // 20 PacketTypeDomainServerPathQuery, PacketTypeDomainServerPathResponse, - UNUSED_3, + PacketTypeDomainServerAddedNode, UNUSED_4, UNUSED_5, // 25 PacketTypeOctreeStats, From 5c75863af43e1f511bc668dd8fb6afc3144419ea Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 28 May 2015 10:45:03 -0700 Subject: [PATCH 18/33] immediately send add packet from DS for new node --- domain-server/src/DomainServer.cpp | 87 +++++++++++++++++++----- domain-server/src/DomainServer.h | 3 + libraries/networking/src/PacketHeaders.h | 17 ++--- 3 files changed, 81 insertions(+), 26 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 2c784abe54..8eaf1a29f3 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -654,6 +654,19 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock SharedNodePointer newNode = limitedNodeList->addOrUpdateNode(nodeUUID, nodeType, publicSockAddr, localSockAddr, canAdjustLocks, canRez); + + // So that we can send messages to this node at will - we need to activate the correct socket on this node now + if (senderSockAddr == publicSockAddr) { + newNode->activatePublicSocket(); + } else if (senderSockAddr == localSockAddr) { + newNode->activateLocalSocket(); + } else { + // set the Node's symmetric socket to the sender socket + newNode->setSymmetricSocket(senderSockAddr); + // activate that symmetric socket + newNode->activateSymmetricSocket(); + } + // when the newNode is created the linked data is also created // if this was a static assignment set the UUID, set the sendingSockAddr DomainServerNodeData* nodeData = reinterpret_cast(newNode->getLinkedData()); @@ -683,6 +696,9 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock // reply back to the user with a PacketTypeDomainList sendDomainListToNode(newNode, senderSockAddr, nodeInterestList.toSet()); + + // send out this node to our other connected nodes + broadcastNewNode(newNode); } } @@ -699,9 +715,9 @@ unsigned int DomainServer::countConnectedUsers() { } -bool DomainServer::verifyUsersKey (const QString& username, - const QByteArray& usernameSignature, - QString& reasonReturn) { +bool DomainServer::verifyUsersKey(const QString& username, + const QByteArray& usernameSignature, + QString& reasonReturn) { // it's possible this user can be allowed to connect, but we need to check their username signature QByteArray publicKeyArray = _userPublicKeys.value(username); @@ -954,21 +970,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif nodeDataStream << *otherNode.data(); // pack the secret that these two nodes will use to communicate with each other - QUuid secretUUID = nodeData->getSessionSecretHash().value(otherNode->getUUID()); - if (secretUUID.isNull()) { - // generate a new secret UUID these two nodes can use - secretUUID = QUuid::createUuid(); - - // set that on the current Node's sessionSecretHash - nodeData->getSessionSecretHash().insert(otherNode->getUUID(), secretUUID); - - // set it on the other Node's sessionSecretHash - reinterpret_cast(otherNode->getLinkedData()) - ->getSessionSecretHash().insert(node->getUUID(), secretUUID); - - } - - nodeDataStream << secretUUID; + nodeDataStream << connectionSecretForNodes(node, otherNode); if (broadcastPacket.size() + nodeByteArray.size() > dataMTU) { // we need to break here and start a new packet @@ -992,6 +994,55 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif limitedNodeList->writeUnverifiedDatagram(broadcastPacket, node, senderSockAddr); } +QUuid DomainServer::connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB) { + DomainServerNodeData* nodeAData = dynamic_cast(nodeA->getLinkedData()); + DomainServerNodeData* nodeBData = dynamic_cast(nodeB->getLinkedData()); + + if (nodeAData && nodeBData) { + QUuid& secretUUID = nodeAData->getSessionSecretHash()[nodeB->getUUID()]; + + if (secretUUID.isNull()) { + // generate a new secret UUID these two nodes can use + secretUUID = QUuid::createUuid(); + + // set it on the other Node's sessionSecretHash + reinterpret_cast(nodeBData)->getSessionSecretHash().insert(nodeA->getUUID(), secretUUID); + } + + return secretUUID; + } + + return QUuid(); +} + +void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) { + + auto limitedNodeList = DependencyManager::get(); + + // setup the add packet for this new node + QByteArray addNodePacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainServerAddedNode); + QDataStream addNodeStream(&addNodePacket, QIODevice::Append); + + addNodeStream << *addedNode.data(); + + int connectionSecretIndex = addNodePacket.size(); + + limitedNodeList->eachMatchingNode( + [&](const SharedNodePointer& node)->bool { + return (node->getLinkedData() && node->getActiveSocket() && node != addedNode); + }, + [&](const SharedNodePointer& node) { + QByteArray rfcConnectionSecret = connectionSecretForNodes(node, addedNode).toRfc4122(); + + // replace the bytes at the end of the packet for the connection secret between these nodes + addNodePacket.replace(connectionSecretIndex, NUM_BYTES_RFC4122_UUID, rfcConnectionSecret); + + // send off this packet to the node + limitedNodeList->writeUnverifiedDatagram(addNodePacket, node); + } + ); +} + void DomainServer::readAvailableDatagrams() { auto limitedNodeList = DependencyManager::get(); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 22e3efa378..c70c9cec13 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -101,6 +101,9 @@ private: void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr, const NodeSet& nodeInterestList); + QUuid connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB); + void broadcastNewNode(const SharedNodePointer& node); + void parseAssignmentConfigs(QSet& excludedTypes); void addStaticAssignmentToAssignmentHash(Assignment* newAssignment); void createStaticAssignmentsForType(Assignment::Type type, const QVariantList& configList); diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index 56045be1c3..a702df7798 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -91,14 +91,15 @@ const PacketSequenceNumber DEFAULT_SEQUENCE_NUMBER = 0; typedef std::map PacketTypeSequenceMap; const QSet NON_VERIFIED_PACKETS = QSet() -<< PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest -<< PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainConnectionDenied -<< PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse -<< PacketTypeNodeJsonStats << PacketTypeEntityQuery -<< PacketTypeOctreeDataNack << PacketTypeEntityEditNack -<< PacketTypeIceServerHeartbeat << PacketTypeIceServerHeartbeatResponse -<< PacketTypeUnverifiedPing << PacketTypeUnverifiedPingReply << PacketTypeStopNode -<< PacketTypeDomainServerPathQuery << PacketTypeDomainServerPathResponse; + << PacketTypeDomainServerRequireDTLS << PacketTypeDomainConnectRequest + << PacketTypeDomainList << PacketTypeDomainListRequest << PacketTypeDomainConnectionDenied + << PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse + << PacketTypeNodeJsonStats << PacketTypeEntityQuery + << PacketTypeOctreeDataNack << PacketTypeEntityEditNack + << PacketTypeIceServerHeartbeat << PacketTypeIceServerHeartbeatResponse + << PacketTypeUnverifiedPing << PacketTypeUnverifiedPingReply << PacketTypeStopNode + << PacketTypeDomainServerPathQuery << PacketTypeDomainServerPathResponse + << PacketTypeDomainServerAddedNode; const QSet SEQUENCE_NUMBERED_PACKETS = QSet() << PacketTypeAvatarData; From cee058c4ce42ace1c32a9cdf0806dc5e25d86347 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 28 May 2015 11:02:45 -0700 Subject: [PATCH 19/33] handle immediate add packets in NodeList --- libraries/networking/src/LimitedNodeList.cpp | 4 ++ libraries/networking/src/LimitedNodeList.h | 1 + libraries/networking/src/NodeList.cpp | 67 +++++++++++++------- libraries/networking/src/NodeList.h | 7 +- 4 files changed, 53 insertions(+), 26 deletions(-) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 8c18c42f50..7102df62e0 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -490,6 +490,10 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t // we didn't have this node, so add them Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, canAdjustLocks, canRez, connectionSecret); + if (nodeType == NodeType::AudioMixer) { + LimitedNodeList::flagTimeForConnectionStep(LimitedNodeList::AddedAudioMixer); + } + SharedNodePointer newNodePointer(newNode); _nodeHash.insert(UUIDNodePair(newNode->getUUID(), newNodePointer)); diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index 8d99c28d5c..fdfb1fa834 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -96,6 +96,7 @@ public: SetDomainSocket, SendDSCheckIn, ReceiveDSList, + AddedAudioMixer, SendAudioPing, SetAudioMixerSocket, SendAudioPacket, diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 67876ccc73..7384457458 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -170,12 +170,18 @@ void NodeList::timePingReply(const QByteArray& packet, const SharedNodePointer& } void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet) { - switch (packetTypeForPacket(packet)) { - case PacketTypeDomainList: { + PacketType packetType = packetTypeForPacket(packet); + switch (packetType) { + case PacketTypeDomainList: + case PacketTypeDomainServerAddedNode: { if (!_domainHandler.getSockAddr().isNull()) { - // only process a list from domain-server if we're talking to a domain + // only process a packet from domain-server if we're talking to a domain // TODO: how do we make sure this is actually the domain we want the list from (DTLS probably) - processDomainServerList(packet); + if (packetType == PacketTypeDomainList) { + processDomainServerList(packet); + } else if (packetType == PacketTypeDomainServerAddedNode) { + processDomainServerAddedNode(packet); + } } break; } @@ -525,31 +531,44 @@ int NodeList::processDomainServerList(const QByteArray& packet) { // pull each node in the packet while (packetStream.device()->pos() < packet.size()) { - // setup variables to read into from QDataStream - qint8 nodeType; - QUuid nodeUUID, connectionUUID; - HifiSockAddr nodePublicSocket, nodeLocalSocket; - bool canAdjustLocks; - bool canRez; - - packetStream >> nodeType >> nodeUUID >> nodePublicSocket >> nodeLocalSocket >> canAdjustLocks >> canRez; - - // if the public socket address is 0 then it's reachable at the same IP - // as the domain server - if (nodePublicSocket.getAddress().isNull()) { - nodePublicSocket.setAddress(_domainHandler.getIP()); - } - - packetStream >> connectionUUID; - - SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket, - nodeLocalSocket, canAdjustLocks, canRez, - connectionUUID); + parseNodeFromPacketStream(packetStream); } return readNodes; } +void NodeList::processDomainServerAddedNode(const QByteArray& packet) { + // setup a QDataStream, skip the header + QDataStream packetStream(packet); + packetStream.skipRawData(numBytesForPacketHeader(packet)); + + // use our shared method to pull out the new node + parseNodeFromPacketStream(packetStream); +} + +void NodeList::parseNodeFromPacketStream(QDataStream& packetStream) { + // setup variables to read into from QDataStream + qint8 nodeType; + QUuid nodeUUID, connectionUUID; + HifiSockAddr nodePublicSocket, nodeLocalSocket; + bool canAdjustLocks; + bool canRez; + + packetStream >> nodeType >> nodeUUID >> nodePublicSocket >> nodeLocalSocket >> canAdjustLocks >> canRez; + + // if the public socket address is 0 then it's reachable at the same IP + // as the domain server + if (nodePublicSocket.getAddress().isNull()) { + nodePublicSocket.setAddress(_domainHandler.getIP()); + } + + packetStream >> connectionUUID; + + SharedNodePointer node = addOrUpdateNode(nodeUUID, nodeType, nodePublicSocket, + nodeLocalSocket, canAdjustLocks, canRez, + connectionUUID); +} + void NodeList::sendAssignment(Assignment& assignment) { PacketType assignmentPacketType = assignment.getCommand() == Assignment::CreateCommand diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index e699f99aad..d62715af8b 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -61,8 +61,6 @@ public: void processNodeData(const HifiSockAddr& senderSockAddr, const QByteArray& packet); - int processDomainServerList(const QByteArray& packet); - void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; } void sendAssignment(Assignment& assignment); @@ -94,6 +92,11 @@ private: void sendDSPathQuery(const QString& newPath); + int processDomainServerList(const QByteArray& packet); + void processDomainServerAddedNode(const QByteArray& packet); + void parseNodeFromPacketStream(QDataStream& packetStream); + + NodeType_t _ownerType; NodeSet _nodeTypesOfInterest; DomainHandler _domainHandler; From 81c23e9ca0188a7817e47012f17dd812a5bb04e2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 28 May 2015 11:22:27 -0700 Subject: [PATCH 20/33] output if pinged node cannot be reached --- libraries/networking/src/NetworkPeer.h | 2 +- libraries/networking/src/Node.cpp | 3 +++ libraries/networking/src/NodeList.cpp | 13 ++++++++++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/libraries/networking/src/NetworkPeer.h b/libraries/networking/src/NetworkPeer.h index 2a060408c1..92f271efa4 100644 --- a/libraries/networking/src/NetworkPeer.h +++ b/libraries/networking/src/NetworkPeer.h @@ -57,7 +57,7 @@ public: int getConnectionAttempts() const { return _connectionAttempts; } void incrementConnectionAttempts() { ++_connectionAttempts; } - void resetConnectionAttemps() { _connectionAttempts = 0; } + void resetConnectionAttempts() { _connectionAttempts = 0; } void recordBytesSent(int count); void recordBytesReceived(int count); diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index f651553ff6..e4144d080c 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -119,6 +119,9 @@ void Node::setActiveSocket(HifiSockAddr* discoveredSocket) { // we have an active socket, stop our ping timer stopPingTimer(); + + // we're now considered connected to this peer - reset the number of connection attemps + resetConnectionAttempts(); } void Node::activateLocalSocket() { diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 7384457458..0838ec18ec 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -475,7 +475,7 @@ void NodeList::handleICEConnectionToDomainServer() { if (_domainHandler.getICEPeer().isNull() || _domainHandler.getICEPeer().getConnectionAttempts() >= MAX_ICE_CONNECTION_ATTEMPTS) { - _domainHandler.getICEPeer().resetConnectionAttemps(); + _domainHandler.getICEPeer().resetConnectionAttempts(); flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendICEServerHearbeat); @@ -588,6 +588,13 @@ void NodeList::pingPunchForInactiveNode(const SharedNodePointer& node) { flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendAudioPing); } + // every second we're trying to ping this node and we're not getting anywhere - debug that out + const int NUM_DEBUG_CONNECTION_ATTEMPTS = 1000 / (UDP_PUNCH_PING_INTERVAL_MS); + + if (node->getConnectionAttempts() > 0 && node->getConnectionAttempts() % NUM_DEBUG_CONNECTION_ATTEMPTS == 0) { + qCDebug(networking) << "No response to UDP hole punch pings for node" << node->getUUID() << "in last second."; + } + // send the ping packet to the local and public sockets for this node QByteArray localPingPacket = constructPingPacket(PingType::Local); writeDatagram(localPingPacket, node, node->getLocalSocket()); @@ -615,10 +622,10 @@ void NodeList::startNodeHolePunch(const SharedNodePointer& node) { } void NodeList::handleNodePingTimeout() { - Node* senderNode = qobject_cast(sender()); + SharedNodePointer senderNode = nodeWithUUID(qobject_cast(sender())->getUUID()); if (senderNode) { - pingPunchForInactiveNode(nodeWithUUID(senderNode->getUUID())); + pingPunchForInactiveNode(senderNode); } } From eb6b80133b5299ae8ad174a9e535dea7294f4d90 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 28 May 2015 13:17:48 -0700 Subject: [PATCH 21/33] immediately ping the ICE DS every 25ms --- libraries/networking/src/DomainHandler.cpp | 11 +++++-- libraries/networking/src/DomainHandler.h | 2 +- libraries/networking/src/NetworkPeer.cpp | 15 +++++++++- libraries/networking/src/NetworkPeer.h | 11 +++---- libraries/networking/src/NodeList.cpp | 34 ++++++++++++++++++---- libraries/networking/src/NodeList.h | 4 ++- 6 files changed, 60 insertions(+), 17 deletions(-) diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 593f0b1597..e09dcb186b 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -31,12 +31,13 @@ DomainHandler::DomainHandler(QObject* parent) : _iceDomainID(), _iceClientID(), _iceServerSockAddr(), - _icePeer(), + _icePeer(this), _isConnected(false), _settingsObject(), _failedSettingsRequests(0) { - + // if we get a socket that make sure our NetworkPeer ping timer stops + connect(this, &DomainHandler::completedSocketDiscovery, &_icePeer, &NetworkPeer::stopPingTimer); } void DomainHandler::clearConnectionInfo() { @@ -309,6 +310,10 @@ void DomainHandler::processICEResponsePacket(const QByteArray& icePacket) { qCDebug(networking) << "Received network peer object for domain -" << packetPeer; _icePeer = packetPeer; - emit requestICEConnectionAttempt(); + // ask the peer object to start its ping timer + _icePeer.startPingTimer(); + + // emit our signal so the NodeList knows to send a ping immediately + emit icePeerSocketsReceived(); } } diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index 794a7793bb..0c1698f5ec 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -99,7 +99,7 @@ signals: void disconnectedFromDomain(); void iceSocketAndIDReceived(); - void requestICEConnectionAttempt(); + void icePeerSocketsReceived(); void settingsReceived(const QJsonObject& domainSettingsObject); void settingsReceiveFail(); diff --git a/libraries/networking/src/NetworkPeer.cpp b/libraries/networking/src/NetworkPeer.cpp index 69099c5201..a6ed7e44fb 100644 --- a/libraries/networking/src/NetworkPeer.cpp +++ b/libraries/networking/src/NetworkPeer.cpp @@ -17,7 +17,8 @@ #include "NetworkPeer.h" #include "BandwidthRecorder.h" -NetworkPeer::NetworkPeer() : +NetworkPeer::NetworkPeer(QObject* parent) : + QObject(parent), _uuid(), _publicSocket(), _localSocket(), @@ -66,6 +67,18 @@ void NetworkPeer::swap(NetworkPeer& otherPeer) { swap(_connectionAttempts, otherPeer._connectionAttempts); } +void NetworkPeer::softReset() { + // a soft reset should clear the sockets and reset the number of connection attempts + _localSocket.clear(); + _publicSocket.clear(); + + // stop our ping timer since we don't have sockets to ping anymore anyways + stopPingTimer(); + + _connectionAttempts = 0; +} + + QByteArray NetworkPeer::toByteArray() const { QByteArray peerByteArray; diff --git a/libraries/networking/src/NetworkPeer.h b/libraries/networking/src/NetworkPeer.h index 92f271efa4..53b79c0126 100644 --- a/libraries/networking/src/NetworkPeer.h +++ b/libraries/networking/src/NetworkPeer.h @@ -28,7 +28,7 @@ const int UDP_PUNCH_PING_INTERVAL_MS = 25; class NetworkPeer : public QObject { Q_OBJECT public: - NetworkPeer(); + NetworkPeer(QObject* parent = 0); NetworkPeer(const QUuid& uuid, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket); // privatize copy and assignment operator to disallow peer copying @@ -36,11 +36,12 @@ public: NetworkPeer& operator=(const NetworkPeer& otherPeer); bool isNull() const { return _uuid.isNull(); } + bool hasSockets() const { return !_localSocket.isNull() && !_publicSocket.isNull(); } const QUuid& getUUID() const { return _uuid; } void setUUID(const QUuid& uuid) { _uuid = uuid; } - void reset(); + void softReset(); const HifiSockAddr& getPublicSocket() const { return _publicSocket; } virtual void setPublicSocket(const HifiSockAddr& publicSocket) { _publicSocket = publicSocket; } @@ -65,11 +66,11 @@ public: float getOutboundBandwidth(); // in kbps float getInboundBandwidth(); // in kbps - void startPingTimer(); - void stopPingTimer(); - friend QDataStream& operator<<(QDataStream& out, const NetworkPeer& peer); friend QDataStream& operator>>(QDataStream& in, NetworkPeer& peer); +public slots: + void startPingTimer(); + void stopPingTimer(); signals: void pingTimerTimeout(); protected: diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 0838ec18ec..bfd0c64b96 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -73,8 +73,11 @@ NodeList::NodeList(char newOwnerType, unsigned short socketListenPort, unsigned // send an ICE heartbeat as soon as we get ice server information connect(&_domainHandler, &DomainHandler::iceSocketAndIDReceived, this, &NodeList::handleICEConnectionToDomainServer); - // handle ICE signal from DS so connection is attempted immediately - connect(&_domainHandler, &DomainHandler::requestICEConnectionAttempt, this, &NodeList::handleICEConnectionToDomainServer); + // handle ping timeout from DomainHandler to establish a connection with auto networked domain-server + connect(&_domainHandler.getICEPeer(), &NetworkPeer::pingTimerTimeout, this, &NodeList::pingPunchForDomainServer); + + // send a ping punch immediately + connect(&_domainHandler, &DomainHandler::icePeerSocketsReceived, this, &NodeList::pingPunchForDomainServer); // clear out NodeList when login is finished connect(&AccountManager::getInstance(), &AccountManager::loginComplete , this, &NodeList::reset); @@ -299,6 +302,7 @@ void NodeList::sendDomainServerCheckIn() { // we don't know our public socket and we need to send it to the domain server qCDebug(networking) << "Waiting for inital public socket from STUN. Will not send domain-server check in."; } else if (_domainHandler.getIP().isNull() && _domainHandler.requiresICE()) { + qCDebug(networking) << "Waiting for ICE discovered domain-server socket. Will not send domain-server check in."; handleICEConnectionToDomainServer(); } else if (!_domainHandler.getIP().isNull()) { bool isUsingDTLS = false; @@ -472,8 +476,9 @@ void NodeList::handleDSPathQueryResponse(const QByteArray& packet) { } void NodeList::handleICEConnectionToDomainServer() { - if (_domainHandler.getICEPeer().isNull() - || _domainHandler.getICEPeer().getConnectionAttempts() >= MAX_ICE_CONNECTION_ATTEMPTS) { + // if we're still waiting to get sockets we want to ping for the domain-server + // then send another heartbeat now + if (!_domainHandler.getICEPeer().hasSockets()) { _domainHandler.getICEPeer().resetConnectionAttempts(); @@ -482,7 +487,24 @@ void NodeList::handleICEConnectionToDomainServer() { LimitedNodeList::sendHeartbeatToIceServer(_domainHandler.getICEServerSockAddr(), _domainHandler.getICEClientID(), _domainHandler.getICEDomainID()); - } else { + } +} + +void NodeList::pingPunchForDomainServer() { + // make sure if we're here that we actually still need to ping the domain-server + if (_domainHandler.getIP().isNull() && _domainHandler.getICEPeer().hasSockets()) { + + // check if we've hit the number of pings we'll send to the DS before we consider it a fail + const int NUM_DOMAIN_SERVER_PINGS_BEFORE_RESET = 2000 / UDP_PUNCH_PING_INTERVAL_MS; + + if (_domainHandler.getICEPeer().getConnectionAttempts() > 0 + && _domainHandler.getICEPeer().getConnectionAttempts() % NUM_DOMAIN_SERVER_PINGS_BEFORE_RESET == 0) { + // if we have then nullify the domain handler's network peer and send a fresh ICE heartbeat + + _domainHandler.getICEPeer().softReset(); + handleICEConnectionToDomainServer(); + } + qCDebug(networking) << "Sending ping packets to establish connectivity with domain-server with ID" << uuidStringWithoutCurlyBraces(_domainHandler.getICEDomainID()); @@ -624,7 +646,7 @@ void NodeList::startNodeHolePunch(const SharedNodePointer& node) { void NodeList::handleNodePingTimeout() { SharedNodePointer senderNode = nodeWithUUID(qobject_cast(sender())->getUUID()); - if (senderNode) { + if (senderNode && !senderNode->getActiveSocket()) { pingPunchForInactiveNode(senderNode); } } diff --git a/libraries/networking/src/NodeList.h b/libraries/networking/src/NodeList.h index d62715af8b..5e70b45b5f 100644 --- a/libraries/networking/src/NodeList.h +++ b/libraries/networking/src/NodeList.h @@ -64,7 +64,6 @@ public: void setAssignmentServerSocket(const HifiSockAddr& serverSocket) { _assignmentServerSocket = serverSocket; } void sendAssignment(Assignment& assignment); - void pingPunchForInactiveNode(const SharedNodePointer& node); public slots: void reset(); void sendDomainServerCheckIn(); @@ -77,6 +76,8 @@ private slots: void startNodeHolePunch(const SharedNodePointer& node); void handleNodePingTimeout(); + + void pingPunchForDomainServer(); private: NodeList() : LimitedNodeList(0, 0) { assert(false); } // Not implemented, needed for DependencyManager templates compile NodeList(char ownerType, unsigned short socketListenPort = 0, unsigned short dtlsListenPort = 0); @@ -96,6 +97,7 @@ private: void processDomainServerAddedNode(const QByteArray& packet); void parseNodeFromPacketStream(QDataStream& packetStream); + void pingPunchForInactiveNode(const SharedNodePointer& node); NodeType_t _ownerType; NodeSet _nodeTypesOfInterest; From 531ef1fa1c387c8a3f5d517c998b669cae1cc6e5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 28 May 2015 14:05:43 -0700 Subject: [PATCH 22/33] change ice-server heartbeat behaviour --- domain-server/src/DomainServer.cpp | 21 +-- domain-server/src/DomainServer.h | 2 +- ice-server/src/IceServer.cpp | 164 ++++++++----------- ice-server/src/IceServer.h | 10 +- libraries/networking/src/LimitedNodeList.cpp | 25 ++- libraries/networking/src/LimitedNodeList.h | 9 +- libraries/networking/src/NodeList.cpp | 6 +- libraries/networking/src/PacketHeaders.cpp | 31 ++-- libraries/networking/src/PacketHeaders.h | 11 +- 9 files changed, 135 insertions(+), 144 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 8eaf1a29f3..651685a3fd 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1357,24 +1357,21 @@ void DomainServer::sendICEPingPackets() { } } -void DomainServer::processICEHeartbeatResponse(const QByteArray& packet) { +void DomainServer::processICEPeerInformation(const QByteArray& packet) { // loop through the packet and pull out network peers // any peer we don't have we add to the hash, otherwise we update QDataStream iceResponseStream(packet); iceResponseStream.skipRawData(numBytesForPacketHeader(packet)); NetworkPeer receivedPeer; + iceResponseStream >> receivedPeer; - while (!iceResponseStream.atEnd()) { - iceResponseStream >> receivedPeer; - - if (!_connectedICEPeers.contains(receivedPeer.getUUID())) { - if (!_connectingICEPeers.contains(receivedPeer.getUUID())) { - qDebug() << "New peer requesting connection being added to hash -" << receivedPeer; - } - - _connectingICEPeers[receivedPeer.getUUID()] = receivedPeer; + if (!_connectedICEPeers.contains(receivedPeer.getUUID())) { + if (!_connectingICEPeers.contains(receivedPeer.getUUID())) { + qDebug() << "New peer requesting connection being added to hash -" << receivedPeer; } + + _connectingICEPeers[receivedPeer.getUUID()] = receivedPeer; } } @@ -1458,8 +1455,8 @@ void DomainServer::processDatagram(const QByteArray& receivedPacket, const HifiS processICEPingReply(receivedPacket, senderSockAddr); break; } - case PacketTypeIceServerHeartbeatResponse: - processICEHeartbeatResponse(receivedPacket); + case PacketTypeIceServerPeerInformation: + processICEPeerInformation(receivedPacket); break; default: break; diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index c70c9cec13..302f683108 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -79,7 +79,7 @@ private: void setupAutomaticNetworking(); void sendHeartbeatToDataServer(const QString& networkAddress); void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr); - void processICEHeartbeatResponse(const QByteArray& packet); + void processICEPeerInformation(const QByteArray& packet); void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr); diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index e72555cac1..ec9943d2a1 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -33,133 +33,113 @@ IceServer::IceServer(int argc, char* argv[]) : qDebug() << "ice-server socket is listening on" << ICE_SERVER_DEFAULT_PORT; qDebug() << "monitoring http endpoint is listening on " << ICE_SERVER_MONITORING_PORT; _serverSocket.bind(QHostAddress::AnyIPv4, ICE_SERVER_DEFAULT_PORT); - + // call our process datagrams slot when the UDP socket has packets ready connect(&_serverSocket, &QUdpSocket::readyRead, this, &IceServer::processDatagrams); - + // setup our timer to clear inactive peers QTimer* inactivePeerTimer = new QTimer(this); connect(inactivePeerTimer, &QTimer::timeout, this, &IceServer::clearInactivePeers); inactivePeerTimer->start(CLEAR_INACTIVE_PEERS_INTERVAL_MSECS); - + } void IceServer::processDatagrams() { HifiSockAddr sendingSockAddr; QByteArray incomingPacket; - + while (_serverSocket.hasPendingDatagrams()) { incomingPacket.resize(_serverSocket.pendingDatagramSize()); - + _serverSocket.readDatagram(incomingPacket.data(), incomingPacket.size(), sendingSockAddr.getAddressPointer(), sendingSockAddr.getPortPointer()); - - - if (packetTypeForPacket(incomingPacket) == PacketTypeIceServerHeartbeat) { + + PacketType packetType = packetTypeForPacket(incomingPacket); + + if (packetType == PacketTypeIceServerHeartbeat) { + addOrUpdateHeartbeatingPeer(incomingPacket); + } else if (packetType == PacketTypeIceServerQuery) { + // this is a node hoping to connect to a heartbeating peer - do we have the heartbeating peer? QUuid senderUUID = uuidFromPacketHeader(incomingPacket); - + // pull the public and private sock addrs for this peer HifiSockAddr publicSocket, localSocket; - + QDataStream hearbeatStream(incomingPacket); hearbeatStream.skipRawData(numBytesForPacketHeader(incomingPacket)); - + hearbeatStream >> publicSocket >> localSocket; - - // make sure we have this sender in our peer hash - SharedNetworkPeer matchingPeer = _activePeers.value(senderUUID); - - if (!matchingPeer) { - // if we don't have this sender we need to create them now - matchingPeer = SharedNetworkPeer(new NetworkPeer(senderUUID, publicSocket, localSocket)); - _activePeers.insert(senderUUID, matchingPeer); - - qDebug() << "Added a new network peer" << *matchingPeer; - } else { - // we already had the peer so just potentially update their sockets - matchingPeer->setPublicSocket(publicSocket); - matchingPeer->setLocalSocket(localSocket); - - qDebug() << "Matched hearbeat to existing network peer" << *matchingPeer; - } - - // update our last heard microstamp for this network peer to now - matchingPeer->setLastHeardMicrostamp(usecTimestampNow()); - + // check if this node also included a UUID that they would like to connect to QUuid connectRequestID; hearbeatStream >> connectRequestID; - - // get the peers asking for connections with this peer - QSet& requestingConnections = _currentConnections[senderUUID]; - - if (!connectRequestID.isNull()) { - qDebug() << "Peer wants to connect to peer with ID" << uuidStringWithoutCurlyBraces(connectRequestID); - - // ensure this peer is in the set of current connections for the peer with ID it wants to connect with - _currentConnections[connectRequestID].insert(senderUUID); - - // add the ID of the node they have said they would like to connect to - requestingConnections.insert(connectRequestID); - } - - if (requestingConnections.size() > 0) { - // send a heartbeart response based on the set of connections - qDebug() << "Sending a heartbeat response to" << senderUUID << "who has" << requestingConnections.size() - << "potential connections"; - sendHeartbeatResponse(sendingSockAddr, requestingConnections); + + SharedNetworkPeer matchingPeer = _activePeers.value(connectRequestID); + + if (matchingPeer) { + // we have the peer they want to connect to - send them pack the information for that peer + sendPeerInformationPacket(matchingPeer, sendingSockAddr); } } } } -void IceServer::sendHeartbeatResponse(const HifiSockAddr& destinationSockAddr, QSet& connections) { - QSet::iterator peerID = connections.begin(); - +SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(const QByteArray& incomingPacket) { + QUuid senderUUID = uuidFromPacketHeader(incomingPacket); + + // pull the public and private sock addrs for this peer + HifiSockAddr publicSocket, localSocket; + + QDataStream hearbeatStream(incomingPacket); + hearbeatStream.skipRawData(numBytesForPacketHeader(incomingPacket)); + + hearbeatStream >> publicSocket >> localSocket; + + // make sure we have this sender in our peer hash + SharedNetworkPeer matchingPeer = _activePeers.value(senderUUID); + + if (!matchingPeer) { + // if we don't have this sender we need to create them now + matchingPeer = SharedNetworkPeer(new NetworkPeer(senderUUID, publicSocket, localSocket)); + _activePeers.insert(senderUUID, matchingPeer); + + qDebug() << "Added a new network peer" << *matchingPeer; + } else { + // we already had the peer so just potentially update their sockets + matchingPeer->setPublicSocket(publicSocket); + matchingPeer->setLocalSocket(localSocket); + + qDebug() << "Matched hearbeat to existing network peer" << *matchingPeer; + } + + // update our last heard microstamp for this network peer to now + matchingPeer->setLastHeardMicrostamp(usecTimestampNow()); + + return matchingPeer; +} + +void IceServer::sendPeerInformationPacket(const SharedNetworkPeer& peer, const HifiSockAddr& destinationSockAddr) { QByteArray outgoingPacket(MAX_PACKET_SIZE, 0); - int currentPacketSize = populatePacketHeaderWithUUID(outgoingPacket, PacketTypeIceServerHeartbeatResponse, _id); + int currentPacketSize = populatePacketHeaderWithUUID(outgoingPacket, PacketTypeIceServerPeerInformation, _id); int numHeaderBytes = currentPacketSize; - - // go through the connections, sending packets containing connection information for those nodes - while (peerID != connections.end()) { - SharedNetworkPeer matchingPeer = _activePeers.value(*peerID); - // if this node is inactive we remove it from the set - if (!matchingPeer) { - peerID = connections.erase(peerID); - } else { - // get the byte array for this peer - QByteArray peerBytes = matchingPeer->toByteArray(); - - if (currentPacketSize + peerBytes.size() > MAX_PACKET_SIZE) { - // write the current packet - _serverSocket.writeDatagram(outgoingPacket.data(), currentPacketSize, - destinationSockAddr.getAddress(), destinationSockAddr.getPort()); - - // reset the packet size to our number of header bytes - currentPacketSize = populatePacketHeaderWithUUID(outgoingPacket, PacketTypeIceServerHeartbeatResponse, _id); - } - - // append the current peer bytes - outgoingPacket.insert(currentPacketSize, peerBytes); - currentPacketSize += peerBytes.size(); - - ++peerID; - } - } - - if (currentPacketSize > numHeaderBytes) { - // write the last packet, if there is data in it - _serverSocket.writeDatagram(outgoingPacket.data(), currentPacketSize, - destinationSockAddr.getAddress(), destinationSockAddr.getPort()); - } + + // get the byte array for this peer + QByteArray peerBytes = peer->toByteArray(); + outgoingPacket.replace(numHeaderBytes, peerBytes.size(), peerBytes); + + currentPacketSize += peerBytes.size(); + + // write the current packet + _serverSocket.writeDatagram(outgoingPacket.data(), outgoingPacket.size(), + destinationSockAddr.getAddress(), destinationSockAddr.getPort()); } void IceServer::clearInactivePeers() { NetworkPeerHash::iterator peerItem = _activePeers.begin(); - + while (peerItem != _activePeers.end()) { SharedNetworkPeer peer = peerItem.value(); - + if ((usecTimestampNow() - peer->getLastHeardMicrostamp()) > (PEER_SILENCE_THRESHOLD_MSECS * 1000)) { qDebug() << "Removing peer from memory for inactivity -" << *peer; peerItem = _activePeers.erase(peerItem); @@ -171,11 +151,9 @@ void IceServer::clearInactivePeers() { } bool IceServer::handleHTTPRequest(HTTPConnection* connection, const QUrl& url, bool skipSubHandler) { - // // We need an HTTP handler in order to monitor the health of the ice server // The correct functioning of the ICE server will be determined by its HTTP availability, - // - + if (connection->requestOperation() == QNetworkAccessManager::GetOperation) { if (url.path() == "/status") { connection->respond(HTTPConnection::StatusCode200, QByteArray::number(_activePeers.size())); diff --git a/ice-server/src/IceServer.h b/ice-server/src/IceServer.h index be6d298e3d..454a867ec2 100644 --- a/ice-server/src/IceServer.h +++ b/ice-server/src/IceServer.h @@ -31,14 +31,14 @@ private slots: void processDatagrams(); void clearInactivePeers(); private: - - void sendHeartbeatResponse(const HifiSockAddr& destinationSockAddr, QSet& connections); - + + SharedNetworkPeer addOrUpdateHeartbeatingPeer(const QByteArray& incomingPacket); + void sendPeerInformationPacket(const SharedNetworkPeer& peer, const HifiSockAddr& destinationSockAddr); + QUuid _id; QUdpSocket _serverSocket; NetworkPeerHash _activePeers; - QHash > _currentConnections; HTTPManager _httpManager; }; -#endif // hifi_IceServer_h \ No newline at end of file +#endif // hifi_IceServer_h diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 7102df62e0..61d844c1be 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -814,23 +814,30 @@ void LimitedNodeList::updateLocalSockAddr() { } } -void LimitedNodeList::sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr, - QUuid headerID, const QUuid& connectionRequestID) { +void LimitedNodeList::sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr) { + sendPacketToIceServer(PacketTypeIceServerHeartbeat, iceServerSockAddr, _sessionUUID); +} - if (headerID.isNull()) { - headerID = _sessionUUID; - } +void LimitedNodeList::sendPeerQueryToIceServer(const HifiSockAddr& iceServerSockAddr, const QUuid& clientID, + const QUuid& peerID) { + sendPacketToIceServer(PacketTypeIceServerQuery, iceServerSockAddr, clientID, peerID); +} - QByteArray iceRequestByteArray = byteArrayWithUUIDPopulatedHeader(PacketTypeIceServerHeartbeat, headerID); +void LimitedNodeList::sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr, + const QUuid& headerID, const QUuid& peerID) { + + QByteArray iceRequestByteArray = byteArrayWithUUIDPopulatedHeader(packetType, headerID); QDataStream iceDataStream(&iceRequestByteArray, QIODevice::Append); iceDataStream << _publicSockAddr << _localSockAddr; - if (!connectionRequestID.isNull()) { - iceDataStream << connectionRequestID; + if (packetType == PacketTypeIceServerQuery) { + assert(!peerID.isNull()); + + iceDataStream << peerID; qCDebug(networking) << "Sending packet to ICE server to request connection info for peer with ID" - << uuidStringWithoutCurlyBraces(connectionRequestID); + << uuidStringWithoutCurlyBraces(peerID); } writeUnverifiedDatagram(iceRequestByteArray, iceServerSockAddr); diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index fdfb1fa834..ec4b7546f0 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -89,7 +89,7 @@ public: SetPublicSocketFromSTUN, SetICEServerHostname, SetICEServerSocket, - SendICEServerHearbeat, + SendICEServerQuery, ReceiveDSPeerInformation, SendPingsToDS, SetDomainHostname, @@ -178,8 +178,8 @@ public: virtual bool processSTUNResponse(const QByteArray& packet); - void sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr, - QUuid headerID = QUuid(), const QUuid& connectRequestID = QUuid()); + void sendHeartbeatToIceServer(const HifiSockAddr& iceServerSockAddr); + void sendPeerQueryToIceServer(const HifiSockAddr& iceServerSockAddr, const QUuid& clientID, const QUuid& peerID); template void eachNode(NodeLambda functor) { @@ -276,6 +276,9 @@ protected: void stopInitialSTUNUpdate(bool success); + void sendPacketToIceServer(PacketType packetType, const HifiSockAddr& iceServerSockAddr, const QUuid& headerID, + const QUuid& peerRequestID = QUuid()); + QUuid _sessionUUID; NodeHash _nodeHash; QReadWriteLock _nodeMutex; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index bfd0c64b96..dc6b33d0a9 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -192,7 +192,7 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr _domainHandler.parseDTLSRequirementPacket(packet); break; } - case PacketTypeIceServerHeartbeatResponse: { + case PacketTypeIceServerPeerInformation: { _domainHandler.processICEResponsePacket(packet); break; } @@ -482,9 +482,9 @@ void NodeList::handleICEConnectionToDomainServer() { _domainHandler.getICEPeer().resetConnectionAttempts(); - flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendICEServerHearbeat); + flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendICEServerQuery); - LimitedNodeList::sendHeartbeatToIceServer(_domainHandler.getICEServerSockAddr(), + LimitedNodeList::sendPeerQueryToIceServer(_domainHandler.getICEServerSockAddr(), _domainHandler.getICEClientID(), _domainHandler.getICEDomainID()); } diff --git a/libraries/networking/src/PacketHeaders.cpp b/libraries/networking/src/PacketHeaders.cpp index c5030fb1e7..4b02a32cad 100644 --- a/libraries/networking/src/PacketHeaders.cpp +++ b/libraries/networking/src/PacketHeaders.cpp @@ -78,6 +78,9 @@ PacketVersion versionForPacketType(PacketType packetType) { return 2; case PacketTypeAudioStreamStats: return 1; + case PacketTypeIceServerHeartbeat: + case PacketTypeIceServerQuery: + return 1; default: return 0; } @@ -125,7 +128,9 @@ QString nameForPacketType(PacketType packetType) { PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityEditNack); PACKET_TYPE_NAME_LOOKUP(PacketTypeSignedTransactionPayment); PACKET_TYPE_NAME_LOOKUP(PacketTypeIceServerHeartbeat); - PACKET_TYPE_NAME_LOOKUP(PacketTypeIceServerHeartbeatResponse); + PACKET_TYPE_NAME_LOOKUP(PacketTypeDomainServerAddedNode); + PACKET_TYPE_NAME_LOOKUP(PacketTypeIceServerQuery); + PACKET_TYPE_NAME_LOOKUP(PacketTypeIceServerPeerInformation); PACKET_TYPE_NAME_LOOKUP(PacketTypeUnverifiedPing); PACKET_TYPE_NAME_LOOKUP(PacketTypeUnverifiedPingReply); PACKET_TYPE_NAME_LOOKUP(PacketTypeEntityAdd); @@ -149,33 +154,33 @@ int populatePacketHeaderWithUUID(QByteArray& packet, PacketType packetType, cons if (packet.size() < numBytesForPacketHeaderGivenPacketType(packetType)) { packet.resize(numBytesForPacketHeaderGivenPacketType(packetType)); } - + return populatePacketHeaderWithUUID(packet.data(), packetType, connectionUUID); } int populatePacketHeaderWithUUID(char* packet, PacketType packetType, const QUuid& connectionUUID) { int numTypeBytes = packArithmeticallyCodedValue(packetType, packet); packet[numTypeBytes] = versionForPacketType(packetType); - + char* position = packet + numTypeBytes + sizeof(PacketVersion); - + QByteArray rfcUUID = connectionUUID.toRfc4122(); memcpy(position, rfcUUID.constData(), NUM_BYTES_RFC4122_UUID); position += NUM_BYTES_RFC4122_UUID; - + if (!NON_VERIFIED_PACKETS.contains(packetType)) { // pack 16 bytes of zeros where the md5 hash will be placed once data is packed memset(position, 0, NUM_BYTES_MD5_HASH); position += NUM_BYTES_MD5_HASH; } - + if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) { // Pack zeros for the number of bytes that the sequence number requires. // The LimitedNodeList will handle packing in the sequence number when sending out the packet. memset(position, 0, sizeof(PacketSequenceNumber)); position += sizeof(PacketSequenceNumber); } - + // return the number of bytes written for pointer pushing return position - packet; } @@ -235,13 +240,13 @@ PacketSequenceNumber sequenceNumberFromHeader(const QByteArray& packet, PacketTy if (packetType == PacketTypeUnknown) { packetType = packetTypeForPacket(packet); } - + PacketSequenceNumber result = DEFAULT_SEQUENCE_NUMBER; - + if (SEQUENCE_NUMBERED_PACKETS.contains(packetType)) { memcpy(&result, packet.data() + sequenceNumberOffsetForPacketType(packetType), sizeof(PacketSequenceNumber)); } - + return result; } @@ -249,7 +254,7 @@ void replaceHashInPacket(QByteArray& packet, const QUuid& connectionUUID, Packet if (packetType == PacketTypeUnknown) { packetType = packetTypeForPacket(packet); } - + packet.replace(hashOffsetForPacketType(packetType), NUM_BYTES_MD5_HASH, hashForPacketAndConnectionUUID(packet, connectionUUID)); } @@ -258,7 +263,7 @@ void replaceSequenceNumberInPacket(QByteArray& packet, PacketSequenceNumber sequ if (packetType == PacketTypeUnknown) { packetType = packetTypeForPacket(packet); } - + packet.replace(sequenceNumberOffsetForPacketType(packetType), sizeof(PacketSequenceNumber), reinterpret_cast(&sequenceNumber), sizeof(PacketSequenceNumber)); } @@ -268,7 +273,7 @@ void replaceHashAndSequenceNumberInPacket(QByteArray& packet, const QUuid& conne if (packetType == PacketTypeUnknown) { packetType = packetTypeForPacket(packet); } - + replaceHashInPacket(packet, connectionUUID, packetType); replaceSequenceNumberInPacket(packet, sequenceNumber, packetType); } diff --git a/libraries/networking/src/PacketHeaders.h b/libraries/networking/src/PacketHeaders.h index a702df7798..cf1a323741 100644 --- a/libraries/networking/src/PacketHeaders.h +++ b/libraries/networking/src/PacketHeaders.h @@ -25,6 +25,7 @@ // NOTE: if adding a new packet packetType, you can replace one marked usable or add at the end // NOTE: if you want the name of the packet packetType to be available for debugging or logging, update nameForPacketType() as well + enum PacketType { PacketTypeUnknown, // 0 PacketTypeStunResponse, @@ -50,8 +51,8 @@ enum PacketType { PacketTypeDomainServerPathQuery, PacketTypeDomainServerPathResponse, PacketTypeDomainServerAddedNode, - UNUSED_4, - UNUSED_5, // 25 + PacketTypeIceServerPeerInformation, + PacketTypeIceServerQuery, // 25 PacketTypeOctreeStats, PacketTypeJurisdiction, PacketTypeJurisdictionRequest, @@ -77,7 +78,6 @@ enum PacketType { PacketTypeEntityEditNack, PacketTypeSignedTransactionPayment, PacketTypeIceServerHeartbeat, // 50 - PacketTypeIceServerHeartbeatResponse, PacketTypeUnverifiedPing, PacketTypeUnverifiedPingReply, PacketTypeParticleEntitiesFix @@ -96,8 +96,9 @@ const QSet NON_VERIFIED_PACKETS = QSet() << PacketTypeCreateAssignment << PacketTypeRequestAssignment << PacketTypeStunResponse << PacketTypeNodeJsonStats << PacketTypeEntityQuery << PacketTypeOctreeDataNack << PacketTypeEntityEditNack - << PacketTypeIceServerHeartbeat << PacketTypeIceServerHeartbeatResponse - << PacketTypeUnverifiedPing << PacketTypeUnverifiedPingReply << PacketTypeStopNode + << PacketTypeIceServerHeartbeat << PacketTypeIceServerPeerInformation + << PacketTypeIceServerQuery << PacketTypeUnverifiedPing + << PacketTypeUnverifiedPingReply << PacketTypeStopNode << PacketTypeDomainServerPathQuery << PacketTypeDomainServerPathResponse << PacketTypeDomainServerAddedNode; From e096cbe04033d14f043768cbf6404611499adde2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 28 May 2015 14:25:18 -0700 Subject: [PATCH 23/33] have ice-server immediately send peer to DS --- domain-server/src/DomainServer.cpp | 11 +-- ice-server/src/IceServer.cpp | 20 +++-- ice-server/src/IceServer.h | 2 +- libraries/networking/src/NetworkPeer.cpp | 110 ++++++++++++++++++++++- libraries/networking/src/NetworkPeer.h | 22 ++++- libraries/networking/src/Node.cpp | 73 --------------- libraries/networking/src/Node.h | 16 ---- 7 files changed, 142 insertions(+), 112 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 651685a3fd..0ffdabcb8a 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -656,16 +656,7 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock canAdjustLocks, canRez); // So that we can send messages to this node at will - we need to activate the correct socket on this node now - if (senderSockAddr == publicSockAddr) { - newNode->activatePublicSocket(); - } else if (senderSockAddr == localSockAddr) { - newNode->activateLocalSocket(); - } else { - // set the Node's symmetric socket to the sender socket - newNode->setSymmetricSocket(senderSockAddr); - // activate that symmetric socket - newNode->activateSymmetricSocket(); - } + newNode->activateMatchingOrNewSymmetricSocket(senderSockAddr); // when the newNode is created the linked data is also created // if this was a static assignment set the UUID, set the sendingSockAddr diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index ec9943d2a1..c39a2259af 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -57,8 +57,12 @@ void IceServer::processDatagrams() { PacketType packetType = packetTypeForPacket(incomingPacket); if (packetType == PacketTypeIceServerHeartbeat) { - addOrUpdateHeartbeatingPeer(incomingPacket); + SharedNetworkPeer peer = addOrUpdateHeartbeatingPeer(incomingPacket); + + // so that we can send packets to the heartbeating peer when we need, we need to activate a socket now + peer->activateMatchingOrNewSymmetricSocket(sendingSockAddr); } else if (packetType == PacketTypeIceServerQuery) { + // this is a node hoping to connect to a heartbeating peer - do we have the heartbeating peer? QUuid senderUUID = uuidFromPacketHeader(incomingPacket); @@ -78,7 +82,13 @@ void IceServer::processDatagrams() { if (matchingPeer) { // we have the peer they want to connect to - send them pack the information for that peer - sendPeerInformationPacket(matchingPeer, sendingSockAddr); + sendPeerInformationPacket(matchingPeer.data(), &sendingSockAddr); + + // we also need to send them to the active peer they are hoping to connect to + // create a dummy peer object we can pass to sendPeerInformationPacket + + NetworkPeer dummyPeer(senderUUID, publicSocket, localSocket); + sendPeerInformationPacket(dummyPeer, matchingPeer->getActiveSocket()); } } } @@ -118,20 +128,20 @@ SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(const QByteArray& incom return matchingPeer; } -void IceServer::sendPeerInformationPacket(const SharedNetworkPeer& peer, const HifiSockAddr& destinationSockAddr) { +void IceServer::sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr) { QByteArray outgoingPacket(MAX_PACKET_SIZE, 0); int currentPacketSize = populatePacketHeaderWithUUID(outgoingPacket, PacketTypeIceServerPeerInformation, _id); int numHeaderBytes = currentPacketSize; // get the byte array for this peer - QByteArray peerBytes = peer->toByteArray(); + QByteArray peerBytes = peer.toByteArray(); outgoingPacket.replace(numHeaderBytes, peerBytes.size(), peerBytes); currentPacketSize += peerBytes.size(); // write the current packet _serverSocket.writeDatagram(outgoingPacket.data(), outgoingPacket.size(), - destinationSockAddr.getAddress(), destinationSockAddr.getPort()); + destinationSockAddr->getAddress(), destinationSockAddr->getPort()); } void IceServer::clearInactivePeers() { diff --git a/ice-server/src/IceServer.h b/ice-server/src/IceServer.h index 454a867ec2..1f213fa606 100644 --- a/ice-server/src/IceServer.h +++ b/ice-server/src/IceServer.h @@ -33,7 +33,7 @@ private slots: private: SharedNetworkPeer addOrUpdateHeartbeatingPeer(const QByteArray& incomingPacket); - void sendPeerInformationPacket(const SharedNetworkPeer& peer, const HifiSockAddr& destinationSockAddr); + void sendPeerInformationPacket(const NetworkPeer& peer, const HifiSockAddr* destinationSockAddr); QUuid _id; QUdpSocket _serverSocket; diff --git a/libraries/networking/src/NetworkPeer.cpp b/libraries/networking/src/NetworkPeer.cpp index a6ed7e44fb..77e3de13e0 100644 --- a/libraries/networking/src/NetworkPeer.cpp +++ b/libraries/networking/src/NetworkPeer.cpp @@ -9,12 +9,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include +#include "NetworkPeer.h" + +#include +#include #include #include -#include "NetworkPeer.h" +#include "NetworkLogging.h" + #include "BandwidthRecorder.h" NetworkPeer::NetworkPeer(QObject* parent) : @@ -22,6 +26,8 @@ NetworkPeer::NetworkPeer(QObject* parent) : _uuid(), _publicSocket(), _localSocket(), + _symmetricSocket(), + _activeSocket(NULL), _wakeTimestamp(QDateTime::currentMSecsSinceEpoch()), _lastHeardMicrostamp(usecTimestampNow()), _connectionAttempts(0) @@ -29,10 +35,13 @@ NetworkPeer::NetworkPeer(QObject* parent) : } -NetworkPeer::NetworkPeer(const QUuid& uuid, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket) : +NetworkPeer::NetworkPeer(const QUuid& uuid, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, QObject* parent) : + QObject(parent), _uuid(uuid), _publicSocket(publicSocket), _localSocket(localSocket), + _symmetricSocket(), + _activeSocket(NULL), _wakeTimestamp(QDateTime::currentMSecsSinceEpoch()), _lastHeardMicrostamp(usecTimestampNow()), _connectionAttempts(0) @@ -44,6 +53,17 @@ NetworkPeer::NetworkPeer(const NetworkPeer& otherPeer) : QObject() { _uuid = otherPeer._uuid; _publicSocket = otherPeer._publicSocket; _localSocket = otherPeer._localSocket; + _symmetricSocket = otherPeer._symmetricSocket; + + if (otherPeer._activeSocket) { + if (otherPeer._activeSocket == &otherPeer._localSocket) { + _activeSocket = &_localSocket; + } else if (otherPeer._activeSocket == &otherPeer._publicSocket) { + _activeSocket = &_publicSocket; + } else if (otherPeer._activeSocket == &otherPeer._symmetricSocket) { + _activeSocket = &_symmetricSocket; + } + } _wakeTimestamp = otherPeer._wakeTimestamp; _lastHeardMicrostamp = otherPeer._lastHeardMicrostamp; @@ -62,11 +82,95 @@ void NetworkPeer::swap(NetworkPeer& otherPeer) { swap(_uuid, otherPeer._uuid); swap(_publicSocket, otherPeer._publicSocket); swap(_localSocket, otherPeer._localSocket); + swap(_symmetricSocket, otherPeer._symmetricSocket); + swap(_activeSocket, otherPeer._activeSocket); swap(_wakeTimestamp, otherPeer._wakeTimestamp); swap(_lastHeardMicrostamp, otherPeer._lastHeardMicrostamp); swap(_connectionAttempts, otherPeer._connectionAttempts); } +void NetworkPeer::setPublicSocket(const HifiSockAddr& publicSocket) { + if (publicSocket != _publicSocket) { + if (_activeSocket == &_publicSocket) { + // if the active socket was the public socket then reset it to NULL + _activeSocket = NULL; + } + + if (!_publicSocket.isNull()) { + qCDebug(networking) << "Public socket change for node" << *this; + } + + _publicSocket = publicSocket; + } +} + +void NetworkPeer::setLocalSocket(const HifiSockAddr& localSocket) { + if (localSocket != _localSocket) { + if (_activeSocket == &_localSocket) { + // if the active socket was the local socket then reset it to NULL + _activeSocket = NULL; + } + + if (!_localSocket.isNull()) { + qCDebug(networking) << "Local socket change for node" << *this; + } + + _localSocket = localSocket; + } +} + +void NetworkPeer::setSymmetricSocket(const HifiSockAddr& symmetricSocket) { + if (symmetricSocket != _symmetricSocket) { + if (_activeSocket == &_symmetricSocket) { + // if the active socket was the symmetric socket then reset it to NULL + _activeSocket = NULL; + } + + if (!_symmetricSocket.isNull()) { + qCDebug(networking) << "Symmetric socket change for node" << *this; + } + + _symmetricSocket = symmetricSocket; + } +} + +void NetworkPeer::setActiveSocket(HifiSockAddr* discoveredSocket) { + _activeSocket = discoveredSocket; + + // we have an active socket, stop our ping timer + stopPingTimer(); + + // we're now considered connected to this peer - reset the number of connection attemps + resetConnectionAttempts(); +} + +void NetworkPeer::activateLocalSocket() { + qCDebug(networking) << "Activating local socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid); + setActiveSocket(&_localSocket); +} + +void NetworkPeer::activatePublicSocket() { + qCDebug(networking) << "Activating public socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid); + setActiveSocket(&_publicSocket); +} + +void NetworkPeer::activateSymmetricSocket() { + qCDebug(networking) << "Activating symmetric socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid); + setActiveSocket(&_symmetricSocket); +} + +void NetworkPeer::activateMatchingOrNewSymmetricSocket(const HifiSockAddr& matchableSockAddr) { + if (matchableSockAddr == _publicSocket) { + activatePublicSocket(); + } else if (matchableSockAddr == _localSocket) { + activateLocalSocket(); + } else { + // set the Node's symmetric socket to the passed socket + setSymmetricSocket(matchableSockAddr); + activateSymmetricSocket(); + } +} + void NetworkPeer::softReset() { // a soft reset should clear the sockets and reset the number of connection attempts _localSocket.clear(); diff --git a/libraries/networking/src/NetworkPeer.h b/libraries/networking/src/NetworkPeer.h index 53b79c0126..a4db99ba9e 100644 --- a/libraries/networking/src/NetworkPeer.h +++ b/libraries/networking/src/NetworkPeer.h @@ -29,9 +29,8 @@ class NetworkPeer : public QObject { Q_OBJECT public: NetworkPeer(QObject* parent = 0); - NetworkPeer(const QUuid& uuid, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket); + NetworkPeer(const QUuid& uuid, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, QObject* parent = 0); - // privatize copy and assignment operator to disallow peer copying NetworkPeer(const NetworkPeer &otherPeer); NetworkPeer& operator=(const NetworkPeer& otherPeer); @@ -44,9 +43,20 @@ public: void softReset(); const HifiSockAddr& getPublicSocket() const { return _publicSocket; } - virtual void setPublicSocket(const HifiSockAddr& publicSocket) { _publicSocket = publicSocket; } const HifiSockAddr& getLocalSocket() const { return _localSocket; } - virtual void setLocalSocket(const HifiSockAddr& localSocket) { _localSocket = localSocket; } + const HifiSockAddr& getSymmetricSocket() const { return _symmetricSocket; } + + void setPublicSocket(const HifiSockAddr& publicSocket); + void setLocalSocket(const HifiSockAddr& localSocket); + void setSymmetricSocket(const HifiSockAddr& symmetricSocket); + + const HifiSockAddr* getActiveSocket() const { return _activeSocket; } + + void activatePublicSocket(); + void activateLocalSocket(); + void activateSymmetricSocket(); + + void activateMatchingOrNewSymmetricSocket(const HifiSockAddr& matchableSockAddr); quint64 getWakeTimestamp() const { return _wakeTimestamp; } void setWakeTimestamp(quint64 wakeTimestamp) { _wakeTimestamp = wakeTimestamp; } @@ -74,10 +84,14 @@ public slots: signals: void pingTimerTimeout(); protected: + void setActiveSocket(HifiSockAddr* discoveredSocket); + QUuid _uuid; HifiSockAddr _publicSocket; HifiSockAddr _localSocket; + HifiSockAddr _symmetricSocket; + HifiSockAddr* _activeSocket; quint64 _wakeTimestamp; quint64 _lastHeardMicrostamp; diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index e4144d080c..94b57b3f59 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -16,7 +16,6 @@ #include "Node.h" #include "SharedUtil.h" -#include "NetworkLogging.h" #include #include @@ -45,8 +44,6 @@ Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, bool canAdjustLocks, bool canRez, const QUuid& connectionSecret) : NetworkPeer(uuid, publicSocket, localSocket), _type(type), - _activeSocket(NULL), - _symmetricSocket(), _connectionSecret(connectionSecret), _linkedData(NULL), _isAlive(true), @@ -69,76 +66,6 @@ void Node::updateClockSkewUsec(int clockSkewSample) { _clockSkewUsec = (int)_clockSkewMovingPercentile.getValueAtPercentile(); } -void Node::setPublicSocket(const HifiSockAddr& publicSocket) { - if (publicSocket != _publicSocket) { - if (_activeSocket == &_publicSocket) { - // if the active socket was the public socket then reset it to NULL - _activeSocket = NULL; - } - - if (!_publicSocket.isNull()) { - qCDebug(networking) << "Public socket change for node" << *this; - } - - _publicSocket = publicSocket; - } -} - -void Node::setLocalSocket(const HifiSockAddr& localSocket) { - if (localSocket != _localSocket) { - if (_activeSocket == &_localSocket) { - // if the active socket was the local socket then reset it to NULL - _activeSocket = NULL; - } - - if (!_localSocket.isNull()) { - qCDebug(networking) << "Local socket change for node" << *this; - } - - _localSocket = localSocket; - } -} - -void Node::setSymmetricSocket(const HifiSockAddr& symmetricSocket) { - if (symmetricSocket != _symmetricSocket) { - if (_activeSocket == &_symmetricSocket) { - // if the active socket was the symmetric socket then reset it to NULL - _activeSocket = NULL; - } - - if (!_symmetricSocket.isNull()) { - qCDebug(networking) << "Symmetric socket change for node" << *this; - } - - _symmetricSocket = symmetricSocket; - } -} - -void Node::setActiveSocket(HifiSockAddr* discoveredSocket) { - _activeSocket = discoveredSocket; - - // we have an active socket, stop our ping timer - stopPingTimer(); - - // we're now considered connected to this peer - reset the number of connection attemps - resetConnectionAttempts(); -} - -void Node::activateLocalSocket() { - qCDebug(networking) << "Activating local socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid); - setActiveSocket(&_localSocket); -} - -void Node::activatePublicSocket() { - qCDebug(networking) << "Activating public socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid); - setActiveSocket(&_publicSocket); -} - -void Node::activateSymmetricSocket() { - qCDebug(networking) << "Activating symmetric socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid); - setActiveSocket(&_symmetricSocket); -} - PacketSequenceNumber Node::getLastSequenceNumberForPacketType(PacketType packetType) const { auto typeMatch = _lastSequenceNumbers.find(packetType); if (typeMatch != _lastSequenceNumbers.end()) { diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index 884e65a626..1eaf1a02c7 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -72,23 +72,12 @@ public: void updateClockSkewUsec(int clockSkewSample); QMutex& getMutex() { return _mutex; } - virtual void setPublicSocket(const HifiSockAddr& publicSocket); - virtual void setLocalSocket(const HifiSockAddr& localSocket); - const HifiSockAddr& getSymmetricSocket() const { return _symmetricSocket; } - virtual void setSymmetricSocket(const HifiSockAddr& symmetricSocket); - - const HifiSockAddr* getActiveSocket() const { return _activeSocket; } - void setCanAdjustLocks(bool canAdjustLocks) { _canAdjustLocks = canAdjustLocks; } bool getCanAdjustLocks() { return _canAdjustLocks; } void setCanRez(bool canRez) { _canRez = canRez; } bool getCanRez() { return _canRez; } - void activatePublicSocket(); - void activateLocalSocket(); - void activateSymmetricSocket(); - void setLastSequenceNumberForPacketType(PacketSequenceNumber sequenceNumber, PacketType packetType) { _lastSequenceNumbers[packetType] = sequenceNumber; } PacketSequenceNumber getLastSequenceNumberForPacketType(PacketType packetType) const; @@ -101,13 +90,8 @@ private: Node(const Node &otherNode); Node& operator=(Node otherNode); - void setActiveSocket(HifiSockAddr* discoveredSocket); - NodeType_t _type; - HifiSockAddr* _activeSocket; - HifiSockAddr _symmetricSocket; - QUuid _connectionSecret; NodeData* _linkedData; bool _isAlive; From da0c9fbc31d79de19afcd1b42fca79224384d1e5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 28 May 2015 14:37:16 -0700 Subject: [PATCH 24/33] fix send of existing peer to querier --- ice-server/src/IceServer.cpp | 4 +--- libraries/networking/src/LimitedNodeList.cpp | 2 +- libraries/networking/src/NetworkPeer.cpp | 19 ++++++++++++------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/ice-server/src/IceServer.cpp b/ice-server/src/IceServer.cpp index c39a2259af..8b5e5a4b39 100644 --- a/ice-server/src/IceServer.cpp +++ b/ice-server/src/IceServer.cpp @@ -82,7 +82,7 @@ void IceServer::processDatagrams() { if (matchingPeer) { // we have the peer they want to connect to - send them pack the information for that peer - sendPeerInformationPacket(matchingPeer.data(), &sendingSockAddr); + sendPeerInformationPacket(*(matchingPeer.data()), &sendingSockAddr); // we also need to send them to the active peer they are hoping to connect to // create a dummy peer object we can pass to sendPeerInformationPacket @@ -118,8 +118,6 @@ SharedNetworkPeer IceServer::addOrUpdateHeartbeatingPeer(const QByteArray& incom // we already had the peer so just potentially update their sockets matchingPeer->setPublicSocket(publicSocket); matchingPeer->setLocalSocket(localSocket); - - qDebug() << "Matched hearbeat to existing network peer" << *matchingPeer; } // update our last heard microstamp for this network peer to now diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 61d844c1be..c02eff4b75 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -840,7 +840,7 @@ void LimitedNodeList::sendPacketToIceServer(PacketType packetType, const HifiSoc << uuidStringWithoutCurlyBraces(peerID); } - writeUnverifiedDatagram(iceRequestByteArray, iceServerSockAddr); + writeUnverifiedDatagram(iceRequestByteArray, HifiSockAddr("127.0.0.1", ICE_SERVER_DEFAULT_PORT)); } void LimitedNodeList::putLocalPortIntoSharedMemory(const QString key, QObject* parent, quint16 localPort) { diff --git a/libraries/networking/src/NetworkPeer.cpp b/libraries/networking/src/NetworkPeer.cpp index 77e3de13e0..f1197e5da0 100644 --- a/libraries/networking/src/NetworkPeer.cpp +++ b/libraries/networking/src/NetworkPeer.cpp @@ -145,18 +145,24 @@ void NetworkPeer::setActiveSocket(HifiSockAddr* discoveredSocket) { } void NetworkPeer::activateLocalSocket() { - qCDebug(networking) << "Activating local socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid); - setActiveSocket(&_localSocket); + if (_activeSocket != &_localSocket) { + qCDebug(networking) << "Activating local socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid); + setActiveSocket(&_localSocket); + } } void NetworkPeer::activatePublicSocket() { - qCDebug(networking) << "Activating public socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid); - setActiveSocket(&_publicSocket); + if (_activeSocket != &_publicSocket) { + qCDebug(networking) << "Activating public socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid); + setActiveSocket(&_publicSocket); + } } void NetworkPeer::activateSymmetricSocket() { - qCDebug(networking) << "Activating symmetric socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid); - setActiveSocket(&_symmetricSocket); + if (_activeSocket != &_symmetricSocket) { + qCDebug(networking) << "Activating symmetric socket for network peer with ID" << uuidStringWithoutCurlyBraces(_uuid); + setActiveSocket(&_symmetricSocket); + } } void NetworkPeer::activateMatchingOrNewSymmetricSocket(const HifiSockAddr& matchableSockAddr) { @@ -182,7 +188,6 @@ void NetworkPeer::softReset() { _connectionAttempts = 0; } - QByteArray NetworkPeer::toByteArray() const { QByteArray peerByteArray; From 9d2e1773a09dc2cf9306585cd6da93dc16b06a8c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 28 May 2015 15:31:21 -0700 Subject: [PATCH 25/33] fix memory issues for immediate ice pings --- domain-server/src/DomainServer.cpp | 116 ++++++++++----------- domain-server/src/DomainServer.h | 8 +- libraries/networking/src/DomainHandler.cpp | 11 +- libraries/networking/src/NetworkPeer.cpp | 50 +++------ libraries/networking/src/NetworkPeer.h | 6 +- libraries/networking/src/NodeList.cpp | 4 +- 6 files changed, 84 insertions(+), 111 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 0ffdabcb8a..7827271aa9 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -369,7 +369,7 @@ void DomainServer::setupAutomaticNetworking() { // setup a timer to heartbeat with the ice-server every so often QTimer* iceHeartbeatTimer = new QTimer(this); - connect(iceHeartbeatTimer, &QTimer::timeout, this, &DomainServer::performICEUpdates); + connect(iceHeartbeatTimer, &QTimer::timeout, this, &DomainServer::sendHeartbeatToIceServer); iceHeartbeatTimer->start(ICE_HEARBEAT_INTERVAL_MSECS); } @@ -624,9 +624,17 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock QUuid nodeUUID; - if (_connectingICEPeers.contains(packetUUID) || _connectedICEPeers.contains(packetUUID)) { + HifiSockAddr discoveredSocket = senderSockAddr; + SharedNetworkPeer connectedPeer = _icePeers.value(packetUUID); + + if (connectedPeer) { // this user negotiated a connection with us via ICE, so re-use their ICE client ID nodeUUID = packetUUID; + + if (connectedPeer->getActiveSocket()) { + // set their discovered socket to whatever the activated socket on the network peer object was + discoveredSocket = *connectedPeer->getActiveSocket(); + } } else { // we got a packetUUID we didn't recognize, just add the node nodeUUID = QUuid::createUuid(); @@ -656,7 +664,7 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock canAdjustLocks, canRez); // So that we can send messages to this node at will - we need to activate the correct socket on this node now - newNode->activateMatchingOrNewSymmetricSocket(senderSockAddr); + newNode->activateMatchingOrNewSymmetricSocket(discoveredSocket); // when the newNode is created the linked data is also created // if this was a static assignment set the UUID, set the sendingSockAddr @@ -693,7 +701,6 @@ void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSock } } - unsigned int DomainServer::countConnectedUsers() { unsigned int result = 0; auto nodeList = DependencyManager::get(); @@ -936,13 +943,6 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); - // if we've established a connection via ICE with this peer, use that socket - // otherwise just try to reply back to them on their sending socket (although that may not work) - HifiSockAddr destinationSockAddr = _connectedICEPeers.value(node->getUUID()); - if (destinationSockAddr.isNull()) { - destinationSockAddr = senderSockAddr; - } - if (nodeInterestList.size() > 0) { // DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL; @@ -982,7 +982,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif } // always write the last broadcastPacket - limitedNodeList->writeUnverifiedDatagram(broadcastPacket, node, senderSockAddr); + limitedNodeList->writeUnverifiedDatagram(broadcastPacket, node); } QUuid DomainServer::connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB) { @@ -1306,45 +1306,45 @@ void DomainServer::sendHeartbeatToDataServer(const QString& networkAddress) { domainUpdateJSON.toUtf8()); } -// todo: have data-web respond with ice-server hostname to use - -void DomainServer::performICEUpdates() { - sendHeartbeatToIceServer(); - sendICEPingPackets(); -} +// TODO: have data-web respond with ice-server hostname to use void DomainServer::sendHeartbeatToIceServer() { DependencyManager::get()->sendHeartbeatToIceServer(_iceServerSocket); } -void DomainServer::sendICEPingPackets() { - auto nodeList = DependencyManager::get(); +const int NUM_PEER_PINGS_BEFORE_DELETE = 2000 / UDP_PUNCH_PING_INTERVAL_MS; - QHash::iterator peer = _connectingICEPeers.begin(); +void DomainServer::pingPunchForConnectingPeer(const SharedNetworkPeer& peer) { - while (peer != _connectingICEPeers.end()) { + if (peer->getConnectionAttempts() > 0 && peer->getConnectionAttempts() % NUM_PEER_PINGS_BEFORE_DELETE == 0) { + // we've reached the maximum number of ping attempts + qDebug() << "Maximum number of ping attempts reached for peer with ID" << peer->getUUID(); + qDebug() << "Removing from list of connecting peers."; - if (peer->getConnectionAttempts() >= MAX_ICE_CONNECTION_ATTEMPTS) { - // we've already tried to connect to this peer enough times - // remove it from our list - if it wants to re-connect it'll come back through ice-server - peer = _connectingICEPeers.erase(peer); - } else { - // send ping packets to this peer's interfaces - qDebug() << "Sending ping packets to establish connectivity with ICE peer with ID" - << peer->getUUID(); + _icePeers.remove(peer->getUUID()); + } else { + auto nodeList = DependencyManager::get(); - // send the ping packet to the local and public sockets for this node - QByteArray localPingPacket = nodeList->constructPingPacket(PingType::Local, false); - nodeList->writeUnverifiedDatagram(localPingPacket, peer->getLocalSocket()); + // send ping packets to this peer's interfaces + qDebug() << "Sending ping packets to establish connectivity with ICE peer with ID" + << peer->getUUID(); - QByteArray publicPingPacket = nodeList->constructPingPacket(PingType::Public, false); - nodeList->writeUnverifiedDatagram(publicPingPacket, peer->getPublicSocket()); + // send the ping packet to the local and public sockets for this node + QByteArray localPingPacket = nodeList->constructPingPacket(PingType::Local, false); + nodeList->writeUnverifiedDatagram(localPingPacket, peer->getLocalSocket()); - peer->incrementConnectionAttempts(); + QByteArray publicPingPacket = nodeList->constructPingPacket(PingType::Public, false); + nodeList->writeUnverifiedDatagram(publicPingPacket, peer->getPublicSocket()); - // go to next peer in hash - ++peer; - } + peer->incrementConnectionAttempts(); + } +} + +void DomainServer::handlePeerPingTimeout() { + SharedNetworkPeer senderPeer = _icePeers.value(qobject_cast(sender())->getUUID()); + + if (senderPeer && !senderPeer->getActiveSocket()) { + pingPunchForConnectingPeer(senderPeer); } } @@ -1354,31 +1354,32 @@ void DomainServer::processICEPeerInformation(const QByteArray& packet) { QDataStream iceResponseStream(packet); iceResponseStream.skipRawData(numBytesForPacketHeader(packet)); - NetworkPeer receivedPeer; - iceResponseStream >> receivedPeer; + NetworkPeer* receivedPeer = new NetworkPeer; + iceResponseStream >> *receivedPeer; - if (!_connectedICEPeers.contains(receivedPeer.getUUID())) { - if (!_connectingICEPeers.contains(receivedPeer.getUUID())) { - qDebug() << "New peer requesting connection being added to hash -" << receivedPeer; - } + if (!_icePeers.contains(receivedPeer->getUUID())) { + qDebug() << "New peer requesting ICE connection being added to hash -" << *receivedPeer; + SharedNetworkPeer newPeer = SharedNetworkPeer(receivedPeer); + _icePeers[receivedPeer->getUUID()] = newPeer; - _connectingICEPeers[receivedPeer.getUUID()] = receivedPeer; + // make sure we know when we should ping this peer + connect(newPeer.data(), &NetworkPeer::pingTimerTimeout, this, &DomainServer::handlePeerPingTimeout); + + // immediately ping the new peer, and start a timer to continue pinging it until we connect to it + newPeer->startPingTimer(); + pingPunchForConnectingPeer(newPeer); + } else { + delete receivedPeer; } } void DomainServer::processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr) { QUuid nodeUUID = uuidFromPacketHeader(packet); - NetworkPeer sendingPeer = _connectingICEPeers.take(nodeUUID); + SharedNetworkPeer sendingPeer = _icePeers.value(nodeUUID); - if (!sendingPeer.isNull()) { + if (sendingPeer) { // we had this NetworkPeer in our connecting list - add the right sock addr to our connected list - if (senderSockAddr == sendingPeer.getLocalSocket()) { - qDebug() << "Activating local socket for communication with network peer -" << sendingPeer; - _connectedICEPeers.insert(nodeUUID, sendingPeer.getLocalSocket()); - } else if (senderSockAddr == sendingPeer.getPublicSocket()) { - qDebug() << "Activating public socket for communication with network peer -" << sendingPeer; - _connectedICEPeers.insert(nodeUUID, sendingPeer.getPublicSocket()); - } + sendingPeer->activateMatchingOrNewSymmetricSocket(senderSockAddr); } } @@ -2106,9 +2107,8 @@ void DomainServer::nodeAdded(SharedNodePointer node) { void DomainServer::nodeKilled(SharedNodePointer node) { - // remove this node from the connecting / connected ICE lists (if they exist) - _connectingICEPeers.remove(node->getUUID()); - _connectedICEPeers.remove(node->getUUID()); + // if this peer connected via ICE then remove them from our ICE peers hash + _icePeers.remove(node->getUUID()); DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 302f683108..7a9fb2fe9b 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -63,10 +63,9 @@ private slots: void sendPendingTransactionsToServer(); void performIPAddressUpdate(const HifiSockAddr& newPublicSockAddr); - void performICEUpdates(); void sendHeartbeatToDataServer() { sendHeartbeatToDataServer(QString()); } void sendHeartbeatToIceServer(); - void sendICEPingPackets(); + void handlePeerPingTimeout(); private: void setupNodeListAndAssignments(const QUuid& sessionUUID = QUuid::createUuid()); bool optionallySetupOAuth(); @@ -81,6 +80,8 @@ private: void processICEPingReply(const QByteArray& packet, const HifiSockAddr& senderSockAddr); void processICEPeerInformation(const QByteArray& packet); + void pingPunchForConnectingPeer(const SharedNetworkPeer& peer); + void processDatagram(const QByteArray& receivedPacket, const HifiSockAddr& senderSockAddr); void handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr); @@ -153,8 +154,7 @@ private: QHash _userPublicKeys; - QHash _connectingICEPeers; - QHash _connectedICEPeers; + QHash _icePeers; QString _automaticNetworkingSetting; diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index e09dcb186b..38d1ade2ad 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -43,7 +43,7 @@ DomainHandler::DomainHandler(QObject* parent) : void DomainHandler::clearConnectionInfo() { _uuid = QUuid(); - _icePeer = NetworkPeer(); + _icePeer.reset(); if (requiresICE()) { // if we connected to this domain with ICE, re-set the socket so we reconnect through the ice-server @@ -299,16 +299,15 @@ void DomainHandler::processICEResponsePacket(const QByteArray& icePacket) { QDataStream iceResponseStream(icePacket); iceResponseStream.skipRawData(numBytesForPacketHeader(icePacket)); - NetworkPeer packetPeer; - iceResponseStream >> packetPeer; + iceResponseStream >> _icePeer; DependencyManager::get()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveDSPeerInformation); - if (packetPeer.getUUID() != _iceDomainID) { + if (_icePeer.getUUID() != _iceDomainID) { qCDebug(networking) << "Received a network peer with ID that does not match current domain. Will not attempt connection."; + _icePeer.reset(); } else { - qCDebug(networking) << "Received network peer object for domain -" << packetPeer; - _icePeer = packetPeer; + qCDebug(networking) << "Received network peer object for domain -" << _icePeer; // ask the peer object to start its ping timer _icePeer.startPingTimer(); diff --git a/libraries/networking/src/NetworkPeer.cpp b/libraries/networking/src/NetworkPeer.cpp index f1197e5da0..83e5e72a87 100644 --- a/libraries/networking/src/NetworkPeer.cpp +++ b/libraries/networking/src/NetworkPeer.cpp @@ -49,44 +49,8 @@ NetworkPeer::NetworkPeer(const QUuid& uuid, const HifiSockAddr& publicSocket, co } -NetworkPeer::NetworkPeer(const NetworkPeer& otherPeer) : QObject() { - _uuid = otherPeer._uuid; - _publicSocket = otherPeer._publicSocket; - _localSocket = otherPeer._localSocket; - _symmetricSocket = otherPeer._symmetricSocket; - - if (otherPeer._activeSocket) { - if (otherPeer._activeSocket == &otherPeer._localSocket) { - _activeSocket = &_localSocket; - } else if (otherPeer._activeSocket == &otherPeer._publicSocket) { - _activeSocket = &_publicSocket; - } else if (otherPeer._activeSocket == &otherPeer._symmetricSocket) { - _activeSocket = &_symmetricSocket; - } - } - - _wakeTimestamp = otherPeer._wakeTimestamp; - _lastHeardMicrostamp = otherPeer._lastHeardMicrostamp; - _connectionAttempts = otherPeer._connectionAttempts; -} - -NetworkPeer& NetworkPeer::operator=(const NetworkPeer& otherPeer) { - NetworkPeer temp(otherPeer); - swap(temp); - return *this; -} - -void NetworkPeer::swap(NetworkPeer& otherPeer) { - using std::swap; - - swap(_uuid, otherPeer._uuid); - swap(_publicSocket, otherPeer._publicSocket); - swap(_localSocket, otherPeer._localSocket); - swap(_symmetricSocket, otherPeer._symmetricSocket); - swap(_activeSocket, otherPeer._activeSocket); - swap(_wakeTimestamp, otherPeer._wakeTimestamp); - swap(_lastHeardMicrostamp, otherPeer._lastHeardMicrostamp); - swap(_connectionAttempts, otherPeer._connectionAttempts); +NetworkPeer::~NetworkPeer() { + qDebug() << "Removing network peer with ID" << _uuid; } void NetworkPeer::setPublicSocket(const HifiSockAddr& publicSocket) { @@ -181,6 +145,8 @@ void NetworkPeer::softReset() { // a soft reset should clear the sockets and reset the number of connection attempts _localSocket.clear(); _publicSocket.clear(); + _symmetricSocket.clear(); + _activeSocket = NULL; // stop our ping timer since we don't have sockets to ping anymore anyways stopPingTimer(); @@ -188,6 +154,14 @@ void NetworkPeer::softReset() { _connectionAttempts = 0; } +void NetworkPeer::reset() { + softReset(); + + _uuid = QUuid(); + _wakeTimestamp = QDateTime::currentMSecsSinceEpoch(); + _lastHeardMicrostamp = usecTimestampNow(); +} + QByteArray NetworkPeer::toByteArray() const { QByteArray peerByteArray; diff --git a/libraries/networking/src/NetworkPeer.h b/libraries/networking/src/NetworkPeer.h index a4db99ba9e..8cbb6cbe24 100644 --- a/libraries/networking/src/NetworkPeer.h +++ b/libraries/networking/src/NetworkPeer.h @@ -31,8 +31,7 @@ public: NetworkPeer(QObject* parent = 0); NetworkPeer(const QUuid& uuid, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, QObject* parent = 0); - NetworkPeer(const NetworkPeer &otherPeer); - NetworkPeer& operator=(const NetworkPeer& otherPeer); + ~NetworkPeer(); bool isNull() const { return _uuid.isNull(); } bool hasSockets() const { return !_localSocket.isNull() && !_publicSocket.isNull(); } @@ -41,6 +40,7 @@ public: void setUUID(const QUuid& uuid) { _uuid = uuid; } void softReset(); + void reset(); const HifiSockAddr& getPublicSocket() const { return _publicSocket; } const HifiSockAddr& getLocalSocket() const { return _localSocket; } @@ -99,8 +99,6 @@ protected: QTimer* _pingTimer = NULL; int _connectionAttempts; -private: - void swap(NetworkPeer& otherPeer); }; QDebug operator<<(QDebug debug, const NetworkPeer &peer); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index dc6b33d0a9..326573ef35 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -193,7 +193,9 @@ void NodeList::processNodeData(const HifiSockAddr& senderSockAddr, const QByteAr break; } case PacketTypeIceServerPeerInformation: { - _domainHandler.processICEResponsePacket(packet); + if (!_domainHandler.getICEPeer().hasSockets()) { + _domainHandler.processICEResponsePacket(packet); + } break; } case PacketTypePing: { From 493a9da43eba8266eab3b5f45116fc02774e04f1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 28 May 2015 16:23:14 -0700 Subject: [PATCH 26/33] make sure DS log handler is cleaned up --- domain-server/src/DomainServer.cpp | 18 ++++++++++++++---- domain-server/src/DomainServer.h | 2 ++ libraries/networking/src/NodeList.cpp | 9 ++++++--- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 7827271aa9..47d629a8f1 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -68,6 +68,8 @@ DomainServer::DomainServer(int argc, char* argv[]) : LogUtils::init(); Setting::init(); + connect(this, &QCoreApplication::aboutToQuit, this, &DomainServer::aboutToQuit); + setOrganizationName("High Fidelity"); setOrganizationDomain("highfidelity.io"); setApplicationName("domain-server"); @@ -106,6 +108,11 @@ DomainServer::DomainServer(int argc, char* argv[]) : } } +void DomainServer::aboutToQuit() { + // clear the log handler so that Qt doesn't call the destructor on LogHandler + qInstallMessageHandler(0); +} + void DomainServer::restart() { qDebug() << "domain-server is restarting."; @@ -558,7 +565,6 @@ const NodeSet STATICALLY_ASSIGNED_NODES = NodeSet() << NodeType::AudioMixer << NodeType::AvatarMixer << NodeType::EntityServer; void DomainServer::handleConnectRequest(const QByteArray& packet, const HifiSockAddr& senderSockAddr) { - NodeType_t nodeType; HifiSockAddr publicSockAddr, localSockAddr; @@ -1341,10 +1347,14 @@ void DomainServer::pingPunchForConnectingPeer(const SharedNetworkPeer& peer) { } void DomainServer::handlePeerPingTimeout() { - SharedNetworkPeer senderPeer = _icePeers.value(qobject_cast(sender())->getUUID()); + NetworkPeer* senderPeer = qobject_cast(sender()); - if (senderPeer && !senderPeer->getActiveSocket()) { - pingPunchForConnectingPeer(senderPeer); + if (senderPeer) { + SharedNetworkPeer sharedPeer = _icePeers.value(senderPeer->getUUID()); + + if (sharedPeer && !sharedPeer->getActiveSocket()) { + pingPunchForConnectingPeer(sharedPeer); + } } } diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 7a9fb2fe9b..74dbde8b4b 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -57,6 +57,8 @@ public slots: void restart(); private slots: + void aboutToQuit(); + void loginFailed(); void readAvailableDatagrams(); void setupPendingAssignmentCredits(); diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 326573ef35..24a9225374 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -646,10 +646,13 @@ void NodeList::startNodeHolePunch(const SharedNodePointer& node) { } void NodeList::handleNodePingTimeout() { - SharedNodePointer senderNode = nodeWithUUID(qobject_cast(sender())->getUUID()); + Node* senderNode = qobject_cast(sender()); + if (senderNode) { + SharedNodePointer sharedNode = nodeWithUUID(senderNode->getUUID()); - if (senderNode && !senderNode->getActiveSocket()) { - pingPunchForInactiveNode(senderNode); + if (sharedNode && !sharedNode->getActiveSocket()) { + pingPunchForInactiveNode(sharedNode); + } } } From 265e0f3da6df688a01d37a0ff4e23b6f9a9cb398 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 28 May 2015 16:44:34 -0700 Subject: [PATCH 27/33] remove NetworkPeer dtor debug, fix ICE socket --- libraries/networking/src/LimitedNodeList.cpp | 2 +- libraries/networking/src/NetworkPeer.cpp | 4 ---- libraries/networking/src/NetworkPeer.h | 2 -- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index c02eff4b75..61d844c1be 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -840,7 +840,7 @@ void LimitedNodeList::sendPacketToIceServer(PacketType packetType, const HifiSoc << uuidStringWithoutCurlyBraces(peerID); } - writeUnverifiedDatagram(iceRequestByteArray, HifiSockAddr("127.0.0.1", ICE_SERVER_DEFAULT_PORT)); + writeUnverifiedDatagram(iceRequestByteArray, iceServerSockAddr); } void LimitedNodeList::putLocalPortIntoSharedMemory(const QString key, QObject* parent, quint16 localPort) { diff --git a/libraries/networking/src/NetworkPeer.cpp b/libraries/networking/src/NetworkPeer.cpp index 83e5e72a87..dfa4066dd2 100644 --- a/libraries/networking/src/NetworkPeer.cpp +++ b/libraries/networking/src/NetworkPeer.cpp @@ -49,10 +49,6 @@ NetworkPeer::NetworkPeer(const QUuid& uuid, const HifiSockAddr& publicSocket, co } -NetworkPeer::~NetworkPeer() { - qDebug() << "Removing network peer with ID" << _uuid; -} - void NetworkPeer::setPublicSocket(const HifiSockAddr& publicSocket) { if (publicSocket != _publicSocket) { if (_activeSocket == &_publicSocket) { diff --git a/libraries/networking/src/NetworkPeer.h b/libraries/networking/src/NetworkPeer.h index 8cbb6cbe24..d2802a1308 100644 --- a/libraries/networking/src/NetworkPeer.h +++ b/libraries/networking/src/NetworkPeer.h @@ -31,8 +31,6 @@ public: NetworkPeer(QObject* parent = 0); NetworkPeer(const QUuid& uuid, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, QObject* parent = 0); - ~NetworkPeer(); - bool isNull() const { return _uuid.isNull(); } bool hasSockets() const { return !_localSocket.isNull() && !_publicSocket.isNull(); } From 29d3ca85d936a03271207128208f9172db71702b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 28 May 2015 16:53:13 -0700 Subject: [PATCH 28/33] cleanup debug logs for DS/node ping punch --- domain-server/src/DomainServer.cpp | 8 ++++---- libraries/networking/src/NodeList.cpp | 17 +++++++++-------- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 47d629a8f1..24d2d4812e 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -1331,10 +1331,6 @@ void DomainServer::pingPunchForConnectingPeer(const SharedNetworkPeer& peer) { } else { auto nodeList = DependencyManager::get(); - // send ping packets to this peer's interfaces - qDebug() << "Sending ping packets to establish connectivity with ICE peer with ID" - << peer->getUUID(); - // send the ping packet to the local and public sockets for this node QByteArray localPingPacket = nodeList->constructPingPacket(PingType::Local, false); nodeList->writeUnverifiedDatagram(localPingPacket, peer->getLocalSocket()); @@ -1377,6 +1373,10 @@ void DomainServer::processICEPeerInformation(const QByteArray& packet) { // immediately ping the new peer, and start a timer to continue pinging it until we connect to it newPeer->startPingTimer(); + + qDebug() << "Sending ping packets to establish connectivity with ICE peer with ID" + << newPeer->getUUID(); + pingPunchForConnectingPeer(newPeer); } else { delete receivedPeer; diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 24a9225374..26cd3f1425 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -499,17 +499,18 @@ void NodeList::pingPunchForDomainServer() { // check if we've hit the number of pings we'll send to the DS before we consider it a fail const int NUM_DOMAIN_SERVER_PINGS_BEFORE_RESET = 2000 / UDP_PUNCH_PING_INTERVAL_MS; - if (_domainHandler.getICEPeer().getConnectionAttempts() > 0 - && _domainHandler.getICEPeer().getConnectionAttempts() % NUM_DOMAIN_SERVER_PINGS_BEFORE_RESET == 0) { - // if we have then nullify the domain handler's network peer and send a fresh ICE heartbeat + if (_domainHandler.getICEPeer().getConnectionAttempts() == 0) { + qCDebug(networking) << "Sending ping packets to establish connectivity with domain-server with ID" + << uuidStringWithoutCurlyBraces(_domainHandler.getICEDomainID()); + } else { + if (_domainHandler.getICEPeer().getConnectionAttempts() % NUM_DOMAIN_SERVER_PINGS_BEFORE_RESET == 0) { + // if we have then nullify the domain handler's network peer and send a fresh ICE heartbeat - _domainHandler.getICEPeer().softReset(); - handleICEConnectionToDomainServer(); + _domainHandler.getICEPeer().softReset(); + handleICEConnectionToDomainServer(); + } } - qCDebug(networking) << "Sending ping packets to establish connectivity with domain-server with ID" - << uuidStringWithoutCurlyBraces(_domainHandler.getICEDomainID()); - flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::SendPingsToDS); // send the ping packet to the local and public sockets for this node From 046828ee5de33b5284eb1a8df643f0ba8f8e468f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 28 May 2015 16:58:58 -0700 Subject: [PATCH 29/33] make the LNL the parent of any Nodes it adds --- libraries/networking/src/LimitedNodeList.cpp | 2 +- libraries/networking/src/Node.cpp | 5 +++-- libraries/networking/src/Node.h | 3 ++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index 61d844c1be..aa0ab262d7 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -488,7 +488,7 @@ SharedNodePointer LimitedNodeList::addOrUpdateNode(const QUuid& uuid, NodeType_t return matchingNode; } else { // we didn't have this node, so add them - Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, canAdjustLocks, canRez, connectionSecret); + Node* newNode = new Node(uuid, nodeType, publicSocket, localSocket, canAdjustLocks, canRez, connectionSecret, this); if (nodeType == NodeType::AudioMixer) { LimitedNodeList::flagTimeForConnectionStep(LimitedNodeList::AddedAudioMixer); diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp index 94b57b3f59..05da87d69a 100644 --- a/libraries/networking/src/Node.cpp +++ b/libraries/networking/src/Node.cpp @@ -41,8 +41,9 @@ const QString& NodeType::getNodeTypeName(NodeType_t nodeType) { } Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, - const HifiSockAddr& localSocket, bool canAdjustLocks, bool canRez, const QUuid& connectionSecret) : - NetworkPeer(uuid, publicSocket, localSocket), + const HifiSockAddr& localSocket, bool canAdjustLocks, bool canRez, const QUuid& connectionSecret, + QObject* parent) : + NetworkPeer(uuid, publicSocket, localSocket, parent), _type(type), _connectionSecret(connectionSecret), _linkedData(NULL), diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index 1eaf1a02c7..b1f5d8b037 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -47,7 +47,8 @@ class Node : public NetworkPeer { public: Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket, const HifiSockAddr& localSocket, - bool canAdjustLocks, bool canRez, const QUuid& connectionSecret = QUuid()); + bool canAdjustLocks, bool canRez, const QUuid& connectionSecret = QUuid(), + QObject* parent = 0); ~Node(); bool operator==(const Node& otherNode) const { return _uuid == otherNode._uuid; } From cb7b23f3467905aacff60469cbf64872c39aa761 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 28 May 2015 17:04:15 -0700 Subject: [PATCH 30/33] install DS message handler in DomainServer --- domain-server/src/DomainServer.cpp | 2 ++ domain-server/src/main.cpp | 10 ++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 24d2d4812e..57d687f307 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -65,6 +65,8 @@ DomainServer::DomainServer(int argc, char* argv[]) : _settingsManager(), _iceServerSocket(ICE_SERVER_DEFAULT_HOSTNAME, ICE_SERVER_DEFAULT_PORT) { + qInstallMessageHandler(LogHandler::verboseMessageHandler); + LogUtils::init(); Setting::init(); diff --git a/domain-server/src/main.cpp b/domain-server/src/main.cpp index ba80e6fce0..790cc07c56 100644 --- a/domain-server/src/main.cpp +++ b/domain-server/src/main.cpp @@ -26,18 +26,16 @@ int main(int argc, char* argv[]) { #ifndef WIN32 setvbuf(stdout, NULL, _IOLBF, 0); #endif - - qInstallMessageHandler(LogHandler::verboseMessageHandler); - + int currentExitCode = 0; - + // use a do-while to handle domain-server restart do { DomainServer domainServer(argc, argv); currentExitCode = domainServer.exec(); } while (currentExitCode == DomainServer::EXIT_CODE_REBOOT); - - + + return currentExitCode; } From 7f86ca3f107b7eee041c1973d413362f094486a5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 28 May 2015 17:07:20 -0700 Subject: [PATCH 31/33] refresh the network dialog if it is already up --- interface/src/ui/DialogsManager.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index ca7a13eb07..309c3e0ffe 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -178,11 +178,14 @@ void DialogsManager::showIRCLink() { } void DialogsManager::showDomainConnectionDialog() { - if (!_domainConnectionDialog) { - // if the dialog already exists we delete it so the connection data is refreshed - maybeCreateDialog(_domainConnectionDialog); - - _domainConnectionDialog->show(); - _domainConnectionDialog->raise(); + // if the dialog already exists we delete it so the connection data is refreshed + if (_domainConnectionDialog) { + _domainConnectionDialog->close(); + _domainConnectionDialog = NULL; } + + maybeCreateDialog(_domainConnectionDialog); + + _domainConnectionDialog->show(); + _domainConnectionDialog->raise(); } From 5749fefcd5ffbff09a70c7c451a0716462993b3c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 28 May 2015 17:07:57 -0700 Subject: [PATCH 32/33] have show DS connect refresh existing dialog --- interface/src/ui/DialogsManager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 309c3e0ffe..1170e3c3a6 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -181,6 +181,7 @@ void DialogsManager::showDomainConnectionDialog() { // if the dialog already exists we delete it so the connection data is refreshed if (_domainConnectionDialog) { _domainConnectionDialog->close(); + _domainConnectionDialog->deleteLater(); _domainConnectionDialog = NULL; } From e91ee7e7e34f0250cb87505bec7949f72e199dfa Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 29 May 2015 09:49:54 -0700 Subject: [PATCH 33/33] only send add packet for nodes in interest set --- domain-server/src/DomainServer.cpp | 35 ++++++++-------------- domain-server/src/DomainServer.h | 5 ++-- domain-server/src/DomainServerNodeData.h | 26 +++++++++------- libraries/networking/src/LimitedNodeList.h | 2 -- libraries/networking/src/Node.h | 16 +--------- libraries/networking/src/NodeType.h | 34 +++++++++++++++++++++ 6 files changed, 65 insertions(+), 53 deletions(-) create mode 100644 libraries/networking/src/NodeType.h diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 57d687f307..034b98e8db 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -918,26 +918,8 @@ int DomainServer::parseNodeDataFromByteArray(QDataStream& packetStream, NodeType return packetStream.device()->pos(); } -NodeSet DomainServer::nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes) { - QDataStream packetStream(packet); - packetStream.skipRawData(numPreceedingBytes); - - quint8 numInterestTypes = 0; - packetStream >> numInterestTypes; - - quint8 nodeType; - NodeSet nodeInterestSet; - - for (int i = 0; i < numInterestTypes; i++) { - packetStream >> nodeType; - nodeInterestSet.insert((NodeType_t) nodeType); - } - - return nodeInterestSet; -} - void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr &senderSockAddr, - const NodeSet& nodeInterestList) { + const NodeSet& nodeInterestSet) { auto limitedNodeList = DependencyManager::get(); QByteArray broadcastPacket = limitedNodeList->byteArrayWithPopulatedHeader(PacketTypeDomainList); @@ -951,7 +933,10 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); - if (nodeInterestList.size() > 0) { + // store the nodeInterestSet on this DomainServerNodeData, in case it has changed + nodeData->setNodeInterestSet(nodeInterestSet); + + if (nodeInterestSet.size() > 0) { // DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL; int dataMTU = MAX_PACKET_SIZE; @@ -963,7 +948,7 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif QByteArray nodeByteArray; QDataStream nodeDataStream(&nodeByteArray, QIODevice::Append); - if (otherNode->getUUID() != node->getUUID() && nodeInterestList.contains(otherNode->getType())) { + if (otherNode->getUUID() != node->getUUID() && nodeInterestSet.contains(otherNode->getType())) { // don't send avatar nodes to other avatars, that will come from avatar mixer nodeDataStream << *otherNode.data(); @@ -1028,7 +1013,13 @@ void DomainServer::broadcastNewNode(const SharedNodePointer& addedNode) { limitedNodeList->eachMatchingNode( [&](const SharedNodePointer& node)->bool { - return (node->getLinkedData() && node->getActiveSocket() && node != addedNode); + if (node->getLinkedData() && node->getActiveSocket() && node != addedNode) { + // is the added Node in this node's interest list? + DomainServerNodeData* nodeData = dynamic_cast(node->getLinkedData()); + return nodeData->getNodeInterestSet().contains(addedNode->getType()); + } else { + return false; + } }, [&](const SharedNodePointer& node) { QByteArray rfcConnectionSecret = connectionSecretForNodes(node, addedNode).toRfc4122(); diff --git a/domain-server/src/DomainServer.h b/domain-server/src/DomainServer.h index 74dbde8b4b..f62ba89871 100644 --- a/domain-server/src/DomainServer.h +++ b/domain-server/src/DomainServer.h @@ -58,7 +58,7 @@ public slots: private slots: void aboutToQuit(); - + void loginFailed(); void readAvailableDatagrams(); void setupPendingAssignmentCredits(); @@ -100,9 +100,8 @@ private: HifiSockAddr& publicSockAddr, HifiSockAddr& localSockAddr, const HifiSockAddr& senderSockAddr); - NodeSet nodeInterestListFromPacket(const QByteArray& packet, int numPreceedingBytes); void sendDomainListToNode(const SharedNodePointer& node, const HifiSockAddr& senderSockAddr, - const NodeSet& nodeInterestList); + const NodeSet& nodeInterestSet); QUuid connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB); void broadcastNewNode(const SharedNodePointer& node); diff --git a/domain-server/src/DomainServerNodeData.h b/domain-server/src/DomainServerNodeData.h index 366ee8c730..a91a7e0b9c 100644 --- a/domain-server/src/DomainServerNodeData.h +++ b/domain-server/src/DomainServerNodeData.h @@ -12,44 +12,47 @@ #ifndef hifi_DomainServerNodeData_h #define hifi_DomainServerNodeData_h - #include #include #include #include #include +#include class DomainServerNodeData : public NodeData { public: DomainServerNodeData(); int parseData(const QByteArray& packet) { return 0; } - + const QJsonObject& getStatsJSONObject() const { return _statsJSONObject; } - + void parseJSONStatsPacket(const QByteArray& statsPacket); - + void setAssignmentUUID(const QUuid& assignmentUUID) { _assignmentUUID = assignmentUUID; } const QUuid& getAssignmentUUID() const { return _assignmentUUID; } - + void setWalletUUID(const QUuid& walletUUID) { _walletUUID = walletUUID; } const QUuid& getWalletUUID() const { return _walletUUID; } - + void setUsername(const QString& username) { _username = username; } const QString& getUsername() const { return _username; } - + QElapsedTimer& getPaymentIntervalTimer() { return _paymentIntervalTimer; } - + void setSendingSockAddr(const HifiSockAddr& sendingSockAddr) { _sendingSockAddr = sendingSockAddr; } const HifiSockAddr& getSendingSockAddr() { return _sendingSockAddr; } - + void setIsAuthenticated(bool isAuthenticated) { _isAuthenticated = isAuthenticated; } bool isAuthenticated() const { return _isAuthenticated; } - + QHash& getSessionSecretHash() { return _sessionSecretHash; } + + const NodeSet& getNodeInterestSet() const { return _nodeInterestSet; } + void setNodeInterestSet(const NodeSet& nodeInterestSet) { _nodeInterestSet = nodeInterestSet; } private: QJsonObject mergeJSONStatsFromNewObject(const QJsonObject& newObject, QJsonObject destinationObject); - + QHash _sessionSecretHash; QUuid _assignmentUUID; QUuid _walletUUID; @@ -58,6 +61,7 @@ private: QJsonObject _statsJSONObject; HifiSockAddr _sendingSockAddr; bool _isAuthenticated; + NodeSet _nodeInterestSet; }; #endif // hifi_DomainServerNodeData_h diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h index ec4b7546f0..94063f49b1 100644 --- a/libraries/networking/src/LimitedNodeList.h +++ b/libraries/networking/src/LimitedNodeList.h @@ -60,8 +60,6 @@ const QString USERNAME_UUID_REPLACEMENT_STATS_KEY = "$username"; class HifiSockAddr; -typedef QSet NodeSet; - typedef QSharedPointer SharedNodePointer; Q_DECLARE_METATYPE(SharedNodePointer) diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h index b1f5d8b037..4c1b6e9d18 100644 --- a/libraries/networking/src/Node.h +++ b/libraries/networking/src/Node.h @@ -23,25 +23,11 @@ #include "HifiSockAddr.h" #include "NetworkPeer.h" #include "NodeData.h" +#include "NodeType.h" #include "PacketHeaders.h" #include "SimpleMovingAverage.h" #include "MovingPercentile.h" -typedef quint8 NodeType_t; - -namespace NodeType { - const NodeType_t DomainServer = 'D'; - const NodeType_t EntityServer = 'o'; // was ModelServer - const NodeType_t EnvironmentServer = 'E'; - const NodeType_t Agent = 'I'; - const NodeType_t AudioMixer = 'M'; - const NodeType_t AvatarMixer = 'W'; - const NodeType_t Unassigned = 1; - - void init(); - const QString& getNodeTypeName(NodeType_t nodeType); -} - class Node : public NetworkPeer { Q_OBJECT public: diff --git a/libraries/networking/src/NodeType.h b/libraries/networking/src/NodeType.h new file mode 100644 index 0000000000..4427b87158 --- /dev/null +++ b/libraries/networking/src/NodeType.h @@ -0,0 +1,34 @@ +// +// NodeType.h +// libraries/networking/src +// +// Created by Stephen Birarda on 05/29/15. +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_NodeType_h +#define hifi_NodeType_h + +#pragma once + +typedef quint8 NodeType_t; + +namespace NodeType { + const NodeType_t DomainServer = 'D'; + const NodeType_t EntityServer = 'o'; // was ModelServer + const NodeType_t EnvironmentServer = 'E'; + const NodeType_t Agent = 'I'; + const NodeType_t AudioMixer = 'M'; + const NodeType_t AvatarMixer = 'W'; + const NodeType_t Unassigned = 1; + + void init(); + const QString& getNodeTypeName(NodeType_t nodeType); +} + +typedef QSet NodeSet; + +#endif // hifi_NodeType_h