tcp: TcpTxBuffer stores a list of TcpTxItem
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2010 Adrian Sai-wah Tam
|
||||
* Copyright (c) 2010-2015 Adrian Sai-wah Tam
|
||||
* Copyright (c) 2016 Natale Patriciello <natale.patriciello@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
@@ -15,16 +16,16 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Author: Adrian Sai-wah Tam <adrian.sw.tam@gmail.com>
|
||||
* Original author: Adrian Sai-wah Tam <adrian.sw.tam@gmail.com>
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include "ns3/packet.h"
|
||||
#include "ns3/fatal-error.h"
|
||||
#include "ns3/log.h"
|
||||
#include "ns3/abort.h"
|
||||
#include "ns3/tcp-option-ts.h"
|
||||
|
||||
#include "tcp-tx-buffer.h"
|
||||
|
||||
@@ -32,6 +33,62 @@ namespace ns3 {
|
||||
|
||||
NS_LOG_COMPONENT_DEFINE ("TcpTxBuffer");
|
||||
|
||||
TcpTxItem::TcpTxItem ()
|
||||
: m_packet (0),
|
||||
m_lost (false),
|
||||
m_retrans (false),
|
||||
m_lastSent (Time::Min ()),
|
||||
m_sacked (false)
|
||||
{
|
||||
}
|
||||
|
||||
TcpTxItem::TcpTxItem (const TcpTxItem &other)
|
||||
: m_packet (other.m_packet),
|
||||
m_lost (other.m_lost),
|
||||
m_retrans (other.m_retrans),
|
||||
m_lastSent (other.m_lastSent),
|
||||
m_sacked (other.m_sacked)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
TcpTxItem::Print (std::ostream &os) const
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
bool comma = false;
|
||||
os << "pkt pointer: " << m_packet;
|
||||
|
||||
if (m_lost)
|
||||
{
|
||||
os << "[lost]";
|
||||
comma = true;
|
||||
}
|
||||
if (m_retrans)
|
||||
{
|
||||
if (comma)
|
||||
{
|
||||
os << ",";
|
||||
}
|
||||
|
||||
os << "[retrans]";
|
||||
comma = true;
|
||||
}
|
||||
if (m_sacked)
|
||||
{
|
||||
if (comma)
|
||||
{
|
||||
os << ",";
|
||||
}
|
||||
os << "[sacked]";
|
||||
comma = true;
|
||||
}
|
||||
if (comma)
|
||||
{
|
||||
os << ",";
|
||||
}
|
||||
os << "last sent: " << m_lastSent;
|
||||
}
|
||||
|
||||
NS_OBJECT_ENSURE_REGISTERED (TcpTxBuffer);
|
||||
|
||||
TypeId
|
||||
@@ -57,12 +114,27 @@ TcpTxBuffer::GetTypeId (void)
|
||||
* initialized below is insignificant.
|
||||
*/
|
||||
TcpTxBuffer::TcpTxBuffer (uint32_t n)
|
||||
: m_firstByteSeq (n), m_size (0), m_maxBuffer (32768), m_data (0)
|
||||
: m_maxBuffer (32768), m_size (0), m_sentSize (0), m_firstByteSeq (n)
|
||||
{
|
||||
}
|
||||
|
||||
TcpTxBuffer::~TcpTxBuffer (void)
|
||||
{
|
||||
PacketList::iterator it;
|
||||
|
||||
for (it = m_sentList.begin (); it != m_sentList.end (); ++it)
|
||||
{
|
||||
TcpTxItem *item = *it;
|
||||
m_sentSize -= item->m_packet->GetSize ();
|
||||
delete item;
|
||||
}
|
||||
|
||||
for (it = m_appList.begin (); it != m_appList.end (); ++it)
|
||||
{
|
||||
TcpTxItem *item = *it;
|
||||
m_size -= item->m_packet->GetSize ();
|
||||
delete item;
|
||||
}
|
||||
}
|
||||
|
||||
SequenceNumber32
|
||||
@@ -101,23 +173,34 @@ TcpTxBuffer::Available (void) const
|
||||
return m_maxBuffer - m_size;
|
||||
}
|
||||
|
||||
void
|
||||
TcpTxBuffer::SetHeadSequence (const SequenceNumber32& seq)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << seq);
|
||||
m_firstByteSeq = seq;
|
||||
}
|
||||
|
||||
bool
|
||||
TcpTxBuffer::Add (Ptr<Packet> p)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << p);
|
||||
NS_LOG_LOGIC ("Packet of size " << p->GetSize () << " appending to window starting at "
|
||||
<< m_firstByteSeq << ", availSize="<< Available ());
|
||||
NS_LOG_INFO ("Try to append " << p->GetSize () << " bytes to window starting at "
|
||||
<< m_firstByteSeq << ", availSize=" << Available ());
|
||||
if (p->GetSize () <= Available ())
|
||||
{
|
||||
if (p->GetSize () > 0)
|
||||
{
|
||||
m_data.push_back (p);
|
||||
TcpTxItem *item = new TcpTxItem ();
|
||||
item->m_packet = p;
|
||||
m_appList.insert (m_appList.end (), item);
|
||||
m_size += p->GetSize ();
|
||||
NS_LOG_LOGIC ("Updated size=" << m_size << ", lastSeq=" << m_firstByteSeq + SequenceNumber32 (m_size));
|
||||
|
||||
NS_LOG_INFO ("Updated size=" << m_size << ", lastSeq=" <<
|
||||
m_firstByteSeq + SequenceNumber32 (m_size));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
NS_LOG_LOGIC ("Rejected. Not enough room to buffer packet.");
|
||||
NS_LOG_WARN ("Rejected. Not enough room to buffer packet.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -126,120 +209,378 @@ TcpTxBuffer::SizeFromSequence (const SequenceNumber32& seq) const
|
||||
{
|
||||
NS_LOG_FUNCTION (this << seq);
|
||||
// Sequence of last byte in buffer
|
||||
SequenceNumber32 lastSeq = m_firstByteSeq + SequenceNumber32 (m_size);
|
||||
// Non-negative size
|
||||
NS_LOG_LOGIC ("HeadSeq=" << m_firstByteSeq << ", lastSeq=" << lastSeq << ", size=" << m_size <<
|
||||
", returns " << lastSeq - seq);
|
||||
return lastSeq - seq;
|
||||
SequenceNumber32 lastSeq = TailSequence ();
|
||||
|
||||
if (lastSeq >= seq)
|
||||
{
|
||||
return lastSeq - seq;
|
||||
}
|
||||
|
||||
NS_LOG_ERROR ("Requested a sequence beyond our space (" << seq << " > " << lastSeq <<
|
||||
"). Returning 0 for convenience.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Ptr<Packet>
|
||||
TcpTxBuffer::CopyFromSequence (uint32_t numBytes, const SequenceNumber32& seq)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << numBytes << seq);
|
||||
uint32_t s = std::min (numBytes, SizeFromSequence (seq)); // Real size to extract. Insure not beyond end of data
|
||||
if (s == 0)
|
||||
NS_LOG_FUNCTION (*this << numBytes << seq);
|
||||
|
||||
if (m_firstByteSeq > seq)
|
||||
{
|
||||
return Create<Packet> (); // Empty packet returned
|
||||
}
|
||||
if (m_data.size () == 0)
|
||||
{ // No actual data, just return dummy-data packet of correct size
|
||||
return Create<Packet> (s);
|
||||
NS_LOG_ERROR ("Requested a sequence number which is not in the buffer anymore");
|
||||
return Create<Packet> ();
|
||||
}
|
||||
|
||||
// Extract data from the buffer and return
|
||||
uint32_t offset = seq - m_firstByteSeq.Get ();
|
||||
uint32_t count = 0; // Offset of the first byte of a packet in the buffer
|
||||
uint32_t pktSize = 0;
|
||||
bool beginFound = false;
|
||||
int pktCount = 0;
|
||||
Ptr<Packet> outPacket;
|
||||
NS_LOG_LOGIC ("There are " << m_data.size () << " number of packets in buffer");
|
||||
for (BufIterator i = m_data.begin (); i != m_data.end (); ++i)
|
||||
// Real size to extract. Insure not beyond end of data
|
||||
uint32_t s = std::min (numBytes, SizeFromSequence (seq));
|
||||
|
||||
if (s == 0)
|
||||
{
|
||||
pktCount++;
|
||||
pktSize = (*i)->GetSize ();
|
||||
if (!beginFound)
|
||||
{ // Look for first fragment
|
||||
if (count + pktSize > offset)
|
||||
{
|
||||
NS_LOG_LOGIC ("First byte found in packet #" << pktCount << " at buffer offset " << count
|
||||
<< ", packet len=" << pktSize);
|
||||
beginFound = true;
|
||||
uint32_t packetOffset = offset - count;
|
||||
uint32_t fragmentLength = count + pktSize - offset;
|
||||
if (fragmentLength >= s)
|
||||
{ // Data to be copied falls entirely in this packet
|
||||
return (*i)->CreateFragment (packetOffset, s);
|
||||
}
|
||||
else
|
||||
{ // This packet only fulfills part of the request
|
||||
outPacket = (*i)->CreateFragment (packetOffset, fragmentLength);
|
||||
}
|
||||
NS_LOG_LOGIC ("Output packet is now of size " << outPacket->GetSize ());
|
||||
}
|
||||
}
|
||||
else if (count + pktSize >= offset + s)
|
||||
{ // Last packet fragment found
|
||||
NS_LOG_LOGIC ("Last byte found in packet #" << pktCount << " at buffer offset " << count
|
||||
<< ", packet len=" << pktSize);
|
||||
uint32_t fragmentLength = offset + s - count;
|
||||
Ptr<Packet> endFragment = (*i)->CreateFragment (0, fragmentLength);
|
||||
outPacket->AddAtEnd (endFragment);
|
||||
NS_LOG_LOGIC ("Output packet is now of size " << outPacket->GetSize ());
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
NS_LOG_LOGIC ("Appending to output the packet #" << pktCount << " of offset " << count << " len=" << pktSize);
|
||||
outPacket->AddAtEnd (*i);
|
||||
NS_LOG_LOGIC ("Output packet is now of size " << outPacket->GetSize ());
|
||||
}
|
||||
count += pktSize;
|
||||
return Create<Packet> ();
|
||||
}
|
||||
NS_ASSERT (outPacket->GetSize () == s);
|
||||
return outPacket;
|
||||
|
||||
TcpTxItem *outItem = 0;
|
||||
|
||||
if (m_firstByteSeq + m_sentSize >= seq + s)
|
||||
{
|
||||
// already sent this block completely
|
||||
outItem = GetTransmittedSegment (s, seq);
|
||||
NS_ASSERT (outItem != 0);
|
||||
outItem->m_retrans = true;
|
||||
|
||||
NS_LOG_DEBUG ("Retransmitting [" << seq << ";" << seq + s << "|" << s <<
|
||||
"] from " << *this);
|
||||
}
|
||||
else if (m_firstByteSeq + m_sentSize <= seq)
|
||||
{
|
||||
NS_ABORT_MSG_UNLESS (m_firstByteSeq + m_sentSize == seq,
|
||||
"Requesting a piece of new data with an hole");
|
||||
|
||||
// this is the first time we transmit this block
|
||||
outItem = GetNewSegment (s);
|
||||
NS_ASSERT (outItem != 0);
|
||||
NS_ASSERT (outItem->m_retrans == false);
|
||||
|
||||
NS_LOG_DEBUG ("New segment [" << seq << ";" << seq + s << "|" << s <<
|
||||
"] from " << *this);
|
||||
}
|
||||
else if (m_firstByteSeq + m_sentSize > seq && m_firstByteSeq + m_sentSize < seq + s)
|
||||
{
|
||||
// Partial: a part is retransmission, the remaining data is new
|
||||
|
||||
// Take the new data and move it into sent list
|
||||
uint32_t amount = seq + s - m_firstByteSeq.Get () - m_sentSize;
|
||||
NS_LOG_DEBUG ("Moving segment [" << m_firstByteSeq + m_sentSize << ";" <<
|
||||
m_firstByteSeq + m_sentSize + amount <<"|" << amount <<
|
||||
"] from " << *this);
|
||||
|
||||
outItem = GetNewSegment (amount);
|
||||
NS_ASSERT (outItem != 0);
|
||||
|
||||
// Now get outItem from the sent list (there will be a merge)
|
||||
return CopyFromSequence (numBytes, seq);
|
||||
}
|
||||
|
||||
outItem->m_lost = false;
|
||||
outItem->m_lastSent = Simulator::Now ();
|
||||
Ptr<Packet> toRet = outItem->m_packet->Copy ();
|
||||
|
||||
NS_ASSERT (toRet->GetSize () == s);
|
||||
|
||||
return toRet;
|
||||
}
|
||||
|
||||
TcpTxItem*
|
||||
TcpTxBuffer::GetNewSegment (uint32_t numBytes)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << numBytes);
|
||||
|
||||
SequenceNumber32 startOfAppList = m_firstByteSeq + m_sentSize;
|
||||
|
||||
TcpTxItem *item = GetPacketFromList (m_appList, startOfAppList,
|
||||
numBytes, startOfAppList);
|
||||
|
||||
// Move item from AppList to SentList (should be the first, not too complex)
|
||||
PacketList::iterator it = std::find (m_appList.begin (), m_appList.end (), item);
|
||||
NS_ASSERT (it != m_appList.end ());
|
||||
|
||||
m_appList.erase (it);
|
||||
m_sentList.insert (m_sentList.end (), item);
|
||||
m_sentSize += item->m_packet->GetSize ();
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
TcpTxItem*
|
||||
TcpTxBuffer::GetTransmittedSegment (uint32_t numBytes, const SequenceNumber32 &seq)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << numBytes << seq);
|
||||
NS_ASSERT (seq >= m_firstByteSeq);
|
||||
NS_ASSERT (numBytes <= m_sentSize);
|
||||
|
||||
return GetPacketFromList (m_sentList, m_firstByteSeq, numBytes, seq);
|
||||
}
|
||||
|
||||
void
|
||||
TcpTxBuffer::SetHeadSequence (const SequenceNumber32& seq)
|
||||
TcpTxBuffer::SplitItems (TcpTxItem &t1, TcpTxItem &t2, uint32_t size) const
|
||||
{
|
||||
NS_LOG_FUNCTION (this << seq);
|
||||
m_firstByteSeq = seq;
|
||||
NS_LOG_FUNCTION (this << size);
|
||||
|
||||
t1.m_packet = t2.m_packet->CreateFragment (0, size);
|
||||
t2.m_packet->RemoveAtStart (size);
|
||||
|
||||
t1.m_sacked = t2.m_sacked;
|
||||
t1.m_lastSent = t2.m_lastSent;
|
||||
t1.m_retrans = t2.m_retrans;
|
||||
t1.m_lost = t2.m_lost;
|
||||
}
|
||||
|
||||
TcpTxItem*
|
||||
TcpTxBuffer::GetPacketFromList (PacketList &list, const SequenceNumber32 &listStartFrom,
|
||||
uint32_t numBytes, const SequenceNumber32 &seq) const
|
||||
{
|
||||
NS_LOG_FUNCTION (this << numBytes << seq);
|
||||
|
||||
/*
|
||||
* Our possibilites are sketched out in the following:
|
||||
*
|
||||
* |------| |----| |----|
|
||||
* GetList (m_data) = | | --> | | --> | |
|
||||
* |------| |----| |----|
|
||||
*
|
||||
* ^ ^ ^ ^
|
||||
* | | | | (1)
|
||||
* seq | | numBytes
|
||||
* | |
|
||||
* | |
|
||||
* seq numBytes (2)
|
||||
*
|
||||
* (1) seq and numBytes are the boundary of some packet
|
||||
* (2) seq and numBytes are not the boundary of some packet
|
||||
*
|
||||
* We can have mixed case (e.g. seq over the boundary while numBytes not).
|
||||
*
|
||||
* If we discover that we are in (2) or in a mixed case, we split
|
||||
* packets accordingly to the requested bounds and re-run the function.
|
||||
*
|
||||
* In (1), things are pretty easy, it's just a matter of walking the list and
|
||||
* defragment packets, if needed (e.g. seq is the beginning of the first packet
|
||||
* while maxBytes is the end of some packet next in the list).
|
||||
*/
|
||||
|
||||
Ptr<Packet> currentPacket = 0;
|
||||
TcpTxItem *currentItem = 0;
|
||||
TcpTxItem *outItem = 0;
|
||||
PacketList::iterator it = list.begin ();
|
||||
SequenceNumber32 beginOfCurrentPacket = listStartFrom;
|
||||
|
||||
while (it != list.end ())
|
||||
{
|
||||
currentItem = *it;
|
||||
currentPacket = currentItem->m_packet;
|
||||
|
||||
// The objective of this snippet is to find (or to create) the packet
|
||||
// that begin with the sequence seq
|
||||
|
||||
if (seq < beginOfCurrentPacket + currentPacket->GetSize ())
|
||||
{
|
||||
// seq is inside the current packet
|
||||
if (seq == beginOfCurrentPacket)
|
||||
{
|
||||
// seq is the beginning of the current packet. Hurray!
|
||||
outItem = currentItem;
|
||||
NS_LOG_INFO ("Current packet starts at seq " << seq <<
|
||||
" ends at " << seq + currentPacket->GetSize ());
|
||||
}
|
||||
else if (seq > beginOfCurrentPacket)
|
||||
{
|
||||
// seq is inside the current packet but seq is not the beginning,
|
||||
// it's somewhere in the middle. Just fragment the beginning and
|
||||
// start again.
|
||||
NS_LOG_INFO ("we are at " << beginOfCurrentPacket <<
|
||||
" searching for " << seq <<
|
||||
" and now we recurse because packet ends at "
|
||||
<< beginOfCurrentPacket + currentPacket->GetSize ());
|
||||
TcpTxItem *firstPart = new TcpTxItem ();
|
||||
SplitItems (*firstPart, *currentItem, seq - beginOfCurrentPacket);
|
||||
|
||||
// insert firstPart before currentItem
|
||||
list.insert (it, firstPart);
|
||||
|
||||
return GetPacketFromList (list, listStartFrom, numBytes, seq);
|
||||
}
|
||||
else
|
||||
{
|
||||
NS_FATAL_ERROR ("seq < beginOfCurrentPacket: our data is before");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Walk the list, the current packet does not contain seq
|
||||
beginOfCurrentPacket += currentPacket->GetSize ();
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
|
||||
NS_ASSERT (outItem != 0);
|
||||
|
||||
// The objective of this snippet is to find (or to create) the packet
|
||||
// that ends after numBytes bytes. We are sure that outPacket starts
|
||||
// at seq.
|
||||
|
||||
if (seq + numBytes <= beginOfCurrentPacket + currentPacket->GetSize ())
|
||||
{
|
||||
// the end boundary is inside the current packet
|
||||
if (numBytes == currentPacket->GetSize ())
|
||||
{
|
||||
// the end boundary is exactly the end of the current packet. Hurray!
|
||||
if (currentItem->m_packet == outItem->m_packet)
|
||||
{
|
||||
// A perfect match!
|
||||
return outItem;
|
||||
}
|
||||
else
|
||||
{
|
||||
// the end is exactly the end of current packet, but
|
||||
// current > outPacket in the list. Merge current with the
|
||||
// previous, and recurse.
|
||||
NS_ASSERT (it != list.begin ());
|
||||
TcpTxItem *previous = *(--it);
|
||||
|
||||
list.erase (it);
|
||||
|
||||
MergeItems (*previous, *currentItem);
|
||||
delete currentItem;
|
||||
|
||||
return GetPacketFromList (list, listStartFrom, numBytes, seq);
|
||||
}
|
||||
}
|
||||
else if (numBytes < currentPacket->GetSize ())
|
||||
{
|
||||
// the end is inside the current packet, but it isn't exactly
|
||||
// the packet end. Just fragment, fix the list, and return.
|
||||
TcpTxItem *firstPart = new TcpTxItem ();
|
||||
SplitItems (*firstPart, *currentItem, numBytes);
|
||||
|
||||
// insert firstPart before currentItem
|
||||
list.insert (it, firstPart);
|
||||
|
||||
return firstPart;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The end isn't inside current packet, but there is an exception for
|
||||
// the merge and recurse strategy...
|
||||
if (++it == list.end ())
|
||||
{
|
||||
// ...current is the last packet we sent. We have not more data;
|
||||
// Go for this one.
|
||||
NS_LOG_WARN ("Cannot reach the end, but this case is covered "
|
||||
"with conditional statements inside CopyFromSequence."
|
||||
"Something has gone wrong, report a bug");
|
||||
return outItem;
|
||||
}
|
||||
|
||||
// The current packet does not contain the requested end. Merge current
|
||||
// with the packet that follows, and recurse
|
||||
TcpTxItem *next = (*it); // Please remember we have incremented it
|
||||
// in the previous if
|
||||
|
||||
MergeItems (*currentItem, *next);
|
||||
list.erase (it);
|
||||
|
||||
delete next;
|
||||
|
||||
return GetPacketFromList (list, listStartFrom, numBytes, seq);
|
||||
}
|
||||
}
|
||||
|
||||
NS_FATAL_ERROR ("This point is not reachable");
|
||||
}
|
||||
|
||||
void
|
||||
TcpTxBuffer::MergeItems (TcpTxItem &t1, TcpTxItem &t2) const
|
||||
{
|
||||
NS_LOG_FUNCTION (this);
|
||||
if (t1.m_sacked == true && t2.m_sacked == true)
|
||||
{
|
||||
t1.m_sacked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
t1.m_sacked = false;
|
||||
}
|
||||
|
||||
if (t2.m_retrans == true && t1.m_retrans == false)
|
||||
{
|
||||
t1.m_retrans = true;
|
||||
}
|
||||
if (t1.m_lastSent < t2.m_lastSent)
|
||||
{
|
||||
t1.m_lastSent = t2.m_lastSent;
|
||||
}
|
||||
if (t2.m_lost)
|
||||
{
|
||||
t1.m_lost = true;
|
||||
}
|
||||
|
||||
t1.m_packet->AddAtEnd (t2.m_packet);
|
||||
}
|
||||
|
||||
void
|
||||
TcpTxBuffer::DiscardUpTo (const SequenceNumber32& seq)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << seq);
|
||||
NS_LOG_LOGIC ("current data size=" << m_size << ", headSeq=" << m_firstByteSeq << ", maxBuffer=" << m_maxBuffer
|
||||
<< ", numPkts=" << m_data.size ());
|
||||
|
||||
// Cases do not need to scan the buffer
|
||||
if (m_firstByteSeq >= seq) return;
|
||||
if (m_firstByteSeq >= seq)
|
||||
{
|
||||
NS_LOG_DEBUG ("Seq " << seq << " already discarded.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Scan the buffer and discard packets
|
||||
uint32_t offset = seq - m_firstByteSeq.Get (); // Number of bytes to remove
|
||||
uint32_t pktSize;
|
||||
NS_LOG_LOGIC ("Offset=" << offset);
|
||||
BufIterator i = m_data.begin ();
|
||||
while (i != m_data.end ())
|
||||
PacketList::iterator i = m_sentList.begin ();
|
||||
while (m_size > 0 && offset > 0)
|
||||
{
|
||||
if (offset > (*i)->GetSize ())
|
||||
if (i == m_sentList.end ())
|
||||
{
|
||||
Ptr<Packet> p = CopyFromSequence (offset, m_firstByteSeq);
|
||||
NS_ASSERT (p != 0);
|
||||
i = m_sentList.begin ();
|
||||
NS_ASSERT (i != m_sentList.end ());
|
||||
}
|
||||
TcpTxItem *item = *i;
|
||||
Ptr<Packet> p = item->m_packet;
|
||||
pktSize = p->GetSize ();
|
||||
|
||||
if (offset >= pktSize)
|
||||
{ // This packet is behind the seqnum. Remove this packet from the buffer
|
||||
pktSize = (*i)->GetSize ();
|
||||
m_size -= pktSize;
|
||||
m_sentSize -= pktSize;
|
||||
offset -= pktSize;
|
||||
m_firstByteSeq += pktSize;
|
||||
i = m_data.erase (i);
|
||||
NS_LOG_LOGIC ("Removed one packet of size " << pktSize << ", offset=" << offset);
|
||||
i = m_sentList.erase (i);
|
||||
delete item;
|
||||
NS_LOG_INFO ("While removing up to " << seq <<
|
||||
".Removed one packet of size " << pktSize <<
|
||||
" starting from " << m_firstByteSeq - pktSize <<
|
||||
". Remaining data " << m_size);
|
||||
}
|
||||
else if (offset > 0)
|
||||
{ // Part of the packet is behind the seqnum. Fragment
|
||||
pktSize = (*i)->GetSize () - offset;
|
||||
*i = (*i)->CreateFragment (offset, pktSize);
|
||||
pktSize -= offset;
|
||||
// PacketTags are preserved when fragmenting
|
||||
item->m_packet = item->m_packet->CreateFragment (offset, pktSize);
|
||||
m_size -= offset;
|
||||
m_sentSize -= offset;
|
||||
m_firstByteSeq += offset;
|
||||
NS_LOG_LOGIC ("Fragmented one packet by size " << offset << ", new size=" << pktSize);
|
||||
NS_LOG_INFO ("Fragmented one packet by size " << offset <<
|
||||
", new size=" << pktSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -248,9 +589,57 @@ TcpTxBuffer::DiscardUpTo (const SequenceNumber32& seq)
|
||||
{
|
||||
m_firstByteSeq = seq;
|
||||
}
|
||||
NS_LOG_LOGIC ("size=" << m_size << " headSeq=" << m_firstByteSeq << " maxBuffer=" << m_maxBuffer
|
||||
<<" numPkts="<< m_data.size ());
|
||||
NS_ASSERT (m_firstByteSeq == seq);
|
||||
|
||||
if (!m_sentList.empty ())
|
||||
{
|
||||
TcpTxItem *head = m_sentList.front ();
|
||||
if (head->m_sacked)
|
||||
{
|
||||
// It is not possible to have the UNA sacked; otherwise, it would
|
||||
// have been ACKed. This is, most likely, our wrong guessing
|
||||
// when crafting the SACK option for a non-SACK receiver.
|
||||
head->m_sacked = false;
|
||||
}
|
||||
}
|
||||
|
||||
NS_LOG_DEBUG ("Discarded up to " << seq);
|
||||
NS_LOG_LOGIC ("Buffer status after discarding data " << *this);
|
||||
NS_ASSERT (m_firstByteSeq >= seq);
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
operator<< (std::ostream & os, TcpTxBuffer const & tcpTxBuf)
|
||||
{
|
||||
TcpTxBuffer::PacketList::const_iterator it;
|
||||
std::stringstream ss;
|
||||
SequenceNumber32 beginOfCurrentPacket = tcpTxBuf.m_firstByteSeq;
|
||||
uint32_t sentSize = 0, appSize = 0;
|
||||
|
||||
Ptr<Packet> p;
|
||||
for (it = tcpTxBuf.m_sentList.begin (); it != tcpTxBuf.m_sentList.end (); ++it)
|
||||
{
|
||||
p = (*it)->m_packet;
|
||||
ss << "[" << beginOfCurrentPacket << ";"
|
||||
<< beginOfCurrentPacket + p->GetSize () << "|" << p->GetSize () << "|";
|
||||
(*it)->Print (ss);
|
||||
ss << "]";
|
||||
sentSize += p->GetSize ();
|
||||
beginOfCurrentPacket += p->GetSize ();
|
||||
}
|
||||
|
||||
for (it = tcpTxBuf.m_appList.begin (); it != tcpTxBuf.m_appList.end (); ++it)
|
||||
{
|
||||
appSize += (*it)->m_packet->GetSize ();
|
||||
}
|
||||
|
||||
os << "Sent list: " << ss.str () << ", size = " << tcpTxBuf.m_sentList.size () <<
|
||||
" Total size: " << tcpTxBuf.m_size <<
|
||||
" m_firstByteSeq = " << tcpTxBuf.m_firstByteSeq <<
|
||||
" m_sentSize = " << tcpTxBuf.m_sentSize;
|
||||
|
||||
NS_ASSERT (sentSize == tcpTxBuf.m_sentSize);
|
||||
NS_ASSERT (tcpTxBuf.m_size - tcpTxBuf.m_sentSize == appSize);
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namepsace ns3
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) 2010 Adrian Sai-wah Tam
|
||||
* Copyright (c) 2010-2015 Adrian Sai-wah Tam
|
||||
* Copyright (c) 2016 Natale Patriciello <natale.patriciello@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
@@ -15,18 +16,16 @@
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Author: Adrian Sai-wah Tam <adrian.sw.tam@gmail.com>
|
||||
* Original author: Adrian Sai-wah Tam <adrian.sw.tam@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef TCP_TX_BUFFER_H
|
||||
#define TCP_TX_BUFFER_H
|
||||
|
||||
#include <list>
|
||||
#include "ns3/traced-value.h"
|
||||
#include "ns3/trace-source-accessor.h"
|
||||
#include "ns3/object.h"
|
||||
#include "ns3/traced-value.h"
|
||||
#include "ns3/sequence-number.h"
|
||||
#include "ns3/ptr.h"
|
||||
#include "ns3/nstime.h"
|
||||
|
||||
namespace ns3 {
|
||||
class Packet;
|
||||
@@ -34,8 +33,72 @@ class Packet;
|
||||
/**
|
||||
* \ingroup tcp
|
||||
*
|
||||
* \brief class for keeping the data sent by the application to the TCP socket, i.e.
|
||||
* the sending buffer.
|
||||
* \brief Item that encloses the application packet and some flags for it
|
||||
*/
|
||||
class TcpTxItem
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \brief Constructor
|
||||
*/
|
||||
TcpTxItem ();
|
||||
|
||||
/**
|
||||
* \brief Copy-constructor
|
||||
* \param other TcpTxTag to copy values from
|
||||
*/
|
||||
TcpTxItem (const TcpTxItem &other);
|
||||
|
||||
/**
|
||||
* \brief Print the time
|
||||
* \param os ostream
|
||||
*/
|
||||
void Print (std::ostream &os) const;
|
||||
|
||||
Ptr<Packet> m_packet; //!< Application packet
|
||||
bool m_lost; //!< Indicates if the segment has been lost (RTO)
|
||||
bool m_retrans; //!< Indicates if the segment is retransmitted
|
||||
Time m_lastSent; //!< Timestamp of the time at which the segment has
|
||||
// been sent last time
|
||||
bool m_sacked; //!< Indicates if the segment has been SACKed
|
||||
};
|
||||
|
||||
/**
|
||||
* \ingroup tcp
|
||||
*
|
||||
* \brief Tcp sender buffer
|
||||
*
|
||||
* The class keeps track of all data that the application wishes to transmit
|
||||
* to the other end. When the data is acknowledged, it is removed from the buffer.
|
||||
* The buffer has a maximum size, and data is not saved if the amount exceeds
|
||||
* the limit. Packets can be added to the class through the method Add().
|
||||
* An important thing to remember is that all the data managed is strictly
|
||||
* sequential. It can be divided in blocks, but all the data follow a strict
|
||||
* ordering. That ordering is managed through SequenceNumber.
|
||||
*
|
||||
* In other words, this buffer contains numbered bytes (e.g 1,2,3), and the class
|
||||
* is allowed to return only ordered (using "<" as operator) subsets (e.g. 1,2
|
||||
* or 2,3 or 1,2,3).
|
||||
*
|
||||
* The data structure underlying this is composed by two distinct packet lists.
|
||||
*
|
||||
* The first (SentList) is initially empty, and it contains the packets returned
|
||||
* by the method CopyFromSequence.
|
||||
*
|
||||
* The second (AppList) is initially empty, and it contains the packets coming
|
||||
* from the applications, but that are not transmitted yet as segments.
|
||||
*
|
||||
* To discover how the chunk are managed and retrieved from these lists, check
|
||||
* CopyFromSequence documentation.
|
||||
*
|
||||
* The head of the data is represented by m_firstByteSeq, and it is returned by
|
||||
* HeadSequence(). The last byte is returned by TailSequence().
|
||||
* In this class we store also the size (in bytes) of the packets inside the
|
||||
* SentList in the variable m_sentSize.
|
||||
*
|
||||
* \see Size
|
||||
* \see SizeFromSequence
|
||||
* \see CopyFromSequence
|
||||
*/
|
||||
class TcpTxBuffer : public Object
|
||||
{
|
||||
@@ -55,43 +118,43 @@ public:
|
||||
// Accessors
|
||||
|
||||
/**
|
||||
* Returns the first byte's sequence number
|
||||
* \brief Get the sequence number of the buffer head
|
||||
* \returns the first byte's sequence number
|
||||
*/
|
||||
SequenceNumber32 HeadSequence (void) const;
|
||||
|
||||
/**
|
||||
* Returns the last byte's sequence number + 1
|
||||
* \brief Get the sequence number of the buffer tail (plus one)
|
||||
* \returns the last byte's sequence number + 1
|
||||
*/
|
||||
SequenceNumber32 TailSequence (void) const;
|
||||
|
||||
/**
|
||||
* Returns total number of bytes in this Tx buffer
|
||||
* \brief Returns total number of bytes in this buffer
|
||||
* \returns total number of bytes in this Tx buffer
|
||||
*/
|
||||
uint32_t Size (void) const;
|
||||
|
||||
/**
|
||||
* Returns the Tx window size
|
||||
* \brief Get the maximum buffer size
|
||||
* \returns the Tx window size (in bytes)
|
||||
*/
|
||||
uint32_t MaxBufferSize (void) const;
|
||||
|
||||
/**
|
||||
* Set the Tx window size
|
||||
* \brief Set the maximum buffer size
|
||||
* \param n Tx window size (in bytes)
|
||||
*/
|
||||
void SetMaxBufferSize (uint32_t n);
|
||||
|
||||
/**
|
||||
* Returns the available capacity in this Tx window
|
||||
* \brief Returns the available capacity of this buffer
|
||||
* \returns available capacity in this Tx window
|
||||
*/
|
||||
uint32_t Available (void) const;
|
||||
|
||||
/**
|
||||
* Append a data packet to the end of the buffer
|
||||
* \brief Append a data packet to the end of the buffer
|
||||
*
|
||||
* \param p The packet to be appended to the Tx buffer
|
||||
* \return Boolean to indicate success
|
||||
@@ -99,14 +162,28 @@ public:
|
||||
bool Add (Ptr<Packet> p);
|
||||
|
||||
/**
|
||||
* Returns the number of bytes from the buffer in the range [seq, tailSequence)
|
||||
* \brief Returns the number of bytes from the buffer in the range [seq, tailSequence)
|
||||
*
|
||||
* \param seq initial sequence number
|
||||
* \returns the number of bytes from the buffer in the range
|
||||
*/
|
||||
uint32_t SizeFromSequence (const SequenceNumber32& seq) const;
|
||||
|
||||
/**
|
||||
* Copy data of size numBytes into a packet, data from the range [seq, seq+numBytes)
|
||||
* \brief Copy data from the range [seq, seq+numBytes) into a packet
|
||||
*
|
||||
* In the following, we refer to the block [seq, seq+numBytes) simply as "block".
|
||||
* We check the boundary of the block, and divide the possibilities in three
|
||||
* cases:
|
||||
*
|
||||
* - the block have already been transmitted (managed in GetTransmittedSegment)
|
||||
* - the block have not been transmitted yet (managed in GetNewSegment)
|
||||
*
|
||||
* The last case is when the block is partially transmitted and partially
|
||||
* not transmitted. We trick this case by requesting the portion not transmitted
|
||||
* from GetNewSegment, and then calling GetTransmittedSegment with the full
|
||||
* block range.
|
||||
*
|
||||
* \param numBytes number of bytes to copy
|
||||
* \param seq start sequence number to extract
|
||||
* \returns a packet
|
||||
@@ -114,29 +191,166 @@ public:
|
||||
Ptr<Packet> CopyFromSequence (uint32_t numBytes, const SequenceNumber32& seq);
|
||||
|
||||
/**
|
||||
* Set the m_firstByteSeq to seq. Supposed to be called only when the
|
||||
* \brief Set the head sequence of the buffer
|
||||
*
|
||||
* Set the head (m_firstByteSeq) to seq. Supposed to be called only when the
|
||||
* connection is just set up and we did not send any data out yet.
|
||||
* \param seq The sequence number of the head byte
|
||||
*/
|
||||
void SetHeadSequence (const SequenceNumber32& seq);
|
||||
|
||||
/**
|
||||
* Discard data up to but not including this sequence number.
|
||||
* \brief Discard data up to but not including this sequence number.
|
||||
*
|
||||
* \param seq The sequence number of the head byte
|
||||
* \param seq The first sequence number to maintain after discarding all the
|
||||
* previous sequences.
|
||||
*/
|
||||
void DiscardUpTo (const SequenceNumber32& seq);
|
||||
|
||||
private:
|
||||
/// container for data stored in the buffer
|
||||
typedef std::list<Ptr<Packet> >::iterator BufIterator;
|
||||
friend std::ostream & operator<< (std::ostream & os, TcpTxBuffer const & tcpTxBuf);
|
||||
|
||||
typedef std::list<TcpTxItem*> PacketList; //!< container for data stored in the buffer
|
||||
|
||||
/**
|
||||
* \brief Get a block of data not transmitted yet and move it into SentList
|
||||
*
|
||||
* If the block is not yet transmitted, hopefully, seq is exactly the sequence
|
||||
* number of the first byte of the first packet inside AppList. We extract
|
||||
* the block from AppList and move it into the SentList, before returning the
|
||||
* block itself. We manage possible fragmentation (or merges) inside AppList
|
||||
* through GetPacketFromList.
|
||||
*
|
||||
* \see GetPacketFromList
|
||||
* \param numBytes number of bytes to copy
|
||||
*
|
||||
* \return the item that contains the right packet
|
||||
*/
|
||||
TcpTxItem* GetNewSegment (uint32_t numBytes);
|
||||
|
||||
/**
|
||||
* \brief Get a block of data previously transmitted
|
||||
*
|
||||
* This is clearly a retransmission, and if everything is going well,
|
||||
* the block requested is matching perfectly with another one requested
|
||||
* in the past. If not, fragmentation or merge are required. We manage
|
||||
* both inside GetPacketFromList.
|
||||
*
|
||||
* \see GetPacketFromList
|
||||
*
|
||||
* \param numBytes number of bytes to copy
|
||||
* \param seq sequence requested
|
||||
* \return the item that contains the right packet
|
||||
*/
|
||||
TcpTxItem* GetTransmittedSegment (uint32_t numBytes, const SequenceNumber32 &seq);
|
||||
|
||||
/**
|
||||
* \brief Get a block (which is returned as Packet) from a list
|
||||
*
|
||||
* This function extract a block [requestedSeq,numBytes) from the list, which
|
||||
* starts at startingSeq.
|
||||
*
|
||||
* The cases we need to manage are two, and they are depicted in the following
|
||||
* image:
|
||||
*
|
||||
*\verbatim
|
||||
|------| |----| |----|
|
||||
list = | | --> | | --> | |
|
||||
|------| |----| |----|
|
||||
|
||||
^ ^
|
||||
| ^ ^ | (1)
|
||||
seq | | seq + numBytes
|
||||
| |
|
||||
| |
|
||||
seq seq + numBytes (2)
|
||||
\endverbatim
|
||||
*
|
||||
* The case 1 is easy to manage: the requested block is exactly a packet
|
||||
* already stored. If one value (seq or seq + numBytes) does not align
|
||||
* to a packet boundary, or when both values does not align (case 2), it is
|
||||
* a bit more complex.
|
||||
*
|
||||
* Basically, we have two possible operations:
|
||||
*
|
||||
* - fragment : split an existing packet in two
|
||||
* - merge : merge two existing packets in one
|
||||
*
|
||||
* and we reduce case (2) to case (1) through sequentially applying fragment
|
||||
* or merge. For instance:
|
||||
*
|
||||
*\verbatim
|
||||
|------|
|
||||
| |
|
||||
|------|
|
||||
|
||||
^ ^ ^ ^
|
||||
| | | |
|
||||
start | | |
|
||||
| | end
|
||||
seq |
|
||||
seq + numBytes
|
||||
\endverbatim
|
||||
*
|
||||
* To reduce to case (1), we need to perform two fragment operations:
|
||||
*
|
||||
* - fragment (start, seq)
|
||||
* - fragment (seq + numBytes, end)
|
||||
*
|
||||
* After these operations, the requested block is exactly the resulting packet.
|
||||
* Merge operation is required when the requested block span over two (or more)
|
||||
* existing packets.
|
||||
*
|
||||
* While this could be extremely slow in the worst possible scenario (one big
|
||||
* packet which is split in small packets for transmission, and merged for
|
||||
* re-transmission) that scenario is unlikely during a TCP transmission (since
|
||||
* MSS can change, but it is stable, and retransmissions do not happen for
|
||||
* each segment).
|
||||
*
|
||||
* \param list List to extract block from
|
||||
* \param startingSeq Starting sequence of the list
|
||||
* \param numBytes Bytes to extract, starting from requestedSeq
|
||||
* \param requestedSeq Requested sequence
|
||||
* \return the item that contains the right packet
|
||||
*/
|
||||
TcpTxItem* GetPacketFromList (PacketList &list, const SequenceNumber32 &startingSeq,
|
||||
uint32_t numBytes, const SequenceNumber32 &requestedSeq) const;
|
||||
|
||||
/**
|
||||
* \brief Merge two TcpTxItem
|
||||
*
|
||||
* Merge t2 in t1. It consists in copying the lastSent field if t2 is more
|
||||
* recent than t1. Retransmitted field is copied only if it set in t2 but not
|
||||
* in t1. Sacked is copied only if it is true in both items.
|
||||
*
|
||||
* \param t1 first item
|
||||
* \param t2 second item
|
||||
*/
|
||||
void MergeItems (TcpTxItem &t1, TcpTxItem &t2) const;
|
||||
|
||||
/**
|
||||
* \brief Split one TcpTxItem
|
||||
*
|
||||
* Move "size" bytes from t2 into t1, copying all the fields.
|
||||
*
|
||||
* \param t1 first item
|
||||
* \param t2 second item
|
||||
* \param size Size to split
|
||||
*/
|
||||
void SplitItems (TcpTxItem &t1, TcpTxItem &t2, uint32_t size) const;
|
||||
|
||||
PacketList m_appList; //!< Buffer for application data
|
||||
PacketList m_sentList; //!< Buffer for sent (but not acked) data
|
||||
uint32_t m_maxBuffer; //!< Max number of data bytes in buffer (SND.WND)
|
||||
uint32_t m_size; //!< Size of all data in this buffer
|
||||
uint32_t m_sentSize; //!< Size of sent (and not discarded) segments
|
||||
|
||||
TracedValue<SequenceNumber32> m_firstByteSeq; //!< Sequence number of the first byte in data (SND.UNA)
|
||||
uint32_t m_size; //!< Number of data bytes
|
||||
uint32_t m_maxBuffer; //!< Max number of data bytes in buffer (SND.WND)
|
||||
std::list<Ptr<Packet> > m_data; //!< Corresponding data (may be null)
|
||||
|
||||
};
|
||||
|
||||
std::ostream & operator<< (std::ostream & os, TcpTxBuffer const & tcpTxBuf);
|
||||
|
||||
} // namepsace ns3
|
||||
|
||||
#endif /* TCP_TX_BUFFER_H */
|
||||
|
||||
Reference in New Issue
Block a user