use microseconds for intervals, setup timeout NAKs

This commit is contained in:
Stephen Birarda 2015-07-28 16:43:21 -07:00
parent 09a497a59b
commit 74b0fa7e87
3 changed files with 60 additions and 11 deletions

View file

@ -11,6 +11,8 @@
#include "Connection.h"
#include <NumericalConstants.h>
#include "../HifiSockAddr.h"
#include "ControlPacket.h"
#include "Packet.h"
@ -37,6 +39,28 @@ void Connection::sendReliablePacket(unique_ptr<Packet> packet) {
_sendQueue->queuePacket(move(packet));
}
void Connection::sync() {
// we send out a periodic ACK every rate control interval
sendACK();
// check if we need to re-transmit a loss list
// we do this if it has been longer than the current nakInterval since we last sent
auto now = high_resolution_clock::now();
if (duration_cast<milliseconds>(now - _lastNAKTime).count() >= _nakInterval) {
// construct a NAK packet that will hold all of the lost sequence numbers
auto lossListPacket = ControlPacket::create(ControlPacket::NAK, _lossList.getLength() * sizeof(SequenceNumber));
// have our SendQueue send off this control packet
_sendQueue->sendPacket(*lossListPacket);
_lastNAKTime = high_resolution_clock::now();
}
}
void Connection::sendACK(bool wasCausedBySyncTimeout) {
static const int ACK_PACKET_PAYLOAD_BYTES = sizeof(_lastSentACK) + sizeof(_currentACKSubSequenceNumber)
+ sizeof(_rtt) + sizeof(int32_t) + sizeof(int32_t);
@ -65,10 +89,10 @@ void Connection::sendACK(bool wasCausedBySyncTimeout) {
} else if (nextACKNumber == _lastSentACK) {
// We already sent this ACK, but check if we should re-send it.
// We will re-send if it has been more than RTT + (4 * RTT variance) since the last ACK
milliseconds sinceLastACK = duration_cast<milliseconds>(currentTime - lastACKSendTime);
// We will re-send if it has been more than the estimated timeout since the last ACK
microseconds sinceLastACK = duration_cast<microseconds>(currentTime - lastACKSendTime);
if (sinceLastACK.count() < (_rtt + (4 * _rttVariance))) {
if (sinceLastACK.count() < estimatedTimeout()) {
return;
}
}
@ -162,6 +186,23 @@ void Connection::processReceivedSequenceNumber(SequenceNumber seq) {
// have the send queue send off our packet immediately
_sendQueue->sendPacket(*lossReport);
// record our last NAK time
_lastNAKTime = high_resolution_clock::now();
// figure out when we should send the next loss report, if we haven't heard anything back
_nakInterval = (_rtt + 4 * _rttVariance);
int receivedPacketsPerSecond = _receiveWindow.getPacketReceiveSpeed();
if (receivedPacketsPerSecond > 0) {
// the NAK interval is at least the _minNAKInterval
// but might be the time required for all lost packets to be retransmitted
_nakInterval = std::max((int) (_lossList.getLength() * (USECS_PER_SECOND / receivedPacketsPerSecond)),
_minNAKInterval);
} else {
// the NAK interval is at least the _minNAKInterval but might be the estimated timeout
_nakInterval = std::max(estimatedTimeout(), _minNAKInterval);
}
}
if (seq > _lastReceivedSequenceNumber) {
@ -202,7 +243,7 @@ void Connection::processACK(std::unique_ptr<ControlPacket> controlPacket) {
auto currentTime = high_resolution_clock::now();
static high_resolution_clock::time_point lastACK2SendTime;
milliseconds sinceLastACK2 = duration_cast<milliseconds>(currentTime - lastACK2SendTime);
microseconds sinceLastACK2 = duration_cast<microseconds>(currentTime - lastACK2SendTime);
if (sinceLastACK2.count() > _synInterval || currentACKSubSequenceNumber == _lastSentACK2) {
// setup a static ACK2 packet we will re-use
@ -304,7 +345,7 @@ void Connection::processACK2(std::unique_ptr<ControlPacket> controlPacket) {
// calculate the RTT (time now - time ACK sent)
auto now = high_resolution_clock::now();
int rtt = duration_cast<milliseconds>(now - pair.second).count();
int rtt = duration_cast<microseconds>(now - pair.second).count();
updateRTT(rtt);

View file

@ -35,9 +35,8 @@ public:
Connection(Socket* parentSocket, HifiSockAddr destination);
void sendReliablePacket(std::unique_ptr<Packet> packet);
void sendACK(bool wasCausedBySyncTimeout = true);
void sendLightACK() const;
void sync(); // rate control method, fired by Socket for all connections on SYN interval
SequenceNumber nextACK() const;
@ -47,6 +46,9 @@ public:
void processControl(std::unique_ptr<ControlPacket> controlPacket);
private:
void sendACK(bool wasCausedBySyncTimeout = true);
void sendLightACK() const;
void processACK(std::unique_ptr<ControlPacket> controlPacket);
void processLightACK(std::unique_ptr<ControlPacket> controlPacket);
void processACK2(std::unique_ptr<ControlPacket> controlPacket);
@ -54,7 +56,13 @@ private:
void updateRTT(int rtt);
int _synInterval; // Periodical Rate Control Interval, defaults to 10ms
int estimatedTimeout() const { return _rtt + _rttVariance * 4; }
int _synInterval; // Periodical Rate Control Interval, in microseconds, defaults to 10ms
int _nakInterval; // NAK timeout interval, in microseconds
int _minNAKInterval { 100000 }; // NAK timeout interval lower bound, default of 100ms
std::chrono::high_resolution_clock::time_point _lastNAKTime;
LossList _lossList; // List of all missing packets
SequenceNumber _lastReceivedSequenceNumber { SequenceNumber::MAX }; // The largest sequence number received from the peer
@ -68,7 +76,7 @@ private:
int _totalReceivedACKs { 0 };
int32_t _rtt; // RTT, in milliseconds
int32_t _rtt; // RTT, in microseconds
int32_t _rttVariance; // RTT variance
int _flowWindowSize; // Flow control window size

View file

@ -145,7 +145,7 @@ void Socket::rateControlSync() {
// enumerate our list of connections and ask each of them to send off periodic ACK packet for rate control
for (auto& connection : _connectionsHash) {
connection.second->sendACK();
connection.second->sync();
}
if (_synTimer.interval() != _synInterval) {