NS3 MacRxMiddle类说明

官方文档说明

该类处理片段的重复检测和重组。

也就是说该类的功能主要是检测节点接收到的分组是否重复,以及是否是分片,如果是分片的话,缓存分片。并且等待接收到最后一个分片的情况下,重新组合接收到的分片成为一个完整的packet,也就是重组。

源码解析

源码位置
/src/wifi/model/mac-rx-middle.cc

从源码的角度来看,整个MacRxMiddle类还是比较简单的。

方法数并不是很多。只有一个构造函数。

MacRxMiddle::MacRxMiddle ()
{
  NS_LOG_FUNCTION_NOARGS ();
}

ns3中使用到MacRxMiddle类对象的地方,也就是RegularWifiMac类中使用。是通过回调实现的。
RegularWifiMac类在其构造器中使用下面的代码创建MacRxMiddle类对象那个,并将RegularWifiMac::Receive方法设置为回调函数地址。

m_rxMiddle = new MacRxMiddle ();
m_rxMiddle->SetForwardCallback (MakeCallback (&RegularWifiMac::Receive, this));

而MacRxMiddle的SetForwardCallback方法源码为:

void
MacRxMiddle::SetForwardCallback (ForwardUpCallback callback)
{
  NS_LOG_FUNCTION_NOARGS ();
  m_callback = callback;
}

MacRxMiddle类比较重要的方法是Receive方法。

void
MacRxMiddle::Receive (Ptr<Packet> packet, const WifiMacHeader *hdr)
{
  NS_LOG_FUNCTION (packet << hdr);
  NS_ASSERT (hdr->IsData () || hdr->IsMgt ());
  OriginatorRxStatus *originator = Lookup (hdr);

  if (!(SequenceNumber16 (originator->GetLastSequenceControl ()) < SequenceNumber16 (hdr->GetSequenceControl ())))
    {
      NS_LOG_DEBUG ("Sequence numbers have looped back. last recorded=" << originator->GetLastSequenceControl () <<
                    " currently seen=" << hdr->GetSequenceControl ());
    }
  //filter duplicates.
  if (IsDuplicate (hdr, originator))
    {
      NS_LOG_DEBUG ("duplicate from=" << hdr->GetAddr2 () <<
                    ", seq=" << hdr->GetSequenceNumber () <<
                    ", frag=" << hdr->GetFragmentNumber ());
      return;
    }
  Ptr<Packet> agregate = HandleFragments (packet, hdr, originator);
  if (agregate == 0)
    {
      return;
    }
  NS_LOG_DEBUG ("forwarding data from=" << hdr->GetAddr2 () <<
                ", seq=" << hdr->GetSequenceNumber () <<
                ", frag=" << hdr->GetFragmentNumber ());
  if (!hdr->GetAddr1 ().IsGroup ())
    {
      originator->SetSequenceControl (hdr->GetSequenceControl ());
    }
  m_callback (agregate, hdr);
}

MacRxMiddle::Receive方法通过Mac头找到对应的OriginatorRxStatus 对象,比较上一次接收的序列控制号与本次接收到的packet的序列控制号是否是有序的。

然后通过IsDuplicate 方法判断接收的packet是否是重复的,如果重复,直接返回。

然后通过HandleFragments 方法判断是否需要重组packet,以及完成packet的重组。

如果返回的agregate 对象为空,直接返回。

如果返回的agregate 对象不空,则通过上面的MacRxMiddle::SetForwardCallback方法设置的回调函数地址调用相对应的回调函数。

综上来看,上面的MacRxMiddle::Receive设计到三个重要的方法:Lookup IsDuplicate HandleFragments 。一个个来看。

首先看Lookup 方法:

OriginatorRxStatus *
MacRxMiddle::Lookup (const WifiMacHeader *hdr)
{
  NS_LOG_FUNCTION (hdr);
  OriginatorRxStatus *originator;
  Mac48Address source = hdr->GetAddr2 ();
  if (hdr->IsQosData ()
      && !hdr->GetAddr2 ().IsGroup ())
    {
      /* only for qos data non-broadcast frames */
      originator = m_qosOriginatorStatus[std::make_pair (source, hdr->GetQosTid ())];
      if (originator == 0)
        {
          originator = new OriginatorRxStatus ();
          m_qosOriginatorStatus[std::make_pair (source, hdr->GetQosTid ())] = originator;
        }
    }
  else
    {
      originator = m_originatorStatus[source];
      if (originator == 0)
        {
          originator = new OriginatorRxStatus ();
          m_originatorStatus[source] = originator;
        }
    }
  return originator;
}

MacRxMiddle::Lookup 方法完成的功能就是通过Mac头找到对应的OriginatorRxStatus 对象,如果没有,则创建一个对应的OriginatorRxStatus 对象。

OriginatorRxStatus 类功能也比较简单:

