face: Acks acknowledge TxSequence instead of Sequence
refs #3931 Change-Id: I83919fe815c2a43e47eb09d754f77166c051d013
This commit is contained in:
1 parent
a5a3a87ae0
commit
7b0071e6a9
5 files changed
+740
-561
No files matched your search
@@ -1,5 +1,5 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2014-2017, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
@@ -134,7 +134,14 @@ void
|
||||
GenericLinkService::sendNetPacket(lp::Packet&& pkt)
|
||||
{
|
||||
std::vector<lp::Packet> frags;
|
||||
const ssize_t mtu = this->getTransport()->getMtu();
|
||||
ssize_t mtu = this->getTransport()->getMtu();
|
||||
|
||||
// Make space for feature fields in fragments
|
||||
if (m_options.reliabilityOptions.isEnabled && mtu != MTU_UNLIMITED) {
|
||||
mtu -= LpReliability::RESERVED_HEADER_SPACE;
|
||||
BOOST_ASSERT(mtu > 0);
|
||||
}
|
||||
|
||||
if (m_options.allowFragmentation && mtu != MTU_UNLIMITED) {
|
||||
bool isOk = false;
|
||||
std::tie(isOk, frags) = m_fragmenter.fragmentPacket(pkt, mtu);
|
||||
@@ -155,16 +162,14 @@ GenericLinkService::sendNetPacket(lp::Packet&& pkt)
|
||||
BOOST_ASSERT(!frags.front().has<lp::FragCountField>());
|
||||
}
|
||||
|
||||
// Only assign sequences to fragments if reliability enabled and packet contains a fragment,
|
||||
// or there is more than 1 fragment
|
||||
if ((m_options.reliabilityOptions.isEnabled && frags.front().has<lp::FragmentField>()) ||
|
||||
frags.size() > 1) {
|
||||
// Only assign sequences to fragments if packet contains more than 1 fragment
|
||||
if (frags.size() > 1) {
|
||||
// Assign sequences to all fragments
|
||||
this->assignSequences(frags);
|
||||
}
|
||||
|
||||
if (m_options.reliabilityOptions.isEnabled && frags.front().has<lp::FragmentField>()) {
|
||||
m_reliability.observeOutgoing(frags);
|
||||
m_reliability.handleOutgoing(frags);
|
||||
}
|
||||
|
||||
for (lp::Packet& frag : frags) {
|
||||
|
||||
+147
-107
@@ -1,5 +1,5 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2014-2017, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
@@ -34,6 +34,7 @@ LpReliability::LpReliability(const LpReliability::Options& options, GenericLinkS
|
||||
: m_options(options)
|
||||
, m_linkService(linkService)
|
||||
, m_firstUnackedFrag(m_unackedFrags.begin())
|
||||
, m_lastTxSeqNo(-1) // set to "-1" to start TxSequence numbers at 0
|
||||
, m_isIdleAckTimerRunning(false)
|
||||
{
|
||||
BOOST_ASSERT(m_linkService != nullptr);
|
||||
@@ -60,27 +61,36 @@ LpReliability::getLinkService() const
|
||||
}
|
||||
|
||||
void
|
||||
LpReliability::observeOutgoing(const std::vector<lp::Packet>& frags)
|
||||
LpReliability::handleOutgoing(std::vector<lp::Packet>& frags)
|
||||
{
|
||||
BOOST_ASSERT(m_options.isEnabled);
|
||||
|
||||
// The sequence number of the first fragment is used to identify the NetPkt.
|
||||
lp::Sequence netPktIdentifier = frags.at(0).get<lp::SequenceField>();
|
||||
auto& netPkt = m_netPkts.emplace(netPktIdentifier, NetPkt{}).first->second;
|
||||
auto unackedFragsIt = m_unackedFrags.begin();
|
||||
auto netPktUnackedFragsIt = netPkt.unackedFrags.begin();
|
||||
auto sendTime = time::steady_clock::now();
|
||||
|
||||
auto netPkt = make_shared<NetPkt>();
|
||||
netPkt->unackedFrags.reserve(frags.size());
|
||||
|
||||
for (lp::Packet& frag : frags) {
|
||||
// Assign TxSequence number
|
||||
lp::Sequence txSeq = assignTxSequence(frag);
|
||||
|
||||
for (const lp::Packet& frag : frags) {
|
||||
// Store LpPacket for future retransmissions
|
||||
lp::Sequence seq = frag.get<lp::SequenceField>();
|
||||
unackedFragsIt = m_unackedFrags.emplace_hint(unackedFragsIt, seq, frag);
|
||||
unackedFragsIt = m_unackedFrags.emplace_hint(unackedFragsIt,
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(txSeq),
|
||||
std::forward_as_tuple(frag));
|
||||
unackedFragsIt->second.sendTime = sendTime;
|
||||
unackedFragsIt->second.rtoTimer =
|
||||
scheduler::schedule(m_rto.computeRto(), bind(&LpReliability::onLpPacketLost, this, seq));
|
||||
unackedFragsIt->second.sendTime = time::steady_clock::now();
|
||||
netPktUnackedFragsIt = netPkt.unackedFrags.insert(netPktUnackedFragsIt, seq);
|
||||
scheduler::schedule(m_rto.computeRto(), bind(&LpReliability::onLpPacketLost, this, unackedFragsIt));
|
||||
unackedFragsIt->second.netPkt = netPkt;
|
||||
|
||||
if (m_unackedFrags.size() == 1) {
|
||||
m_firstUnackedFrag = unackedFragsIt;
|
||||
m_firstUnackedFrag = m_unackedFrags.begin();
|
||||
}
|
||||
|
||||
// Add to associated NetPkt
|
||||
netPkt->unackedFrags.push_back(unackedFragsIt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,39 +103,39 @@ LpReliability::processIncomingPacket(const lp::Packet& pkt)
|
||||
|
||||
// Extract and parse Acks
|
||||
for (lp::Sequence ackSeq : pkt.list<lp::AckField>()) {
|
||||
auto txFrag = m_unackedFrags.find(ackSeq);
|
||||
if (txFrag == m_unackedFrags.end()) {
|
||||
// Ignore an Ack for an unknown sequence number
|
||||
auto fragIt = m_unackedFrags.find(ackSeq);
|
||||
if (fragIt == m_unackedFrags.end()) {
|
||||
// Ignore an Ack for an unknown TxSequence number
|
||||
continue;
|
||||
}
|
||||
auto& frag = fragIt->second;
|
||||
|
||||
// Cancel the RTO timer for the acknowledged fragment
|
||||
txFrag->second.rtoTimer.cancel();
|
||||
frag.rtoTimer.cancel();
|
||||
|
||||
if (txFrag->second.retxCount == 0) {
|
||||
if (frag.retxCount == 0) {
|
||||
// This sequence had no retransmissions, so use it to calculate the RTO
|
||||
m_rto.addMeasurement(time::duration_cast<RttEstimator::Duration>(now - txFrag->second.sendTime));
|
||||
m_rto.addMeasurement(time::duration_cast<RttEstimator::Duration>(now - frag.sendTime));
|
||||
}
|
||||
|
||||
// Look for Acks with sequence numbers < ackSeq (allowing for wraparound) and consider them lost
|
||||
// if a configurable number of Acks containing greater sequence numbers have been received.
|
||||
auto lostLpPackets = findLostLpPackets(ackSeq);
|
||||
// Look for frags with TxSequence numbers < ackSeq (allowing for wraparound) and consider them
|
||||
// lost if a configurable number of Acks containing greater TxSequence numbers have been
|
||||
// received.
|
||||
auto lostLpPackets = findLostLpPackets(fragIt);
|
||||
|
||||
// Remove the fragment from the map of unacknowledged sequences and from its associated network
|
||||
// packet (removing the network packet if it has been received in whole by remote host).
|
||||
// Potentially increment the start of the window.
|
||||
onLpPacketAcknowledged(txFrag, getNetPktByFrag(ackSeq));
|
||||
// Remove the fragment from the map of unacknowledged fragments and from its associated network
|
||||
// packet. Potentially increment the start of the window.
|
||||
onLpPacketAcknowledged(fragIt);
|
||||
|
||||
// Resend or fail fragments considered lost. This must be done separately from the above
|
||||
// enhanced for loop because onLpPacketLost may delete the fragment from m_unackedFrags.
|
||||
for (const lp::Sequence& seq : lostLpPackets) {
|
||||
this->onLpPacketLost(seq);
|
||||
// Resend or fail fragments considered lost. Potentially increment the start of the window.
|
||||
for (UnackedFrags::iterator txSeqIt : lostLpPackets) {
|
||||
this->onLpPacketLost(txSeqIt);
|
||||
}
|
||||
}
|
||||
|
||||
// If has Fragment field, extract Sequence and add to AckQueue
|
||||
if (pkt.has<lp::FragmentField>() && pkt.has<lp::SequenceField>()) {
|
||||
m_ackQueue.push(pkt.get<lp::SequenceField>());
|
||||
// If packet has Fragment and TxSequence fields, extract TxSequence and add to AckQueue
|
||||
if (pkt.has<lp::FragmentField>() && pkt.has<lp::TxSequenceField>()) {
|
||||
m_ackQueue.push(pkt.get<lp::TxSequenceField>());
|
||||
if (!m_isIdleAckTimerRunning) {
|
||||
this->startIdleAckTimer();
|
||||
}
|
||||
@@ -136,22 +146,44 @@ void
|
||||
LpReliability::piggyback(lp::Packet& pkt, ssize_t mtu)
|
||||
{
|
||||
BOOST_ASSERT(m_options.isEnabled);
|
||||
BOOST_ASSERT(pkt.wireEncode().type() == lp::tlv::LpPacket);
|
||||
|
||||
int maxAcks = std::numeric_limits<int>::max();
|
||||
if (mtu > 0) {
|
||||
// Ack Type (3 octets) + Ack Length (1 octet) + sizeof(lp::Sequence)
|
||||
size_t ackSize = 3 + 1 + sizeof(lp::Sequence);
|
||||
maxAcks = (mtu - pkt.wireEncode().size()) / ackSize;
|
||||
}
|
||||
// up to 2 extra octets reserved for potential TLV-LENGTH size increases
|
||||
ssize_t pktSize = pkt.wireEncode().size();
|
||||
ssize_t reservedSpace = tlv::sizeOfVarNumber(ndn::MAX_NDN_PACKET_SIZE) -
|
||||
tlv::sizeOfVarNumber(pktSize);
|
||||
ssize_t remainingSpace = (mtu == MTU_UNLIMITED ? ndn::MAX_NDN_PACKET_SIZE : mtu) - reservedSpace;
|
||||
remainingSpace -= pktSize;
|
||||
|
||||
ssize_t nAcksInPkt = 0;
|
||||
while (!m_ackQueue.empty() && nAcksInPkt < maxAcks) {
|
||||
pkt.add<lp::AckField>(m_ackQueue.front());
|
||||
while (!m_ackQueue.empty()) {
|
||||
lp::Sequence ackSeq = m_ackQueue.front();
|
||||
// Ack Size = Ack Type (3 octets) + Ack Length (1 octet) + Value (1, 2, 4, or 8 octets)
|
||||
ssize_t ackSize = tlv::sizeOfVarNumber(lp::tlv::Ack) +
|
||||
tlv::sizeOfVarNumber(
|
||||
tlv::sizeOfNonNegativeInteger(std::numeric_limits<lp::Sequence>::max())) +
|
||||
tlv::sizeOfNonNegativeInteger(ackSeq);
|
||||
|
||||
if (ackSize > remainingSpace) {
|
||||
break;
|
||||
}
|
||||
|
||||
pkt.add<lp::AckField>(ackSeq);
|
||||
m_ackQueue.pop();
|
||||
nAcksInPkt++;
|
||||
remainingSpace -= ackSize;
|
||||
}
|
||||
}
|
||||
|
||||
lp::Sequence
|
||||
LpReliability::assignTxSequence(lp::Packet& frag)
|
||||
{
|
||||
lp::Sequence txSeq = ++m_lastTxSeqNo;
|
||||
frag.set<lp::TxSequenceField>(txSeq);
|
||||
if (m_unackedFrags.size() > 0 && m_lastTxSeqNo == m_firstUnackedFrag->first) {
|
||||
BOOST_THROW_EXCEPTION(std::length_error("TxSequence range exceeded"));
|
||||
}
|
||||
return m_lastTxSeqNo;
|
||||
}
|
||||
|
||||
void
|
||||
LpReliability::startIdleAckTimer()
|
||||
{
|
||||
@@ -174,27 +206,25 @@ LpReliability::stopIdleAckTimer()
|
||||
m_isIdleAckTimerRunning = false;
|
||||
}
|
||||
|
||||
std::vector<lp::Sequence>
|
||||
LpReliability::findLostLpPackets(lp::Sequence ackSeq)
|
||||
std::vector<LpReliability::UnackedFrags::iterator>
|
||||
LpReliability::findLostLpPackets(LpReliability::UnackedFrags::iterator ackIt)
|
||||
{
|
||||
std::vector<lp::Sequence> lostLpPackets;
|
||||
std::vector<UnackedFrags::iterator> lostLpPackets;
|
||||
|
||||
for (auto it = m_firstUnackedFrag; ; ++it) {
|
||||
if (it == m_unackedFrags.end()) {
|
||||
it = m_unackedFrags.begin();
|
||||
}
|
||||
|
||||
if (it->first == ackSeq) {
|
||||
if (it->first == ackIt->first) {
|
||||
break;
|
||||
}
|
||||
|
||||
auto& unackedFrag = it->second;
|
||||
|
||||
unackedFrag.nGreaterSeqAcks++;
|
||||
|
||||
if (unackedFrag.nGreaterSeqAcks >= m_options.seqNumLossThreshold && !unackedFrag.wasTimedOutBySeq) {
|
||||
unackedFrag.wasTimedOutBySeq = true;
|
||||
lostLpPackets.push_back(it->first);
|
||||
if (unackedFrag.nGreaterSeqAcks >= m_options.seqNumLossThreshold) {
|
||||
lostLpPackets.push_back(it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,85 +232,101 @@ LpReliability::findLostLpPackets(lp::Sequence ackSeq)
|
||||
}
|
||||
|
||||
void
|
||||
LpReliability::onLpPacketLost(lp::Sequence seq)
|
||||
LpReliability::onLpPacketLost(UnackedFrags::iterator txSeqIt)
|
||||
{
|
||||
auto& txFrag = m_unackedFrags.at(seq);
|
||||
auto netPktIt = getNetPktByFrag(seq);
|
||||
BOOST_ASSERT(m_unackedFrags.count(txSeqIt->first) > 0);
|
||||
|
||||
auto& txFrag = txSeqIt->second;
|
||||
txFrag.rtoTimer.cancel();
|
||||
auto netPkt = txFrag.netPkt;
|
||||
|
||||
// Check if maximum number of retransmissions exceeded
|
||||
if (txFrag.retxCount >= m_options.maxRetx) {
|
||||
// Delete all LpPackets of NetPkt from TransmitCache
|
||||
lp::Sequence firstSeq = *(netPktIt->second.unackedFrags.begin());
|
||||
lp::Sequence lastSeq = *(std::prev(netPktIt->second.unackedFrags.end()));
|
||||
if (lastSeq >= firstSeq) { // Normal case: no wraparound
|
||||
m_unackedFrags.erase(m_unackedFrags.find(firstSeq), std::next(m_unackedFrags.find(lastSeq)));
|
||||
// Delete all LpPackets of NetPkt from m_unackedFrags (except this one)
|
||||
for (size_t i = 0; i < netPkt->unackedFrags.size(); i++) {
|
||||
if (netPkt->unackedFrags[i] != txSeqIt) {
|
||||
deleteUnackedFrag(netPkt->unackedFrags[i]);
|
||||
}
|
||||
}
|
||||
else { // sequence number wraparound
|
||||
m_unackedFrags.erase(m_unackedFrags.find(firstSeq), m_unackedFrags.end());
|
||||
m_unackedFrags.erase(m_unackedFrags.begin(), std::next(m_unackedFrags.find(lastSeq)));
|
||||
}
|
||||
|
||||
m_netPkts.erase(netPktIt);
|
||||
|
||||
++m_linkService->nRetxExhausted;
|
||||
deleteUnackedFrag(txSeqIt);
|
||||
}
|
||||
else {
|
||||
txFrag.retxCount++;
|
||||
// Assign new TxSequence
|
||||
lp::Sequence newTxSeq = assignTxSequence(txFrag.pkt);
|
||||
|
||||
// Start RTO timer for this sequence
|
||||
txFrag.rtoTimer = scheduler::schedule(m_rto.computeRto(),
|
||||
bind(&LpReliability::onLpPacketLost, this, seq));
|
||||
// Move fragment to new TxSequence mapping
|
||||
auto newTxFragIt = m_unackedFrags.emplace_hint(
|
||||
m_firstUnackedFrag != m_unackedFrags.end() && m_firstUnackedFrag->first > newTxSeq
|
||||
? m_firstUnackedFrag
|
||||
: m_unackedFrags.end(),
|
||||
std::piecewise_construct,
|
||||
std::forward_as_tuple(newTxSeq),
|
||||
std::forward_as_tuple(txFrag.pkt));
|
||||
auto& newTxFrag = newTxFragIt->second;
|
||||
newTxFrag.retxCount = txFrag.retxCount + 1;
|
||||
newTxFrag.netPkt = netPkt;
|
||||
|
||||
// Update associated NetPkt
|
||||
auto fragInNetPkt = std::find(netPkt->unackedFrags.begin(), netPkt->unackedFrags.end(), txSeqIt);
|
||||
BOOST_ASSERT(fragInNetPkt != netPkt->unackedFrags.end());
|
||||
*fragInNetPkt = newTxFragIt;
|
||||
|
||||
deleteUnackedFrag(txSeqIt);
|
||||
|
||||
// Retransmit fragment
|
||||
m_linkService->sendLpPacket(lp::Packet(txFrag.pkt));
|
||||
m_linkService->sendLpPacket(lp::Packet(newTxFrag.pkt));
|
||||
|
||||
// Start RTO timer for this sequence
|
||||
newTxFrag.rtoTimer = scheduler::schedule(m_rto.computeRto(),
|
||||
bind(&LpReliability::onLpPacketLost, this, newTxFragIt));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LpReliability::onLpPacketAcknowledged(std::map<lp::Sequence, LpReliability::UnackedFrag>::iterator fragIt,
|
||||
std::map<lp::Sequence, LpReliability::NetPkt>::iterator netPktIt)
|
||||
LpReliability::onLpPacketAcknowledged(UnackedFrags::iterator fragIt)
|
||||
{
|
||||
lp::Sequence seq = fragIt->first;
|
||||
// We need to store the sequence of the window begin in case we are erasing it from m_unackedFrags
|
||||
lp::Sequence firstUnackedSeq = m_firstUnackedFrag->first;
|
||||
auto nextSeqIt = m_unackedFrags.erase(fragIt);
|
||||
netPktIt->second.unackedFrags.erase(seq);
|
||||
auto netPkt = fragIt->second.netPkt;
|
||||
|
||||
if (!m_unackedFrags.empty() && firstUnackedSeq == seq) {
|
||||
// If "first" fragment in send window (allowing for wraparound), increment window begin
|
||||
if (nextSeqIt == m_unackedFrags.end()) {
|
||||
m_firstUnackedFrag = m_unackedFrags.begin();
|
||||
}
|
||||
else {
|
||||
m_firstUnackedFrag = nextSeqIt;
|
||||
}
|
||||
}
|
||||
// Remove from NetPkt unacked fragment list
|
||||
auto fragInNetPkt = std::find(netPkt->unackedFrags.begin(), netPkt->unackedFrags.end(), fragIt);
|
||||
BOOST_ASSERT(fragInNetPkt != netPkt->unackedFrags.end());
|
||||
*fragInNetPkt = netPkt->unackedFrags.back();
|
||||
netPkt->unackedFrags.pop_back();
|
||||
|
||||
// Check if network-layer packet completely received. If so, delete network packet mapping
|
||||
// and increment counter
|
||||
if (netPktIt->second.unackedFrags.empty()) {
|
||||
if (netPktIt->second.didRetx) {
|
||||
// Check if network-layer packet completely received. If so, increment counters
|
||||
if (netPkt->unackedFrags.empty()) {
|
||||
if (netPkt->didRetx) {
|
||||
++m_linkService->nRetransmitted;
|
||||
}
|
||||
else {
|
||||
++m_linkService->nAcknowledged;
|
||||
}
|
||||
m_netPkts.erase(netPktIt);
|
||||
}
|
||||
|
||||
deleteUnackedFrag(fragIt);
|
||||
}
|
||||
|
||||
std::map<lp::Sequence, LpReliability::NetPkt>::iterator
|
||||
LpReliability::getNetPktByFrag(lp::Sequence seq)
|
||||
void
|
||||
LpReliability::deleteUnackedFrag(UnackedFrags::iterator fragIt)
|
||||
{
|
||||
BOOST_ASSERT(!m_netPkts.empty());
|
||||
auto it = m_netPkts.lower_bound(seq);
|
||||
if (it == m_netPkts.end()) {
|
||||
// This can happen because of sequence number wraparound in the middle of a network packet.
|
||||
// In this case, the network packet will be at the end of m_netPkts and we will need to
|
||||
// decrement the iterator to m_netPkts.end() to the one before it.
|
||||
--it;
|
||||
lp::Sequence firstUnackedTxSeq = m_firstUnackedFrag->first;
|
||||
lp::Sequence currentTxSeq = fragIt->first;
|
||||
auto nextFragIt = m_unackedFrags.erase(fragIt);
|
||||
|
||||
if (!m_unackedFrags.empty() && firstUnackedTxSeq == currentTxSeq) {
|
||||
// If "first" fragment in send window (allowing for wraparound), increment window begin
|
||||
if (nextFragIt == m_unackedFrags.end()) {
|
||||
m_firstUnackedFrag = m_unackedFrags.begin();
|
||||
}
|
||||
else {
|
||||
m_firstUnackedFrag = nextFragIt;
|
||||
}
|
||||
}
|
||||
else if (m_unackedFrags.empty()) {
|
||||
m_firstUnackedFrag = m_unackedFrags.end();
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
LpReliability::UnackedFrag::UnackedFrag(lp::Packet pkt)
|
||||
@@ -288,12 +334,6 @@ LpReliability::UnackedFrag::UnackedFrag(lp::Packet pkt)
|
||||
, sendTime(time::steady_clock::now())
|
||||
, retxCount(0)
|
||||
, nGreaterSeqAcks(0)
|
||||
, wasTimedOutBySeq(false)
|
||||
{
|
||||
}
|
||||
|
||||
LpReliability::NetPkt::NetPkt()
|
||||
: didRetx(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2014-2017, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
@@ -79,11 +79,11 @@ public:
|
||||
const GenericLinkService*
|
||||
getLinkService() const;
|
||||
|
||||
/** \brief observe outgoing fragment(s) of a network packet
|
||||
/** \brief observe outgoing fragment(s) of a network packet and store for potential retransmission
|
||||
* \param frags fragments of network packet
|
||||
*/
|
||||
void
|
||||
observeOutgoing(const std::vector<lp::Packet>& frags);
|
||||
handleOutgoing(std::vector<lp::Packet>& frags);
|
||||
|
||||
/** \brief extract and parse all Acks and add Ack for contained Fragment (if any) to AckQueue
|
||||
* \param pkt incoming LpPacket
|
||||
@@ -99,6 +99,19 @@ public:
|
||||
piggyback(lp::Packet& pkt, ssize_t mtu);
|
||||
|
||||
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
class UnackedFrag;
|
||||
class NetPkt;
|
||||
using UnackedFrags = std::map<lp::Sequence, UnackedFrag>;
|
||||
|
||||
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
/** \brief assign TxSequence number to a fragment
|
||||
* \param frag fragment to assign TxSequence to
|
||||
* \return assigned TxSequence number
|
||||
* \throw std::length_error assigned TxSequence is equal to the start of the existing window
|
||||
*/
|
||||
lp::Sequence
|
||||
assignTxSequence(lp::Packet& frag);
|
||||
|
||||
/** \brief start the idle Ack timer
|
||||
*
|
||||
* This timer requests an IDLE packet to acknowledge pending fragments not already piggybacked.
|
||||
@@ -113,44 +126,46 @@ PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
void
|
||||
stopIdleAckTimer();
|
||||
|
||||
private:
|
||||
/** \brief find and mark as lost fragments where a configurable number of Acks have been received
|
||||
* for greater sequence numbers
|
||||
* \param ackSeq sequence number of received Ack
|
||||
* \return vector containing sequence numbers marked lost by this mechanism
|
||||
/** \brief find and mark as lost fragments where a configurable number of Acks
|
||||
* (\p m_options.seqNumLossThreshold) have been received for greater TxSequence numbers
|
||||
* \param ackIt iterator pointing to acknowledged fragment
|
||||
* \return vector containing iterators to fragments marked lost by this mechanism
|
||||
*/
|
||||
std::vector<lp::Sequence>
|
||||
findLostLpPackets(lp::Sequence ackSeq);
|
||||
std::vector<UnackedFrags::iterator>
|
||||
findLostLpPackets(UnackedFrags::iterator ackIt);
|
||||
|
||||
/** \brief resend (or declare as lost) a lost fragment
|
||||
/** \brief resend (or give up on) a lost fragment
|
||||
*/
|
||||
void
|
||||
onLpPacketLost(lp::Sequence seq);
|
||||
|
||||
class UnackedFrag;
|
||||
class NetPkt;
|
||||
onLpPacketLost(UnackedFrags::iterator txSeqIt);
|
||||
|
||||
/** \brief remove the fragment with the given sequence number from the map of unacknowledged
|
||||
* fragments as well as its associated network packet
|
||||
* \param fragIt iterator to fragment to be removed
|
||||
* fragments, as well as its associated network packet (if any)
|
||||
* \param fragIt iterator to acknowledged fragment
|
||||
*
|
||||
* If the given sequence marks the beginning of the send window, the window will be incremented.
|
||||
* If the given TxSequence marks the beginning of the send window, the window will be incremented.
|
||||
* If the associated network packet has been fully transmitted, it will be removed.
|
||||
*/
|
||||
void
|
||||
onLpPacketAcknowledged(std::map<lp::Sequence, UnackedFrag>::iterator fragIt,
|
||||
std::map<lp::Sequence, NetPkt>::iterator netPktIt);
|
||||
onLpPacketAcknowledged(UnackedFrags::iterator fragIt);
|
||||
|
||||
std::map<lp::Sequence, NetPkt>::iterator
|
||||
getNetPktByFrag(lp::Sequence seq);
|
||||
/** \brief delete a fragment from UnackedFrags and advance acknowledge window if necessary
|
||||
* \param fragIt iterator to an UnackedFrag, must be dereferencable
|
||||
* \post fragIt is not in m_unackedFrags
|
||||
* \post if was equal to m_firstUnackedFrag,
|
||||
* m_firstUnackedFrag is set to the UnackedFrag after fragIt with consideration of
|
||||
* TxSequence number wraparound, or set to m_unackedFrags.end() if m_unackedFrags is empty
|
||||
*/
|
||||
void
|
||||
deleteUnackedFrag(UnackedFrags::iterator fragIt);
|
||||
|
||||
private:
|
||||
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
/** \brief contains a sent fragment that has not been acknowledged and associated data
|
||||
*/
|
||||
class UnackedFrag
|
||||
{
|
||||
public:
|
||||
// Allows implicit conversion from an lp::Packet
|
||||
explicit
|
||||
UnackedFrag(lp::Packet pkt);
|
||||
|
||||
public:
|
||||
@@ -159,7 +174,7 @@ private:
|
||||
time::steady_clock::TimePoint sendTime;
|
||||
size_t retxCount;
|
||||
size_t nGreaterSeqAcks; //!< number of Acks received for sequences greater than this fragment
|
||||
bool wasTimedOutBySeq; //!< whether this fragment has been timed out by the sequence number mechanic
|
||||
shared_ptr<NetPkt> netPkt;
|
||||
};
|
||||
|
||||
/** \brief contains a network-layer packet with unacknowledged fragments
|
||||
@@ -167,20 +182,26 @@ private:
|
||||
class NetPkt
|
||||
{
|
||||
public:
|
||||
NetPkt();
|
||||
|
||||
public:
|
||||
std::set<lp::Sequence> unackedFrags;
|
||||
bool didRetx;
|
||||
std::vector<UnackedFrags::iterator> unackedFrags;
|
||||
bool didRetx = false;
|
||||
};
|
||||
|
||||
public:
|
||||
/// TxSequence TLV-TYPE (3 octets) + TxSequence TLV-LENGTH (1 octet) + sizeof(lp::Sequence)
|
||||
static constexpr size_t RESERVED_HEADER_SPACE = 3 + 1 + sizeof(lp::Sequence);
|
||||
|
||||
PUBLIC_WITH_TESTS_ELSE_PRIVATE:
|
||||
Options m_options;
|
||||
GenericLinkService* m_linkService;
|
||||
std::map<lp::Sequence, UnackedFrag> m_unackedFrags;
|
||||
std::map<lp::Sequence, UnackedFrag>::iterator m_firstUnackedFrag;
|
||||
std::map<lp::Sequence, NetPkt> m_netPkts;
|
||||
UnackedFrags m_unackedFrags;
|
||||
/** An iterator that points to the first unacknowledged fragment in the current window. The window
|
||||
* can wrap around so that the beginning of the window is at a TxSequence greater than other
|
||||
* fragments in the window. When the window is moved past the last item in the iterator, the
|
||||
* first fragment in the map will become the start of the window.
|
||||
*/
|
||||
UnackedFrags::iterator m_firstUnackedFrag;
|
||||
std::queue<lp::Sequence> m_ackQueue;
|
||||
lp::Sequence m_lastTxSeqNo;
|
||||
scheduler::ScopedEventId m_idleAckTimer;
|
||||
bool m_isIdleAckTimerRunning;
|
||||
RttEstimator m_rto;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/**
|
||||
/*
|
||||
* Copyright (c) 2014-2017, Regents of the University of California,
|
||||
* Arizona Board of Regents,
|
||||
* Colorado State University,
|
||||
@@ -399,7 +399,7 @@ BOOST_AUTO_TEST_SUITE_END() // Fragmentation
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(Reliability)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(SendInterestWithSequence)
|
||||
BOOST_AUTO_TEST_CASE(SendInterest)
|
||||
{
|
||||
// Initialize with Options that enables reliability
|
||||
GenericLinkService::Options options;
|
||||
@@ -416,10 +416,10 @@ BOOST_AUTO_TEST_CASE(SendInterestWithSequence)
|
||||
lp::Packet interest1pkt;
|
||||
BOOST_REQUIRE_NO_THROW(interest1pkt.wireDecode(transport->sentPackets.back().packet));
|
||||
BOOST_CHECK(interest1pkt.has<lp::FragmentField>());
|
||||
BOOST_CHECK(interest1pkt.has<lp::SequenceField>());
|
||||
BOOST_CHECK(interest1pkt.has<lp::TxSequenceField>());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(SendDataWithSequence)
|
||||
BOOST_AUTO_TEST_CASE(SendData)
|
||||
{
|
||||
// Initialize with Options that enables reliability
|
||||
GenericLinkService::Options options;
|
||||
@@ -436,10 +436,10 @@ BOOST_AUTO_TEST_CASE(SendDataWithSequence)
|
||||
lp::Packet data1pkt;
|
||||
BOOST_REQUIRE_NO_THROW(data1pkt.wireDecode(transport->sentPackets.back().packet));
|
||||
BOOST_CHECK(data1pkt.has<lp::FragmentField>());
|
||||
BOOST_CHECK(data1pkt.has<lp::SequenceField>());
|
||||
BOOST_CHECK(data1pkt.has<lp::TxSequenceField>());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(SendNackWithSequence)
|
||||
BOOST_AUTO_TEST_CASE(SendNack)
|
||||
{
|
||||
// Initialize with Options that enables reliability
|
||||
GenericLinkService::Options options;
|
||||
@@ -457,7 +457,7 @@ BOOST_AUTO_TEST_CASE(SendNackWithSequence)
|
||||
BOOST_REQUIRE_NO_THROW(nack1pkt.wireDecode(transport->sentPackets.back().packet));
|
||||
BOOST_CHECK(nack1pkt.has<lp::NackField>());
|
||||
BOOST_CHECK(nack1pkt.has<lp::FragmentField>());
|
||||
BOOST_CHECK(nack1pkt.has<lp::SequenceField>());
|
||||
BOOST_CHECK(nack1pkt.has<lp::TxSequenceField>());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END() // Reliability
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user