ndnSIM中LCD的简单实现

一、前言

这里主要列出一些资源。
1. 查找API:

a.  http://ndnsim.net/2.1/doxygen/annotated.html 首先在ndn中搜索,可以查看相关的描述和源码
b.  https://www.nsnam.org/doxygen/index.html 如果在ndn中没有搜索到,移步至NS3中进行搜索

2. 如果有问题解决不了,可以发英文邮件至ndnsim@lists.cs.ucla.edu,会得到及时的解答。也可以查看其邮件记录http://www.lists.cs.ucla.edu/pipermail/ndnsim/,寻找类似的问题。

二、LCD原理简介

LCD即leave copy down,兴趣包每次被响应,都会被缓存在返回路径的下一跳,如果同一个内容被请求多次,那么它就会被缓存得离请求节点越来越近。

图1

如图所示,C代表consumer,P代表producer,C第一次请求内容A,P响应请求,会将A缓存在节点3,第二次请求,节点3命中,直接返回内容,然后将内容缓存在节点2,以此类推。

三、实现思路

通过在返回的数据包中添加一个内容存储变量contentCopyTag,用来判断节点是否存储内容来实现LCD。

  1. 内容存储变量contentCopyTag=1时转发节点并缓存内容,contentCopyTag=0时转发节点不缓存。数据包离开节点时,将contentCopyTag减1。

  2. contentCopyTag的初值为2,若将contentCopyTag的初值为1,producer发送数据后,内容存储变量contentCopyTag变为0,当数据包到达第一个节点时因为contentCopyTag=0则不会存储数据,这不符合LCD,故将contentCopyTag的初值为2。

  3. 假设转发节点上都为空,故兴趣包在到达producer之前无任何转发节点响应该兴趣包。当兴趣包到达producer时,producer响应兴趣包。为实现LCD,需要在返回的数据包中添加一个内容存储变量contentCopyTag

  4. producer发送该数据包,此时contentCopyTag由2变为1。

  5. 当数据包到达第一个转发节点(即3号节点)时,节点判断contentCopyTag的值决定是否存储数据。contentCopyTag>0(即=1)时存储数据。

  6. 3号转发节点存储数据并发送该数据包,此时contentCopyTag由1变为0。

  7. 之后该数据包经过的所有转发节点(不包括consumer节点)时,由于contentCopyTag=0故不会将数据存储到节点内。

  8. consumer再次发送该请求时,由于3号节点中存有该兴趣对应的数据,故3号节点响应该兴趣包,3号节点返回的数据包中添加一个内容存储变量contentCopyTag。以此类推最终所有转发节点都存有该数据。

四、具体实现

(一)contentCopyTag的实现

在ndnSIM中有两种Tag,一个是ns3::ndn::Tag,这个Tag可以加入data中(data继承自TagHost),但是它只能在同一个节点内的不同components之间传递,一旦离开这个节点,那么这个Tag将不复存在。

还有一个Tag是ns3::Tag,这个Tag可以加入packet中,它可以实现节点间的数据传递(至少在ndnSIM2.2之前)。

因此我们在这里实现一个继承自ns3::Tag的Tag,来把contentCopyTag交给下一跳。

实际上ndnSIM本身有一个类似的Tag,即FwHopCountTag。我们就仿照它来实现我们的contentCopyTag。

ns-3/src/ndnSIM/utils/中,编写两个文件ndn-fw-content-copy-tag.hppndn-fw-content-copy-tag.cpp代码如下:

//ndn-fw-content-copy-tag.hpp

#ifndef NDN_FW_CONTENT_COPY_TAG_H_
#define NDN_FW_CONTENT_COPY_TAG_H_

#include "ns3/tag.h"

namespace ns3 {
namespace ndn {

/**
 * @brief Packet tag that is used to track hop count for Interest-Data pairs
 */
class FwcontentCopyTag : public Tag
{
public:
  static TypeId
  GetTypeId (void);

