ndnSIM学习(十)——apps之ndn-producer.cpp和ndn-consumer.cpp源码分析

前言

在前面的文章ndnSIM学习(四)——examples之ndn-simple.cpp超详细剖析中,我们逐行剖析了 ndn-simple.cpp 这个example的代码逻辑。其中消费者consumer和生产者producer是整个代码运行的重点。

在前面的文章ndnSIM学习(九)——从consumer发兴趣包到producer返回data包的全过程中,我们剖析了整个兴趣包的发包收包过程。今天,就让我们一起来看看消费者consumer和生产者producer都在做些什么吧

因为producer的代码相比consumer的代码更简单,仿照庖丁解牛,我们从难啃的硬骨头间的空隙(producer)出发,来分析apps的工作流程。

ndn-producer.hpp

~/ndnSIM/ns-3/src/ndnSIM/apps/ndn-producer.hpp 文件中定义了ndnSIM中生产者(producer)类的定义,代码如下(我手动加了一些注释,直接看注释应该就能看懂了吧,不详细讲了)——

class Producer : public App {
public:
  // 接口,返回关于本类对象的TypeId信息,方便外部配置
  static TypeId GetTypeId(void);

  // 构造函数,每当构造一个对象时,日志里输出相关信息
  // e.g. 输出[+0.000000000s 2 ndn.Producer:Producer()]
  Producer();  

  // 收到interest包后,装载一个data包(内部有编码为block的成员),最后把任务交接给到m_appLink->onReceiveData
  // 后续详见下面的流程图
  virtual void OnInterest(shared_ptr<const Interest> interest);

protected:
  // 调用App创建相关的App管理接口,如AppLinkService这种东西,并把管理链路层和传输层的接口放到Ndn Stack里
  virtual void StartApplication(); // Called at time specified by Start

  // m_active = false并关闭m_face
  virtual void StopApplication(); // Called at time specified by Stop

private:
  Name m_prefix;  // 前缀
  Name m_postfix;  // 后缀
  uint32_t m_virtualPayloadSize;  // 一个包装载数据byte数
  Time m_freshness;  // Fresh time

  uint32_t m_signature;  // SignatureValue
  Name m_keyLocator;
};

发包收包过程流程图

在这里插入图片描述

ndn-consumer.hpp

~/ndnSIM/ns-3/src/ndnSIM/apps/ndn-consumer.hpp 文件中定义了ndnSIM中消费者(consumer)类的定义,代码如下(我手动加了一些注释,直接看注释应该就能看懂了吧,不详细讲了)——

class Consumer : public App {
public:
  // 接口,返回关于本类对象的TypeId信息
  static TypeId GetTypeId();

  // 构造函数以及虚析构函数(便于派生)
  Consumer();
  virtual ~Consumer(){};

  // 收到消费者返回的Data包,执行相应的处理与回显,并更新RTT(Round-Trip Time)
  virtual void OnData(shared_ptr<const Data> contentObject);

  // 如果Not ACK,显示对应的信息与原因
  virtual void OnNack(shared_ptr<const lp::Nack> nack);

  // 超时就重传(RTT翻倍)
  virtual void OnTimeout(uint32_t sequenceNumber);

  // 发interest包,该函数由ScheduleNextPacket设置事件触发
  // 该函数给interest包填充信息后把任务交接给m_appLink->onReceiveInterest准备向自己的链路层发interest
  void SendPacket();

  // 在兴趣包实际发送之前的准备工作
  virtual void WillSendOutInterest(uint32_t sequenceNumber);

public:
  typedef void (*LastRetransmittedInterestDataDelayCallback)(Ptr<App> app, uint32_t seqno, Time delay, int32_t hopCount);  // 函数指针类型
  typedef void (*FirstInterestDataDelayCallback)(Ptr<App> app, uint32_t seqno, Time delay, uint32_t retxCount, int32_t hopCount);  // 函数指针类型

protected:
  // 调用App创建相关的App管理接口,如AppLinkService这种东西,并把管理链路层和传输层的接口放到Ndn Stack里
  // 创建完执行ScheduleNextPacket,调用派生类的发包控制函数
  virtual void StartApplication();

  // m_active = false并关闭m_face
  virtual void StopApplication();

