fix move of connection steps to LNL

This commit is contained in:
Stephen Birarda 2015-05-27 15:50:31 -07:00
parent c7c542ef4c
commit 734798a365
6 changed files with 122 additions and 105 deletions

View file

@ -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<NodeList::ConnectionStep, quint64> times = DependencyManager::get<NodeList>()->getLastConnectionTimes();
QMap<quint64, LimitedNodeList::ConnectionStep> times = DependencyManager::get<NodeList>()->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<NodeList::ConnectionStep>(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

View file

@ -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<NodeList>()->flagTimeForConnectionStep(NodeList::ConnectionStep::ReceiveFirstAudioPacket);
DependencyManager::get<NodeList>()->flagTimeForConnectionStep(LimitedNodeList::ConnectionStep::ReceiveFirstAudioPacket);
if (_audioOutput) {
// Audio output must exist and be correctly set up if we're going to process received audio

View file

@ -61,6 +61,8 @@ LimitedNodeList::LimitedNodeList(unsigned short socketListenPort, unsigned short
firstCall = false;
}
qRegisterMetaType<ConnectionStep>("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;
}
}
}

View file

@ -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<quint64, ConnectionStep> getLastConnectionTimes() const
{ QReadLocker readLock(&_connectionTimeLock); return _lastConnectionTimes; }
void flagTimeForConnectionStep(ConnectionStep connectionStep);
public slots:
void reset();
void eraseAllNodes();
@ -269,6 +296,12 @@ protected:
QPointer<QTimer> _initialSTUNTimer;
int _numInitialSTUNRequests = 0;
bool _hasCompletedInitialSTUN = false;
quint64 _firstSTUNTime = 0;
quint64 _publicSocketUpdateTime = 0;
mutable QReadWriteLock _connectionTimeLock { };
QMap<quint64, ConnectionStep> _lastConnectionTimes;
bool _areConnectionTimesComplete = false;
template<typename IteratorLambda>
void eachNodeHashIterator(IteratorLambda functor) {
@ -279,6 +312,8 @@ protected:
functor(it);
}
}
private slots:
void flagTimeForConnectionStep(ConnectionStep connectionStep, quint64 timestamp);
};
#endif // hifi_LimitedNodeList_h

View file

@ -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>("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<NodeList>()->flagTimeForConnectionStep(NodeList::ConnectionStep::ReceiveFirstDSList);
DependencyManager::get<NodeList>()->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<NodeList::ConnectionStep>(i);
if (_lastConnectionTimes.contains(thisStep) && _lastConnectionTimes[thisStep] >= currentTime) {
_lastConnectionTimes[connectionStep] = timestamp;
break;
}
}
}
}

View file

@ -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<ConnectionStep, quint64> 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<ConnectionStep, quint64> _lastConnectionTimes;
friend class Application;
};