class OriginatorRxStatus
{
private:
  //list列表,保存接收的每个分片packet,当接收到最后一个分片的时候,组合为完成的一个packet。
  typedef std::list<Ptr<const Packet> > Fragments;
  //迭代器
  typedef std::list<Ptr<const Packet> >::const_iterator FragmentsCI;
  //标记位,标记接收的packet是否为分片
  bool m_defragmenting;
  //记录上一次接收的packet的序列控制号
  uint16_t m_lastSequenceControl;
  //list对象
  Fragments m_fragments;


public:
  //默认构造器,初始序列控制号全是1,并标记不是分片。
  OriginatorRxStatus ()
  {
    m_lastSequenceControl = 0xffff;
    m_defragmenting = false;
  }
  ~OriginatorRxStatus ()
  {
    m_fragments.clear ();
  }
  //返回是否是分片标记位
  bool IsDeFragmenting (void)
  {
    return m_defragmenting;
  }
  //当接收到第一个分片的时候,设置标记位,并保存第一个分片packet。
  void AccumulateFirstFragment (Ptr<const Packet> packet)
  {
    NS_ASSERT (!m_defragmenting);
    m_defragmenting = true;
    m_fragments.push_back (packet);
  }
 
  //当接收到最后一个分片的时候,重置标记位,并通过m_fragments对象保存的每一个分片,重新生成一个完成的packet对象,并返回。
  Ptr<Packet> AccumulateLastFragment (Ptr<const Packet> packet)
  {
    NS_ASSERT (m_defragmenting);
    m_fragments.push_back (packet);
    m_defragmenting = false;
    Ptr<Packet> full = Create<Packet> ();
    for (FragmentsCI i = m_fragments.begin (); i != m_fragments.end (); i++)
      {
        full->AddAtEnd (*i);
      }
    m_fragments.erase (m_fragments.begin (), m_fragments.end ());
    return full;
  }
  
  //当接收的分片既不是第一个,又不是最后一个分片的时候,保存在list列表中.
  void AccumulateFragment (Ptr<const Packet> packet)
  {
    NS_ASSERT (m_defragmenting);
    m_fragments.push_back (packet);
  }
 
  //该方法判断接收的分片是否是上一个分片的序列控制号m_lastSequenceControl 的下一个序列控制号
  bool IsNextFragment (uint16_t sequenceControl)
  {
    if ((sequenceControl >> 4) == (m_lastSequenceControl >> 4)
        && (sequenceControl & 0x0f) == ((m_lastSequenceControl & 0x0f) + 1))
      {
        return true;
      }
    else
      {
        return false;
      }
  }

  uint16_t GetLastSequenceControl (void)
  {
    return m_lastSequenceControl;
  }

  void SetSequenceControl (uint16_t sequenceControl)
  {
    m_lastSequenceControl = sequenceControl;
  }
};

MacRxMiddle::Lookup 方法完成的功能就是通过Mac头找到对应的OriginatorRxStatus 对象,有了OriginatorRxStatus 对象就可以完成分片的一些判断了。

MacRxMiddle::IsDuplicate 方法就通过Mac头和上面的MacRxMiddle::Lookup 方法返回的OriginatorRxStatus 对象来判断接收的分片是否是重复的:

bool
MacRxMiddle::IsDuplicate (const WifiMacHeader* hdr,
                          OriginatorRxStatus *originator) const
{
  NS_LOG_FUNCTION (hdr << originator);
  if (hdr->IsRetry ()
      && originator->GetLastSequenceControl () == hdr->GetSequenceControl ())
    {
      return true;
    }
  return false;
}

如果上一次接收的序列控制号与本次接收的序列控制号相等,那就是重复的。

MacRxMiddle::HandleFragments方法完成对接收到的分片进行处理,也同样通过利用MacRxMiddle::Lookup 方法返回的OriginatorRxStatus 对象。

Ptr<Packet>
MacRxMiddle::HandleFragments (Ptr<Packet> packet, const WifiMacHeader *hdr,
                              OriginatorRxStatus *originator)
{
  NS_LOG_FUNCTION (packet << hdr << originator);
  if (originator->IsDeFragmenting ())
    {
      if (hdr->IsMoreFragments ())
        {
          if (originator->IsNextFragment (hdr->GetSequenceControl ()))
            {
              NS_LOG_DEBUG ("accumulate fragment seq=" << hdr->GetSequenceNumber () <<
                            ", frag=" << hdr->GetFragmentNumber () <<
                            ", size=" << packet->GetSize ());
              originator->AccumulateFragment (packet);
              originator->SetSequenceControl (hdr->GetSequenceControl ());
            }
          else
            {
              NS_LOG_DEBUG ("non-ordered fragment");
            }
          return 0;
        }
      else
        {
          if (originator->IsNextFragment (hdr->GetSequenceControl ()))
            {
              NS_LOG_DEBUG ("accumulate last fragment seq=" << hdr->GetSequenceNumber () <<
                            ", frag=" << hdr->GetFragmentNumber () <<
                            ", size=" << hdr->GetSize ());
              Ptr<Packet> p = originator->AccumulateLastFragment (packet);
              originator->SetSequenceControl (hdr->GetSequenceControl ());
              return p;
            }
          else
            {
              NS_LOG_DEBUG ("non-ordered fragment");
              return 0;
            }
        }
    }
  else
    {
      if (hdr->IsMoreFragments ())
        {
          NS_LOG_DEBUG ("accumulate first fragment seq=" << hdr->GetSequenceNumber () <<
                        ", frag=" << hdr->GetFragmentNumber () <<
                        ", size=" << packet->GetSize ());
          originator->AccumulateFirstFragment (packet);
          originator->SetSequenceControl (hdr->GetSequenceControl ());
          return 0;
        }
      else
        {
          return packet;
        }
    }
}