  /**
   * @brief Default constructor
   * 数据包发送时将c_copyTag置为2,使第一个接收到数据包的节点可以缓存该数据包
   */
  FwcontentCopyTag () : c_copyTag (2) { };

  /**
   * @brief Destructor
   */
  ~FwcontentCopyTag () { }

  /**
   * @brief Decrement tag
   * 第一个接收到数据包的节点缓存数据后,调用Decrement(),目的是到达cunsumer之前的节点(除第一个节点)不缓存该数据
   */
  void
  Decrement () { c_copyTag --; }


  /**
   * @brief Get value of hop count
   */
  int
  Get () const { return c_copyTag; }

  ////////////////////////////////////////////////////////
  // from ObjectBase
  ////////////////////////////////////////////////////////
  virtual TypeId
  GetInstanceTypeId () const;

  ////////////////////////////////////////////////////////
  // from Tag
  ////////////////////////////////////////////////////////
/**/
  virtual uint32_t
  GetSerializedSize () const;

  virtual void
  Serialize (TagBuffer i) const;

  virtual void
  Deserialize (TagBuffer i);

  virtual void
  Print (std::ostream &os) const;

private:
  //节点用来判断是否缓存内容的变量,true节点将数据缓存,false则不缓存数据
  int c_copyTag;
};

} // namespace ndn
} // namespace ns3



#endif /* NDN_FW_CONTENT_COPY_TAG_H_ */
//ndn-fw-content-copy-tag.cpp

#include "ndn-fw-content-copy-tag.hpp"

namespace ns3 {
namespace ndn {

TypeId
FwcontentCopyTag::GetTypeId ()
{
  static TypeId tid = TypeId("ns3::ndn::FwcountCopyTag")
    .SetParent<Tag>()
    .AddConstructor<FwcontentCopyTag>()
    ;
  return tid;
}

TypeId
FwcontentCopyTag::GetInstanceTypeId () const
{
  return FwcontentCopyTag::GetTypeId ();
}
/**/
uint32_t
FwcontentCopyTag::GetSerializedSize () const
{
  return sizeof(uint32_t);
}
/**/
void
FwcontentCopyTag::Serialize (TagBuffer i) const
{
  i.WriteU32 (c_copyTag);
}

void
FwcontentCopyTag::Deserialize (TagBuffer i)
{
  c_copyTag = i.ReadU32 ();
}

void
FwcontentCopyTag::Print (std::ostream &os) const
{
  os << c_copyTag;
}

} // namespace ndn
} // namespace ns3
(二)将Tag加入数据包中

以下双横线内的内容可跳过



实现我们的Tag之后就需要将其实例化并加入数据包中。在路径ns-3/src/ndnSIM/NFD/daemon/fw/中,有一个forwarder.cpp,我的理解是,这是一个中转站,所有的兴趣包、数据包都会经过这个中转站。我们需要在数据包发出去的时候将Tag加入包中。于是我们在forwarder.cpp中找到onOutgoingData函数,在对data进行了一些处理之后调用了

outFace.sendData(data);

而outFace的类型是Face类型,说明还要在Face里面做文章。
在face.hpp中找到sendData函数(在路径ns-3/src/ndnSIM/NFD/daemon/face中)

inline void
Face::sendData(const Data& data)
{
  m_service->sendData(data);
}

在private列表里,找到m_service

unique_ptr<LinkService> m_service;

是一个指向LinkService的指针,LinkService在同目录下,

void
LinkService::sendData(const Data& data)
{
  BOOST_ASSERT(m_transport != nullptr);
  NFD_LOG_FACE_TRACE(__func__);

  ++this->nOutData;

  doSendData(data);

  afterSendData(data);
}
/** \brief performs LinkService specific operations to send a Data
   */
  virtual void
  doSendData(const Data& data) = 0;

doSendData是一个虚函数,需要用户实现。
这里ndnSIM已经有一个实现了,在路径ns3/src/ndnSIM/model/下的ndn-net-device-link-service



ns3/src/ndnSIM/model/下的ndn-net-device-link-service.cpp中重载了函数doSendData