  // 发包的调度策略,如Cbr就是以恒定速率发包(纯虚函数,由子类实现)
  virtual void ScheduleNextPacket() = 0;

  // 检查数据包是否需要超时重传
  void CheckRetxTimeout();

  // 设置检查重传超时的频率
  void SetRetxTimer(Time retxTimer);

  // 获取当前检查重传超时的频率
  Time GetRetxTimer() const;

protected:
  Ptr<UniformRandomVariable> m_rand; // 随机数nonce生成器

  uint32_t m_seq;      // 标志目前的请求序列数值seq
  uint32_t m_seqMax;   // 最多请求多少个seq
  EventId m_sendEvent; // 发包对应的事件Id,在子类的发包事件中会返回一个发包事件Id,当App结束时,将会取消掉该事件
  Time m_retxTimer;    // 重传时间
  EventId m_retxEvent; // m_retxEvent对应的事件Id

  Ptr<RttEstimator> m_rtt; // RTT估计器

  Time m_offTime;          ///< \brief Time interval between packets
  Name m_interestName;     // 就是ndn-simple.cpp设置的Prefix
  Time m_interestLifeTime; // 就是ndn-simple.cpp设置的LifeTime

  // 省略部分定义

  RetxSeqsContainer m_retxSeqs; // 重传序列,每当超时的时候,就将目标序列号加入该重传序列
  
  SeqTimeoutsContainer m_seqTimeouts; // 超时序列号+时间

  SeqTimeoutsContainer m_seqLastDelay;// 
  SeqTimeoutsContainer m_seqFullDelay;
  std::map<uint32_t, uint32_t> m_seqRetxCounts;

  TracedCallback<Ptr<App> /* app */, uint32_t /* seqno */, Time /* delay */, int32_t /*hop count*/>
    m_lastRetransmittedInterestDataDelay;
  TracedCallback<Ptr<App> /* app */, uint32_t /* seqno */, Time /* delay */,
                 uint32_t /*retx count*/, int32_t /*hop count*/> m_firstInterestDataDelay;
};

ndn-consumer-cbr.hpp

ndn-simple.cpp 里的消费者是 ConsumerCbr ,其中我推测 Cbr 的含义是 Constant BitRate 的缩写,所以 ConsumerCbr 其实就是以常数速率发包的消费者。这里我对这种类型的消费者进行一些讲解,还是老规矩,看代码注释,我不说废话

class ConsumerCbr : public Consumer {
public:
  static TypeId GetTypeId();

  ConsumerCbr();
  virtual ~ConsumerCbr();

protected:
  // 发包规则:以常数频率Frequence发interest
  virtual void ScheduleNextPacket();

  // 设置随机性规则,可以选择均匀分布、泊松分布
  void SetRandomize(const std::string& value);

  // 获取随机规则
  std::string GetRandomize() const;

protected:
  double m_frequency;  // 发包频率
  bool m_firstTime;  // 标志是不是第一次请求
  Ptr<RandomVariableStream> m_random;  // 随机数生成器
  std::string m_randomType;  // 随机的规则
};

其他消费者

因为基类 Consumer 的函数 ScheduleNextPacket 是纯虚函数,所以消费者一定得用其派生类,比如前面的 ConsumerCbr 就是一个派生类。这里列举一下ndnSIM的消费者(我没细看,可能有错误,仅供参考)

  • ConsumerBatches 用于批量管理消费者,下面管理了一个消费者的节点链表。一旦 ConsumerBatches::StartApplication 被执行,整个类就会为它管理的所有节点执行 ConsumerBatches::ScheduleNextPacket 。其中每过 m_rtt->RetransmitTimeout() 就会触发该消费者的 Consumer::SendPacket
  • ConsumerCbr 是以恒定速率发送兴趣包请求的消费者。
  • ConsumerZipfMandelbrot 继承了 ConsumerCbr ,发包服从Zipf-Mandelbrot律,我也不知道是什么玩意。
  • ConsumerWindow 似乎是以滑动窗口的方式执行拥塞控制的消费者,我没有仔细看里面的代码,所以并不确定。代码里提到本类是高度实验性的,要小心使用
  • ConsumerPcon 继承了 ConsumerWindow 类,参考了论文https://dl.acm.org/citation.cfm?id=2984369
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值