1、如果接收的Mac头对应的packet是分片,即originator->IsDeFragmenting ()返回true,并且Mac头还有分片,即hdr->IsMoreFragments ()返回true,且当前接收的packet就是上一次接收的分片的下一个,序列控制号没有出错,则通过originator对象完成分片的缓存,并重置序列控制号,为下一次接收的分片做准备。

2、如果接收的Mac头对应的packet是分片,即originator->IsDeFragmenting ()返回true,并且Mac头还有分片,即hdr->IsMoreFragments ()返回true,但是当前接收的packet不是上一次接收的分片的下一个,则NS_LOG_DEBUG ("non-ordered fragment");

3、如果接收的Mac头对应的packet是分片,即originator->IsDeFragmenting ()返回true,并且Mac头没有分片,即hdr->IsMoreFragments ()返回false,且当前接收的packet就是上一次接收的分片的下一个,说明接收到了最后一个分片,则通过originator对象的AccumulateLastFragment 方法完成分片的重组,返回一个完成的packet:

Ptr<Packet> p = originator->AccumulateLastFragment (packet);
originator->SetSequenceControl (hdr->GetSequenceControl ());
return p;

4、如果接收的Mac头对应的packet不是分片,即originator->IsDeFragmenting ()返回false,并且Mac头还有分片,即hdr->IsMoreFragments ()返回true,说明接收到的packet是第一个分片,则通过originator的AccumulateFirstFragment 方法处理第一个分片,并设置标记位和序列控制号,为下一个分片做准备。

5、如果接收的Mac头对应的packet不是分片,即originator->IsDeFragmenting ()返回false,并且Mac头没有分片,即hdr->IsMoreFragments ()返回false,即接收的packet是一个独立的数据帧,不是分片,则直接返回该packet对象。

至此,MacRxMiddle类的方法基本说明完毕。


另外,需要说明MacRxMiddle类的Receive 的调用,同样是在RegularWifiMac::RegularWifiMac ()构造器函数中设置的。代码如下:

RegularWifiMac::RegularWifiMac () :
  m_htSupported (0),
  m_vhtSupported (0),
  m_erpSupported (0),
  m_dsssSupported (0)
{
  NS_LOG_FUNCTION (this);
  //创建MacRxMiddle 对象。
  m_rxMiddle = new MacRxMiddle ();
  //设置MacRxMiddle 对象中回调地址
  m_rxMiddle->SetForwardCallback (MakeCallback (&RegularWifiMac::Receive, this));

  m_txMiddle = new MacTxMiddle ();

  m_low = CreateObject<MacLow> ();
  //设置MacRxMiddle 对象的Receive方法的回调
  m_low->SetRxCallback (MakeCallback (&MacRxMiddle::Receive, m_rxMiddle));

 。。。。
}

也就是说MacLow对象中有一个回调地址,设置为MacRxMiddle::Receive,该回调地址调用MacRxMiddle::Receive方法,而MacRxMiddle::Receive方法中,又回调RegularWifiMac::Receive方法,这样一层层传递的。

推荐阅读更多精彩内容

  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 8,392评论 6 13
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 29,694评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 128,902评论 18 137
  • 简介 用简单的话来定义tcpdump,就是:dump the traffic on a network,根据使用者...
    保川阅读 5,278评论 1 13
  • FreakZ学习笔记:路由发现机制 路由发现机制只有在发送通信包的过程中会被调用,而接收过程因为发送时候已经进行了...
    zplodge阅读 448评论 0 0
  • 提起扬州,最先想到的是“烟花三月下扬州”吧! 扬州这座千年古城,经历过历史的沉浮,终于砺成一粒圆润的珍珠,静默,淡...
    韫水阅读 472评论 18 12
  • 思姚家阅读 452评论 0 4
  • 世事沧桑,前途苍茫,曾经一片美好的希冀如今已成梦幻,一点一点消失在过往之中,踽踽独行中,只留下款款离去的背影,匆匆...
    诗词品读之家阅读 572评论 0 2