void
NetDeviceLinkService::doSendData(const Data& data)
{
  NS_LOG_FUNCTION(this << &data);

  Ptr<Packet> packet = Convert::ToPacket(data);
  send(packet);
}

可以看到它将data转换为packet来进行处理。

void
NetDeviceLinkService::send(Ptr<Packet> packet)
{
  NS_ASSERT_MSG(packet->GetSize() <= m_netDevice->GetMtu(),
                "Packet size " << packet->GetSize() << " exceeds device MTU "
                               << m_netDevice->GetMtu());

  FwHopCountTag tag;
  packet->RemovePacketTag(tag);
  tag.Increment();
  packet->AddPacketTag(tag);

  m_netDevice->Send(packet, m_netDevice->GetBroadcast(), L3Protocol::ETHERNET_FRAME_TYPE);
}

send函数中,可以看到它将一个FwHopCountTag加入了packet中。我们的contentCopyTag也在这里加入。

void
NetDeviceLinkService::send(Ptr<Packet> packet)
{
  NS_ASSERT_MSG(packet->GetSize() <= m_netDevice->GetMtu(),
                "Packet size " << packet->GetSize() << " exceeds device MTU "
                               << m_netDevice->GetMtu());

 /////////////////////////////////////////// 
  FwcontentCopyTag contentCopyTag;
  packet->RemovePacketTag(contentCopyTag);
  if(contentCopyTag.Get() > 0){
    contentCopyTag.Decrement();
  }
  packet->AddPacketTag(contentCopyTag);
 /////////////////////////////////////////// 
  FwHopCountTag tag;
  packet->RemovePacketTag(tag);
  tag.Increment();
  packet->AddPacketTag(tag);

  m_netDevice->Send(packet, m_netDevice->GetBroadcast(), L3Protocol::ETHERNET_FRAME_TYPE);
}

首先声明一个FwcontentCopyTag,这句话会调用其默认构造函数,将tag值初始化为2,下一句话:

packet->RemovePacketTag(contentCopyTag);

会将packet中的contentCopyTag移除,如果找到该tag,则返回true,并移除它且将其值赋给contentCopyTag,如果没有则返回false不执行任何操作。
然后将tag值decrement一下,在add回去即可,这样就实现了tag的操作。无论是在producer还是在其他结点都会得到正确的结果。

(三)判断Tag并决定是否缓存

继续回到forwarder.cpp,找到onIncomingData函数,其中有:

// CS insert
  if (m_csFromNdnSim == nullptr)
    m_cs.insert(*dataCopyWithoutPacket);
  else
    m_csFromNdnSim->Add(dataCopyWithoutPacket);

这段代码是将数据包插入CS中。我们在这里对tag进行判断,并决定是否缓存。
将其修改如下即可:

// CS insert

  shared_ptr<Data> shared_data = make_shared<Data>(data);  
  //通过  ns3::ndn::Ns3PacketTag的getPacket函数拿到packet的指针
  auto ns3PacketTag = shared_data->getTag<ns3::ndn::Ns3PacketTag>();
   int contentCopy = 0;
   ns3::ndn::FwcontentCopyTag contentCopyTag;
   if(ns3PacketTag!=nullptr){
     //判断contentCopyTag是否存在,也可以直接用RemovePacketTag函数
     if(ns3PacketTag->getPacket()->PeekPacketTag(contentCopyTag)){
        contentCopy = contentCopyTag.Get();
        //如果大于零则缓存,否则不做任何事
        if( contentCopy > 0){
            if (m_csFromNdnSim == nullptr)
               m_cs.insert(*dataCopyWithoutPacket);
            else
               m_csFromNdnSim->Add(dataCopyWithoutPacket);
        }
     }  
    }  
    //ns3PacketTag只在节点间传递,只有数据包离开节点时才会加入到数据包中,因此有可能为null
    else{
      std::cout<<"forwarder: onIncomingData: ns3PacketTag = null"<<std::endl;
    }
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值