diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 5750a33faf..35be97d610 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -927,15 +927,22 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif const NodeSet& nodeInterestSet) { auto limitedNodeList = DependencyManager::get(); - auto listPacket = NodeListPacket::make(PacketType::DomainList); + PacketList domainListPackets(PacketType::DomainList); // always send the node their own UUID back - QDataStream broadcastDataStream(&listPacket.payload(), QIODevice::Append); - broadcastDataStream << node->getUUID(); - broadcastDataStream << node->getCanAdjustLocks(); - broadcastDataStream << node->getCanRez(); + QDataStream domainListStream(&domainListPackets); - int numBroadcastPacketLeadBytes = broadcastDataStream.device()->pos(); + const int NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES = NUM_BYTES_RFC4122_UUID + 2; + + // setup the extended header for the domain list packets + // this data is at the beginning of each of the domain list packets + QByteArray extendedHeader(NUM_DOMAIN_LIST_EXTENDED_HEADER_BYTES, 0); + extendedHeader.replace(0, NUM_BYTES_RFC4122_UUID, node->getUUID().toRfc4122()); + + extendedHeader[NUM_BYTES_RFC4122_UUID] = (char) node->getCanAdjustLocks(); + extendedHeader[NUM_BYTES_RFC4122_UUID + 1] = (char) node->getCanRez(); + + domainListPackets.setExtendedHeader(extendedHeader); DomainServerNodeData* nodeData = reinterpret_cast(node->getLinkedData()); @@ -944,44 +951,29 @@ void DomainServer::sendDomainListToNode(const SharedNodePointer& node, const Hif if (nodeInterestSet.size() > 0) { -// DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL; - int dataMTU = MAX_PACKET_SIZE; - + // DTLSServerSession* dtlsSession = _isUsingDTLS ? _dtlsSessions[senderSockAddr] : NULL; if (nodeData->isAuthenticated()) { // if this authenticated node has any interest types, send back those nodes as well limitedNodeList->eachNode([&](const SharedNodePointer& otherNode){ - // reset our nodeByteArray and nodeDataStream - QByteArray nodeByteArray; - QDataStream nodeDataStream(&nodeByteArray, QIODevice::Append); - if (otherNode->getUUID() != node->getUUID() && nodeInterestSet.contains(otherNode->getType())) { + // since we're about to add a node to the packet we start a segment + domainListStream.startSegment(); // don't send avatar nodes to other avatars, that will come from avatar mixer - nodeDataStream << *otherNode.data(); + domainListStream << *otherNode.data(); // pack the secret that these two nodes will use to communicate with each other - nodeDataStream << connectionSecretForNodes(node, otherNode); + domainListStream << connectionSecretForNodes(node, otherNode); - if (broadcastPacket.size() + nodeByteArray.size() > dataMTU) { - // we need to break here and start a new packet - // so send the current one - - limitedNodeList->writeUnverifiedDatagram(broadcastPacket, node, senderSockAddr); - - // reset the broadcastPacket structure - broadcastPacket.resize(numBroadcastPacketLeadBytes); - broadcastDataStream.device()->seek(numBroadcastPacketLeadBytes); - } - - // append the nodeByteArray to the current state of broadcastDataStream - broadcastPacket.append(nodeByteArray); + // we've added the node we wanted so end the segment now + domainListStream.endSegment(); } }); } } - // always write the last broadcastPacket - limitedNodeList->writeUnverifiedDatagram(broadcastPacket, node); + // write the PacketList to this node + limitedNodeList->sendPacketList(domainListPackets, node); } QUuid DomainServer::connectionSecretForNodes(const SharedNodePointer& nodeA, const SharedNodePointer& nodeB) { diff --git a/libraries/networking/src/PacketList.cpp b/libraries/networking/src/PacketList.cpp new file mode 100644 index 0000000000..bb61dd39d1 --- /dev/null +++ b/libraries/networking/src/PacketList.cpp @@ -0,0 +1,111 @@ +// +// PacketList.cpp +// libraries/networking/src +// +// Created by Stephen Birarda on 07/06/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 "PacketList.h" + +PacketList::PacketList(PacketType::Value packetType, bool isOrdered) : + _packetType(packetType), + _isOrdered(isOrdered) +{ + +} + +void PacketList::createPacketWithExtendedHeader() { + // use the static create method to create a new packet + _currentPacket = T::create(_packetType); + + // add the extended header to the front of the packet + if (_currentPacket.write(_extendedHeader) == -1) { + qDebug() << "Could not write extendedHeader in PacketList::createPacketWithExtendedHeader" + << "- make sure that _extendedHeader is not larger than the payload capacity."; + } +} + +qint64 writeData(const char* data, qint64 maxSize) { + if (!_currentPacket) { + // we don't have a current packet, time to set one up + createPacketWithExtendedHeader(); + } + + // check if this block of data can fit into the currentPacket + if (maxSize <= _currentPacket.size()) { + // it fits, just write it to the current packet + _currentPacket.write(data, maxSize); + + // return the number of bytes written + return maxSize; + } else { + // it does not fit - this may need to be in the next packet + + if (!_isOrdered) { + auto newPacket = T::create(_packetType); + PacketPayload& newPayload = newPacket.getPayload(); + + if (_segmentStartIndex >= 0) { + // We in the process of writing a segment for an unordered PacketList. + // We need to try and pull the first part of the segment out to our new packet + + PacketPayload& currentPayload = _currentPacket->getPayload(); + + // check now to see if this is an unsupported write + int numBytesToEnd = currentPayload.size() - _segmentStartIndex; + + if ((newPayload.size() - numBytesToEnd) < maxSize) { + // this is an unsupported case - the segment is bigger than the size of an individual packet + // but the PacketList is not going to be sent ordered + qDebug() << "Error in PacketList::writeData - attempted to write a segment to an unordered packet that is" + << "larger than the payload size."; + return -1; + } + + // copy from currentPacket where the segment started to the beginning of the newPacket + newPayload.write(currentPacket.constData() + _segmentStartIndex, numBytesToEnd); + + // the current segment now starts at the beginning of the new packet + _segmentStartIndex = 0; + + // shrink the current payload to the actual size of the packet + currentPayload.setSizeUsed(_segmentStartIndex); + } + + // move the current packet to our list of packets + _packets.insert(std::move(_currentPacket)); + + // write the data to the newPacket + newPayload.write(data, maxSize); + + // set our current packet to the new packet + _currentPacket = newPacket; + + // return the number of bytes written to the new packet + return maxSize; + } else { + // we're an ordered PacketList - let's fit what we can into the current packet and then put the leftover + // into a new packet + PacketPayload& currentPayload = _currentPacket.getPayload(); + + int numBytesToEnd = _currentPayload.size() - _currentPayload.pos(); + _currentPacket.write(data, numBytesToEnd); + + // move the current packet to our list of packets + _packets.insert(std::move(_currentPacket)); + + // recursively call our writeData method for the remaining data to write to a new packet + return numBytesToEnd + writeData(data + numBytesToEnd, maxSize - numBytesToEnd); + } + } +} + +void PacketList::closeCurrentPacket() { + // move the current packet to our list of packets + _packets.insert(std::move(_currentPacket)); +} + diff --git a/libraries/networking/src/PacketList.h b/libraries/networking/src/PacketList.h new file mode 100644 index 0000000000..1b0905a1c8 --- /dev/null +++ b/libraries/networking/src/PacketList.h @@ -0,0 +1,46 @@ +// +// PacketList.h +// libraries/networking/src +// +// Created by Stephen Birarda on 07/06/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_PacketList_h +#define hifi_PacketList_h + +#pragma once + +template class PacketList : public QIODevice { +public: + PacketList(PacketType::Value packetType, bool isOrdered = false); + + virtual bool isSequential() const { return true; } + + void startSegment() { _segmentStartIndex = currentPacket->payload().pos(); } + void endSegment() { _segmentStartIndex = -1; } + + void closeCurrentPacket(); + + void setExtendedHeader(const QByteArray& extendedHeader) { _extendedHeader = extendedHeader; } +protected: + qint64 writeData(const char* data, qint64 maxSize); + qint64 readData(const char* data, qint64 maxSize) { return 0 }; +private: + void createPacketWithExtendedHeader(); + + PacketType::Value _packetType; + bool isOrdered; + + std::unique_ptr _currentPacket; + std::list> _packets; + + int _segmentStartIndex = -1; + + QByteArray _extendedHeader = extendedHeader; +} + +#endif // hifi_PacketList_h diff --git a/libraries/networking/src/PacketPayload.cpp b/libraries/networking/src/PacketPayload.cpp index fa7d0bd694..5f0bc9b557 100644 --- a/libraries/networking/src/PacketPayload.cpp +++ b/libraries/networking/src/PacketPayload.cpp @@ -11,9 +11,9 @@ #include "PacketPayload.h" -PacketPayload::PacketPayload(char* data, qint64 size) : +PacketPayload::PacketPayload(char* data, qint64 capacity) : _data(data) - _size(size) + _capacity(capacity) { } @@ -25,7 +25,7 @@ qint64 PacketPayload::writeData(const char* data, qint64 maxSize) { qint64 currentPos = pos(); // make sure we have the space required to write this block - qint64 bytesAvailable = _size - currentPos; + qint64 bytesAvailable = _capacity - currentPos; if (bytesAvailable < srcBytes) { // good to go - write the data @@ -35,7 +35,7 @@ qint64 PacketPayload::writeData(const char* data, qint64 maxSize) { seek(currentPos + srcBytes); // keep track of _maxWritten so we can just write the actual data when packet is about to be sent - _maxWritten = std::max(pos() + 1, _maxWritten); + _size = std::max(pos() + 1, _maxWritten); // return the number of bytes written return srcBytes; diff --git a/libraries/networking/src/PacketPayload.h b/libraries/networking/src/PacketPayload.h index dceb1f23ce..a9e3811767 100644 --- a/libraries/networking/src/PacketPayload.h +++ b/libraries/networking/src/PacketPayload.h @@ -18,19 +18,23 @@ class PacketPayload : public QIODevice { public: - PacketPayload(char* data, qint64 size); + PacketPayload(char* data, qint64 capacity); - virtual qint64 size() const { return _size; } + qint64 getSizeUsed() const { return _sizeUsed; } + void setSizeUsed(qint64 sizeUsed) { _sizeUsed = sizeUsed; } + + virtual qint64 size() const { return _capacity; } virtual bool isSequential() const { return false; } + const char* constData() const { return _data; } protected: - virtual qint64 writeData(const char* data, qint64 maxSize); - virtual qint64 readData(const char* data, qint64 maxSize); + qint64 writeData(const char* data, qint64 maxSize); + qint64 readData(char* data, qint64 maxSize); private: char* _data; - qint64 _size = 0; - qint64 _maxWritten = 0; + qint64 _sizeUsed = 0; + qint64 _capacity = 0; }; #endif // hifi_PacketPayload_h