Merge branch 'atp' of https://github.com/birarda/hifi into protocol

This commit is contained in:
Atlante45 2015-07-30 17:25:15 -07:00
commit d999ca833f
21 changed files with 382 additions and 70 deletions

View file

@ -11,8 +11,10 @@
#include <QtCore/QObject>
#include <QDebug>
#include <BufferParser.h>
#include <udt/PacketHeaders.h>
#include <UUID.h>
#include "RegisteredMetaTypes.h"
#include "EntityItemID.h"

View file

@ -18,11 +18,11 @@
#include <GLMHelpers.h>
#include <SettingHandle.h>
#include <UUID.h>
#include "AddressManager.h"
#include "NodeList.h"
#include "NetworkLogging.h"
#include "AddressManager.h"
#include "UUID.h"
const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager";
const QString SETTINGS_CURRENT_ADDRESS_KEY = "address";

View file

@ -14,6 +14,7 @@
#include <QtCore/QSharedPointer>
#include "UUID.h"
#include "udt/Packet.h"
class NLPacket : public udt::Packet {

View file

@ -16,11 +16,10 @@
#include <QtCore/QDataStream>
#include <SharedUtil.h>
#include <UUID.h>
#include "NetworkLogging.h"
#include "BandwidthRecorder.h"
#include "NetworkLogging.h"
#include "UUID.h"
NetworkPeer::NetworkPeer(QObject* parent) :
QObject(parent),

View file

@ -12,10 +12,9 @@
#include <cstring>
#include <stdio.h>
#include <UUID.h>
#include "Node.h"
#include "SharedUtil.h"
#include "UUID.h"
#include <QtCore/QDataStream>
#include <QtCore/QDebug>

View file

@ -1,6 +1,6 @@
//
// WalletTransaction.cpp
// domain-server/src
// libraries/networking/src
//
// Created by Stephen Birarda on 2014-05-20.
// Copyright 2014 High Fidelity, Inc.
@ -11,10 +11,10 @@
#include <QtCore/QJsonObject>
#include <UUID.h>
#include "UUID.h"
#include "WalletTransaction.h"
WalletTransaction::WalletTransaction() :
_uuid(),
_destinationUUID(),
@ -64,4 +64,4 @@ void WalletTransaction::loadFromJson(const QJsonObject& jsonObject) {
_uuid = QUuid(transactionObject.value(TRANSACTION_ID_KEY).toString());
_destinationUUID = QUuid(transactionObject.value(TRANSACTION_DESTINATION_WALLET_ID_KEY).toString());
_amount = transactionObject.value(TRANSACTION_AMOUNT_KEY).toInt();
}
}

View file

@ -50,6 +50,8 @@ void Connection::sendReliablePacket(unique_ptr<Packet> packet) {
if (!_sendQueue) {
// Lasily create send queue
_sendQueue = SendQueue::create(_parentSocket, _destination);
QObject::connect(_sendQueue.get(), &SendQueue::packetSent, this, &Connection::packetSent);
}
_sendQueue->queuePacket(move(packet));
@ -74,7 +76,7 @@ void Connection::sendACK(bool wasCausedBySyncTimeout) {
auto currentTime = high_resolution_clock::now();
SequenceNumber nextACKNumber = nextACK();
Q_ASSERT_X(nextACKNumber < _lastSentACK, "Connection::sendACK", "Sending lower ACK, something is wrong");
Q_ASSERT_X(nextACKNumber >= _lastSentACK, "Connection::sendACK", "Sending lower ACK, something is wrong");
if (nextACKNumber == _lastSentACK) {
// We already sent this ACK, but check if we should re-send it.

View file

@ -15,6 +15,8 @@
#include <chrono>
#include <memory>
#include <QtCore/QObject>
#include "ConnectionStats.h"
#include "Constants.h"
#include "LossList.h"
@ -29,8 +31,8 @@ class ControlPacket;
class Packet;
class Socket;
class Connection {
class Connection : public QObject {
Q_OBJECT
public:
using SequenceNumberTimePair = std::pair<SequenceNumber, std::chrono::high_resolution_clock::time_point>;
using SentACKMap = std::unordered_map<SequenceNumber, SequenceNumberTimePair>;
@ -44,6 +46,9 @@ public:
bool processReceivedSequenceNumber(SequenceNumber sequenceNumber); // returns indicates if this packet was a duplicate
void processControl(std::unique_ptr<ControlPacket> controlPacket);
signals:
void packetSent();
private:
void sendACK(bool wasCausedBySyncTimeout = true);

View file

@ -46,12 +46,12 @@ std::unique_ptr<ControlPacket> ControlPacket::create(Type type, qint64 size) {
std::unique_ptr<ControlPacket> controlPacket;
if (size == -1) {
return ControlPacket::create(type);
return std::unique_ptr<ControlPacket>(new ControlPacket(type));
} else {
// Fail with invalid size
Q_ASSERT(size >= 0);
return ControlPacket::create(type, size);
return std::unique_ptr<ControlPacket>(new ControlPacket(type, size));
}
}

View file

@ -21,8 +21,6 @@
#include <QtCore/QSet>
#include <QtCore/QUuid>
#include "UUID.h"
// If adding a new packet packetType, you can replace one marked usable or add at the end.
// If you want the name of the packet packetType to be available for debugging or logging, update nameForPacketType() as well
// This enum must hold 256 or fewer packet types (so the value is <= 255) since it is statically typed as a uint8_t

View file

@ -62,7 +62,9 @@ void SendQueue::run() {
// We need to make sure this is called on the right thread
if (thread() != QThread::currentThread()) {
QMetaObject::invokeMethod(this, "run", Qt::QueuedConnection);
return;
}
_isRunning = true;
// This will loop and sleep to send packets
@ -161,56 +163,64 @@ void SendQueue::loop() {
}
// If there is no packet to resend, grab the next one in the list
if (!nextPacket) {
if (!nextPacket && _packets.size() > 0) {
QWriteLocker locker(&_packetsLock);
nextPacket.swap(_packets.front());
_packets.pop_front();
}
bool shouldSendSecondOfPair = false;
if (!hasResend) {
// if we're not re-sending a packet then need to check if this should be a packet pair
sequenceNumber = getNextSequenceNumber();
if (nextPacket) {
bool shouldSendSecondOfPair = false;
// the first packet in the pair is every 16 (rightmost 16 bits = 0) packets
if (((uint32_t) sequenceNumber & 0xF) == 0) {
shouldSendSecondOfPair = true;
}
}
// Write packet's sequence number and send it off
nextPacket->writeSequenceNumber(sequenceNumber);
sendPacket(*nextPacket);
// Insert the packet we have just sent in the sent list
QWriteLocker locker(&_sentLock);
_sentPackets[nextPacket->getSequenceNumber()].swap(nextPacket);
Q_ASSERT_X(!nextPacket,
"SendQueue::sendNextPacket()", "Overriden packet in sent list");
if (shouldSendSecondOfPair) {
std::unique_ptr<Packet> pairedPacket;
// we've detected we should send the second packet in a pair, do that now before sleeping
{
QWriteLocker locker(&_packetsLock);
pairedPacket.swap(_packets.front());
_packets.pop_front();
}
if (pairedPacket) {
// write this packet's sequence number and send it off
pairedPacket->writeSequenceNumber(getNextSequenceNumber());
sendPacket(*pairedPacket);
if (!hasResend) {
// if we're not re-sending a packet then need to check if this should be a packet pair
sequenceNumber = getNextSequenceNumber();
// add the paired packet to the sent list
QWriteLocker locker(&_sentLock);
_sentPackets[pairedPacket->getSequenceNumber()].swap(pairedPacket);
Q_ASSERT_X(!pairedPacket,
"SendQueue::sendNextPacket()", "Overriden packet in sent list");
// the first packet in the pair is every 16 (rightmost 16 bits = 0) packets
if (((uint32_t) sequenceNumber & 0xF) == 0) {
shouldSendSecondOfPair = true;
}
}
// Write packet's sequence number and send it off
nextPacket->writeSequenceNumber(sequenceNumber);
sendPacket(*nextPacket);
// Insert the packet we have just sent in the sent list
QWriteLocker locker(&_sentLock);
_sentPackets[nextPacket->getSequenceNumber()].swap(nextPacket);
Q_ASSERT_X(!nextPacket,
"SendQueue::sendNextPacket()", "Overriden packet in sent list");
emit packetSent();
if (shouldSendSecondOfPair) {
std::unique_ptr<Packet> pairedPacket;
// we've detected we should send the second packet in a pair, do that now before sleeping
{
QWriteLocker locker(&_packetsLock);
pairedPacket.swap(_packets.front());
_packets.pop_front();
}
if (pairedPacket) {
// write this packet's sequence number and send it off
pairedPacket->writeSequenceNumber(getNextSequenceNumber());
sendPacket(*pairedPacket);
// add the paired packet to the sent list
QWriteLocker locker(&_sentLock);
_sentPackets[pairedPacket->getSequenceNumber()].swap(pairedPacket);
Q_ASSERT_X(!pairedPacket,
"SendQueue::sendNextPacket()", "Overriden packet in sent list");
emit packetSent();
}
}
}
}
// since we're a while loop, give the thread a chance to process events

View file

@ -61,6 +61,9 @@ public slots:
void ack(SequenceNumber ack);
void nak(SequenceNumber start, SequenceNumber end);
void overrideNAKListFromPacket(ControlPacket& packet);
signals:
void packetSent();
private slots:
void loop();

View file

@ -85,11 +85,7 @@ qint64 Socket::writePacket(const Packet& packet, const HifiSockAddr& sockAddr) {
qint64 Socket::writePacket(std::unique_ptr<Packet> packet, const HifiSockAddr& sockAddr) {
if (packet->isReliable()) {
auto it = _connectionsHash.find(sockAddr);
if (it == _connectionsHash.end()) {
it = _connectionsHash.insert(it, std::make_pair(sockAddr, new Connection(this, sockAddr, _ccFactory->create())));
}
it->second->sendReliablePacket(std::move(packet));
findOrCreateConnection(sockAddr)->sendReliablePacket(move(packet));
return 0;
}
@ -111,6 +107,16 @@ qint64 Socket::writeDatagram(const QByteArray& datagram, const HifiSockAddr& soc
return bytesWritten;
}
Connection* Socket::findOrCreateConnection(const HifiSockAddr& sockAddr) {
auto it = _connectionsHash.find(sockAddr);
if (it == _connectionsHash.end()) {
it = _connectionsHash.insert(it, std::make_pair(sockAddr, new Connection(this, sockAddr, _ccFactory->create())));
}
return it->second;
}
void Socket::readPendingDatagrams() {
while (_udpSocket.hasPendingDatagrams()) {
// setup a HifiSockAddr to read into
@ -144,11 +150,8 @@ void Socket::readPendingDatagrams() {
auto controlPacket = ControlPacket::fromReceivedPacket(std::move(buffer), packetSizeWithHeader, senderSockAddr);
// move this control packet to the matching connection
auto it = _connectionsHash.find(senderSockAddr);
if (it != _connectionsHash.end()) {
it->second->processControl(move(controlPacket));
}
auto connection = findOrCreateConnection(senderSockAddr);
connection->processControl(move(controlPacket));
} else {
// setup a Packet from the data we just read
@ -176,6 +179,13 @@ void Socket::readPendingDatagrams() {
}
}
void Socket::connectToSendSignal(const HifiSockAddr& destinationAddr, QObject* receiver, const char* slot) {
auto it = _connectionsHash.find(destinationAddr);
if (it != _connectionsHash.end()) {
connect(it->second, SIGNAL(packetSent()), receiver, slot);
}
}
void Socket::rateControlSync() {
// enumerate our list of connections and ask each of them to send off periodic ACK packet for rate control

View file

@ -60,6 +60,8 @@ public:
{ _unfilteredHandlers[senderSockAddr] = handler; }
void setCongestionControlFactory(std::unique_ptr<CongestionControlVirtualFactory> ccFactory);
void connectToSendSignal(const HifiSockAddr& destinationAddr, QObject* receiver, const char* slot);
private slots:
void readPendingDatagrams();
@ -67,6 +69,7 @@ private slots:
private:
void setSystemBufferSizes();
Connection* findOrCreateConnection(const HifiSockAddr& sockAddr);
QUdpSocket _udpSocket { this };
PacketFilterOperator _packetFilterOperator;

View file

@ -5,6 +5,8 @@ set_target_properties(mtc PROPERTIES FOLDER "Tools")
add_subdirectory(scribe)
set_target_properties(scribe PROPERTIES FOLDER "Tools")
add_subdirectory(udt-test)
set_target_properties(udt-test PROPERTIES FOLDER "Tools")
add_subdirectory(vhacd-util)
set_target_properties(vhacd-util PROPERTIES FOLDER "Tools")

View file

@ -0,0 +1,6 @@
set(TARGET_NAME udt-test)
setup_hifi_project()
link_hifi_libraries(networking)
copy_dlls_beside_windows_executable()

View file

@ -0,0 +1,200 @@
//
// UDTTest.cpp
// tools/udt-test/src
//
// Created by Stephen Birarda on 2015-07-30.
// 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 "UDTTest.h"
#include <QtCore/QDebug>
#include <udt/Constants.h>
#include <udt/Packet.h>
const QCommandLineOption PORT_OPTION { "p", "listening port for socket (defaults to random)", "port", 0 };
const QCommandLineOption TARGET_OPTION {
"target", "target for sent packets (default is listen only)",
"IP:PORT or HOSTNAME:PORT"
};
const QCommandLineOption PACKET_SIZE {
"packet-size", "size for sent packets in bytes (defaults to 1500)", "bytes",
QString(udt::MAX_PACKET_SIZE_WITH_UDP_HEADER)
};
const QCommandLineOption MIN_PACKET_SIZE {
"min-packet-size", "min size for sent packets in bytes", "min-bytes"
};
const QCommandLineOption MAX_PACKET_SIZE {
"max-packet-size", "max size for sent packets in bytes", "max-bytes"
};
const QCommandLineOption MAX_SEND_BYTES {
"max-send-bytes", "number of bytes to send before stopping (default is infinite)", "max-bytes"
};
const QCommandLineOption MAX_SEND_PACKETS {
"max-send-packets", "number of packets to send before stopping (default is infinite)", "max-packets"
};
const QCommandLineOption UNRELIABLE_PACKETS {
"unreliable", "send unreliable packets (default is reliable)"
};
UDTTest::UDTTest(int& argc, char** argv) :
QCoreApplication(argc, argv)
{
parseArguments();
// randomize the seed for packet size randomization
srand(time(NULL));
_socket.bind(QHostAddress::LocalHost, _argumentParser.value(PORT_OPTION).toUInt());
qDebug() << "Test socket is listening on" << _socket.localPort();
if (_argumentParser.isSet(TARGET_OPTION)) {
// parse the IP and port combination for this target
QString hostnamePortString = _argumentParser.value(TARGET_OPTION);
QHostAddress address { hostnamePortString.left(hostnamePortString.indexOf(':')) };
quint16 port { (quint16) hostnamePortString.mid(hostnamePortString.indexOf(':') + 1).toUInt() };
if (address.isNull() || port == 0) {
qCritical() << "Could not parse an IP address and port combination from" << hostnamePortString << "-" <<
"The parsed IP was" << address.toString() << "and the parsed port was" << port;
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
} else {
_target = HifiSockAddr(address, port);
}
}
if (_argumentParser.isSet(PACKET_SIZE)) {
// parse the desired packet size
_minPacketSize = _maxPacketSize = _argumentParser.value(PACKET_SIZE).toInt();
if (_argumentParser.isSet(MIN_PACKET_SIZE) || _argumentParser.isSet(MAX_PACKET_SIZE)) {
qCritical() << "Cannot set a min packet size or max packet size AND a specific packet size.";
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
}
} else {
bool customMinSize = false;
if (_argumentParser.isSet(MIN_PACKET_SIZE)) {
_minPacketSize = _argumentParser.value(MIN_PACKET_SIZE).toInt();
customMinSize = true;
}
if (_argumentParser.isSet(MAX_PACKET_SIZE)) {
_maxPacketSize = _argumentParser.value(MAX_PACKET_SIZE).toInt();
// if we don't have a min packet size we should make it 1, because we have a max
if (customMinSize) {
_minPacketSize = 1;
}
}
if (_maxPacketSize < _minPacketSize) {
qCritical() << "Cannot set a max packet size that is smaller than the min packet size.";
QMetaObject::invokeMethod(this, "quit", Qt::QueuedConnection);
}
}
if (_argumentParser.isSet(MAX_SEND_BYTES)) {
_maxSendBytes = _argumentParser.value(MAX_SEND_BYTES).toInt();
}
if (_argumentParser.isSet(MAX_SEND_PACKETS)) {
_maxSendPackets = _argumentParser.value(MAX_SEND_PACKETS).toInt();
}
if (_argumentParser.isSet(UNRELIABLE_PACKETS)) {
_sendReliable = false;
}
if (!_target.isNull()) {
sendInitialPackets();
}
}
void UDTTest::parseArguments() {
// use a QCommandLineParser to setup command line arguments and give helpful output
_argumentParser.setApplicationDescription("High Fidelity UDT Protocol Test Client");
_argumentParser.addHelpOption();
const QCommandLineOption helpOption = _argumentParser.addHelpOption();
_argumentParser.addOptions({
PORT_OPTION, TARGET_OPTION, PACKET_SIZE, MIN_PACKET_SIZE, MAX_PACKET_SIZE,
MAX_SEND_BYTES, MAX_SEND_PACKETS, UNRELIABLE_PACKETS
});
if (!_argumentParser.parse(arguments())) {
qCritical() << _argumentParser.errorText();
_argumentParser.showHelp();
Q_UNREACHABLE();
}
if (_argumentParser.isSet(helpOption)) {
_argumentParser.showHelp();
Q_UNREACHABLE();
}
}
void UDTTest::sendInitialPackets() {
static const int NUM_INITIAL_PACKETS = 10;
int numPackets = std::max(NUM_INITIAL_PACKETS, _maxSendPackets);
for (int i = 0; i < numPackets; ++i) {
sendPacket();
}
if (numPackets == NUM_INITIAL_PACKETS) {
// we've put 500 initial packets in the queue, everytime we hear one has gone out we should add a new one
_socket.connectToSendSignal(_target, this, SLOT(refillPacket()));
}
}
void UDTTest::sendPacket() {
qDebug() << "Sending packet" << _totalQueuedPackets + 1;
if (_maxSendPackets != -1 && _totalQueuedPackets > _maxSendPackets) {
// don't send more packets, we've hit max
return;
}
if (_maxSendBytes != -1 && _totalQueuedBytes > _maxSendBytes) {
// don't send more packets, we've hit max
return;
}
// we're good to send a new packet, construct it now
// figure out what size the packet will be
int packetPayloadSize = 0;
if (_minPacketSize == _maxPacketSize) {
// we know what size we want - figure out the payload size
packetPayloadSize = _maxPacketSize - udt::Packet::localHeaderSize(false);
} else {
// pick a random size in our range
int randomPacketSize = rand() % _maxPacketSize + _minPacketSize;
packetPayloadSize = randomPacketSize - udt::Packet::localHeaderSize(false);
}
auto newPacket = udt::Packet::create(packetPayloadSize, _sendReliable);
_totalQueuedBytes += newPacket->getDataSize();
// queue or send this packet by calling write packet on the socket for our target
if (_sendReliable) {
_socket.writePacket(std::move(newPacket), _target);
} else {
_socket.writePacket(*newPacket, _target);
}
++_totalQueuedPackets;
}

View file

@ -0,0 +1,53 @@
//
// UDTTest.h
// tools/udt-test/src
//
// Created by Stephen Birarda on 2015-07-30.
// 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
//
#pragma once
#ifndef hifi_UDTTest_h
#define hifi_UDTTest_h
#include <QtCore/QCoreApplication>
#include <QtCore/QCommandLineParser>
#include <udt/Constants.h>
#include <udt/Socket.h>
class UDTTest : public QCoreApplication {
Q_OBJECT
public:
UDTTest(int& argc, char** argv);
public slots:
void refillPacket() { sendPacket(); } // adds a new packet to the queue when we are told one is sent
private:
void parseArguments();
void sendInitialPackets(); // fills the queue with packets to start
void sendPacket(); // constructs and sends a packet according to the test parameters
QCommandLineParser _argumentParser;
udt::Socket _socket;
HifiSockAddr _target; // the target for sent packets
int _minPacketSize { udt::MAX_PACKET_SIZE };
int _maxPacketSize { udt::MAX_PACKET_SIZE };
int _maxSendBytes { -1 }; // the number of bytes to send to the target before stopping
int _maxSendPackets { -1 }; // the number of packets to send to the target before stopping
bool _sendReliable { true }; // wether packets are sent reliably or unreliably
int _totalQueuedPackets { 0 }; // keeps track of the number of packets we have already queued
int _totalQueuedBytes { 0 }; // keeps track of the number of bytes we have already queued
};
#endif // hifi_UDTTest_h

View file

@ -0,0 +1,19 @@
//
// main.cpp
// tools/udt-test/src
//
// Created by Stephen Birarda on 7/30/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 <QtCore/QCoreApplication>
#include "UDTTest.h"
int main(int argc, char* argv[]) {
UDTTest app(argc, argv);
return app.exec();
}